feat: 🔥 优化结构

This commit is contained in:
zouzhibing 2022-11-20 19:53:13 +08:00
parent a981538a69
commit 4699093e6d
23 changed files with 153 additions and 234 deletions

View File

@ -30,6 +30,7 @@ vue-element-perfect 是一个后台前端解决方案,它使用了最新的前
## 分支管理
- master 技术采用 vite + vue3.0 + Typescript + pinia
- vue-admin-simple 简易版本
- vite-vuex vite + vue3.0 + Typescript + vuex
- vue-i18n 语言切换版本
- webpack 技术采用 webpack + vue3.0 + Typescript + vuex

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

View File

@ -1,5 +1,5 @@
<template>
<el-dropdown @command="commandAction">
<el-dropdown >
<span class="el-dropdown-link">
<el-avatar :size="30" class="avatar" :src="AvatarLogo"/>
{{userInfo.username}}
@ -9,10 +9,18 @@
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="2" >
<el-icon><Edit /></el-icon></el-dropdown-item>
<el-dropdown-item :command="1" divided>
<el-icon><SwitchButton /></el-icon>退</el-dropdown-item>
<el-dropdown-item :command="0" @click="switchRolesAction('admin')">
{{currentRoles==='admin'?'当前角色':'切换角色'}}管理员
</el-dropdown-item>
<el-dropdown-item :command="0" divided @click="switchRolesAction('other')">
{{currentRoles==='other'?'当前角色':'切换角色'}}普通用户
</el-dropdown-item>
<el-dropdown-item :command="3" divided @click="modifyPassword">
<el-icon><Edit /></el-icon>
</el-dropdown-item>
<el-dropdown-item :command="4" divided @click="logOut" >
<el-icon><SwitchButton /></el-icon>退
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -28,11 +36,33 @@ import {computed, ref} from "vue";
import AvatarLogo from '@/assets/image/avatar.png'
import {useUserStore} from "@/store/modules/user"
import {useTagsViewStore} from "@/store/modules/tagsView"
import {usePermissionStore} from "@/store/modules/permission"
import PersonalDialog from './PersonalDialog.vue'
const router = useRouter()
const UserStore = useUserStore()
const TagsViewStore = useTagsViewStore()
const PermissionStore = usePermissionStore()
const currentRoles = computed({
get() {
return UserStore.roles[0]
},
set(val) {
;(async () => {
await UserStore.getInfo([val])
router.push({
path:'/'
})
location.reload()
})()
},
})
const switchRolesAction = (type:string)=>{
if(type===currentRoles.value) return
currentRoles.value = currentRoles.value==='admin'?'other':'admin'
}
//
const userInfo = computed(() => UserStore.userInfo)
@ -48,6 +78,7 @@ const logOut = async () => {
await UserStore.logout()
await router.push({path: '/login'})
TagsViewStore.clearVisitedView()
PermissionStore.clearRoutes()
ElMessage({
type: "success",
message: "退出登录成功!"
@ -55,15 +86,8 @@ const logOut = async () => {
})
.catch(() => {})
}
const commandAction = (key: number) => {
switch (key) {
case 1:
logOut()
break
case 2:
const modifyPassword = ()=>{
person.value.show()
break
}
}
</script>

View File

@ -10,13 +10,13 @@
</router-view>
</div>
<Theme />
<Footer/>
</div>
</template>
<script lang="ts" setup>
import Theme from '@/components/Theme/index.vue'
import Footer from '../Footer/index.vue'
import { computed, ref } from 'vue'
import {useSettingStore} from "@/store/modules/setting"
import {usePermissionStore} from "@/store/modules/permission"
@ -33,14 +33,13 @@
flex: 1;
display: flex;
overflow-x: hidden;
flex-direction: column;
width: 100%;
box-sizing: border-box;
.app-main-inner{
flex: 1;
display: flex;
overflow-x: hidden;
flex-direction: column;
width: 100%;
box-sizing: border-box;
}

View File

@ -1,7 +1,7 @@
<template>
<div class="g-container-layout" :class="classObj">
<div v-if="device === 'mobile' && !isCollapse" class="drawer-bg" @click="handleClickOutside" />
<sidebar class="sidebar-container" v-if="mode === 'vertical'" />
<Sidebar class="sidebar-container" v-if="mode === 'vertical'" />
<div
class="main-container"
:class="{
@ -9,10 +9,9 @@
}"
>
<div :style="{ height:`${showTag?90:50}px` }" v-if="SettingStore.themeConfig.fixedHeader"></div>
<u-header />
<div class="m-container-content" :class="{ 'app-main-hide-tag': !showTag }">
<Main />
</div>
<UHeader/>
<Main/>
<Footer/>
</div>
</div>
</template>
@ -23,7 +22,7 @@
import Sidebar from './Sidebar/index.vue'
import UHeader from './Header/index.vue'
import Main from './Main/index.vue'
import Footer from './Footer/index.vue'
import { useResizeHandler } from './hooks/useResizeHandler'
const SettingStore = useSettingStore()
@ -53,11 +52,9 @@
<style lang="scss" scoped>
.g-container-layout {
//display: flex;
height: 100%;
width: 100%;
.main-container {
//overflow: auto;
display: flex;
flex: 1;
box-sizing: border-box;
@ -81,10 +78,4 @@
position: absolute;
z-index: 90;
}
.m-container-content {
display: flex;
flex: 1;
position: relative;
box-sizing: border-box;
}
</style>

View File

@ -2,7 +2,6 @@ import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import pinia from "./store";
// import 'default-passive-events'
// 权限路由
import './permission'

View File

@ -1,4 +1,4 @@
import router from './router/index'
import router from '@/router/index'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import {useUserStore} from "@/store/modules/user"
@ -33,6 +33,7 @@ router.beforeEach(async(to, from, next) => {
const accessRoutes = await PermissionStore.generateRoutes(UserStore.roles)
hasRoles = false
accessRoutes.forEach(item => router.addRoute(item)) // 动态添加访问路由表
console.log('accessRoutes',accessRoutes)
next({ ...to, replace: true }) // // 这里相当于push到一个页面 不在进入路由拦截
}else {
next() // // 如果不传参数就会重新执行路由拦截,重新进到这里

View File

@ -7,7 +7,6 @@ interface extendRoute {
//
import tableRouter from './modules/table'
import dataScreenRouter from './modules/dataScreen'
import errorRouter from './modules/error'
import excelRouter from './modules/excel'
import nestedRouter from './modules/nested'
import systemRouter from './modules/system'
@ -18,6 +17,7 @@ import externalLink from './modules/externalLink'
import formRouter from './modules/from'
import functionPageRouter from './modules/functionPage'
// 异步组件
export const asyncRoutes = [
...dataScreenRouter,
@ -29,13 +29,8 @@ export const asyncRoutes = [
...chatRouter,
...nestedRouter,
...excelRouter,
...errorRouter,
...externalLink,
...systemRouter,
{
path: '/:pathMatch(.*)',
redirect: '/404'
}
]
/**
@ -54,6 +49,18 @@ export const asyncRoutes = [
*/
export const constantRoutes: Array<RouteRecordRaw&extendRoute> = [
{
path: "/404",
name: "404",
component: () => import("@/views/errorPages/404.vue"),
hidden:true,
},
{
path: "/403",
name: "403",
component: () => import("@/views/errorPages/403.vue"),
hidden:true,
},
{
path: '/login',
name: 'Login',
@ -77,6 +84,16 @@ export const constantRoutes: Array<RouteRecordRaw&extendRoute> = [
},
]
/**
* notFoundRouter()
*/
export const notFoundRouter = {
path: '/:pathMatch(.*)',
name: "notFound",
redirect: '/404'
};
const router = createRouter({
// history: createWebHistory(process.env.BASE_URL), // history
history: createWebHashHistory(), // hash

View File

@ -8,7 +8,7 @@ const echartsRouter = [{
redirect: '/echarts/migration',
name: 'echarts',
meta: {
title: '可视化图表',
title: 'Echarts',
icon: 'trend-charts',
roles:['other']
},

View File

@ -1,33 +0,0 @@
import { RouteRecordRaw } from "vue-router";
import Layout from "@/layout/index.vue";
// 扩展继承属性
interface extendRoute {
hidden?:boolean
}
const errorRouter : Array<RouteRecordRaw&extendRoute> = [
{
path: "/403",
name: "403",
component: () => import("@/views/error/403.vue"),
hidden:true,
meta: {
requiresAuth: true,
title: "403页面",
key: "403"
}
},
{
path: "/404",
name: "404",
component: () => import("@/views/error/404.vue"),
hidden:true,
meta: {
requiresAuth: true,
title: "404页面",
key: "404"
}
}
]
export default errorRouter

View File

@ -28,6 +28,12 @@ const externalLink = [{
name: 'github',
meta: { title: 'Gitee 地址', icon: 'MenuIcon' }
},
{
path: 'http://182.61.5.190:8890/#/login',
name: 'simple',
meta: { title: '简易版本', icon: 'MenuIcon' },
component: () => import('@/views/externalLinks/simple/index.vue'),
},
{
path: 'iframe',
component: () => import('@/views/externalLinks/iframe/index.vue'),

View File

@ -21,13 +21,13 @@ const functionPageRouter = [{
},
{
path: '404',
component: () => import('@/views/error/404.vue'),
component: () => import('@/views/errorPages/404.vue'),
name: 'function-404',
meta: { title: '404', keepAlive: true , icon: 'MenuIcon'}
},
{
path: '403',
component: () => import('@/views/error/403.vue'),
component: () => import('@/views/errorPages/403.vue'),
name: 'function-403',
meta: { title: '403', keepAlive: true , icon: 'MenuIcon'}
},

View File

@ -10,15 +10,8 @@ const systemRouter = [{
meta: {
title: '系统管理',
icon: 'Setting',
roles:['other']
},
children: [
{
path: 'page',
component: () => import('@/views/system/page/index.vue'),
name: 'page',
meta: { title: '页面权限', icon: 'MenuIcon', roles:['other'] }
},
{
path: 'user',
component: () => import('@/views/system/user/index.vue'),

View File

@ -1,5 +1,5 @@
import {defineStore} from 'pinia'
import { asyncRoutes, constantRoutes,routerArray } from '@/router/index'
import { asyncRoutes, constantRoutes,routerArray,notFoundRouter } from '@/router/index'
import {hasPermission,filterAsyncRoutes} from "@/utils/routers"
import {filterKeepAlive} from "../../utils/routers";
export const usePermissionStore = defineStore({
@ -31,6 +31,7 @@ export const usePermissionStore = defineStore({
} else {
accessedRoutes = asyncRoutes || []
}
accessedRoutes = accessedRoutes.concat(notFoundRouter)
this.routes = constantRoutes.concat(accessedRoutes)
this.addRoutes = accessedRoutes
resolve(accessedRoutes)

View File

@ -11,7 +11,7 @@ export const useUserStore = defineStore({
// 登录用户信息
userInfo:{},
// 角色
roles:[]
roles:localStorage.roles?JSON.parse(localStorage.roles):[]
}),
getters: {},
@ -32,6 +32,7 @@ export const useUserStore = defineStore({
return new Promise((resolve, reject) =>{
// 获取权限列表 默认就是超级管理员,因为没有进行接口请求 写死
this.roles = ['admin']
localStorage.roles = JSON.stringify(this.roles)
resolve(this.roles)
} )
},

View File

@ -1,64 +0,0 @@
<template>
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px">
<el-radio-button :label="false">expand</el-radio-button>
<el-radio-button :label="true">collapse</el-radio-button>
</el-radio-group>
<el-menu
default-active="2"
class="el-menu-vertical-demo"
:collapse="isCollapse"
@open="handleOpen"
@close="handleClose"
>
<el-sub-menu index="1">
<template #title>
<el-icon><location /></el-icon>
<span>Navigator One</span>
</template>
<el-menu-item-group>
<template #title><span>Group One</span></template>
<el-menu-item index="1-1">item one</el-menu-item>
<el-menu-item index="1-2">item two</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="Group Two">
<el-menu-item index="1-3">item three</el-menu-item>
</el-menu-item-group>
<el-sub-menu index="1-4">
<template #title><span>item four</span></template>
<el-menu-item index="1-4-1">item one</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<el-menu-item index="2">
<el-icon><icon-menu /></el-icon>
<template #title>Navigator Two</template>
</el-menu-item>
<el-menu-item index="3" disabled>
<el-icon><document /></el-icon>
<template #title>Navigator Three</template>
</el-menu-item>
<el-menu-item index="4">
<el-icon><setting /></el-icon>
<template #title>Navigator Four</template>
</el-menu-item>
</el-menu>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { Location, Document, Menu as IconMenu, Setting } from '@element-plus/icons-vue'
const isCollapse = ref(true)
const handleOpen = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
}
</script>
<style>
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px;
min-height: 400px;
}
</style>

View File

@ -1,4 +1,6 @@
<template>
<div class="app-container">
<div class="app-container-inner">
<div class="wscn-http403-container">
<div class="wscn-http403">
<div class="pic-403">
@ -32,6 +34,8 @@
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
@ -39,9 +43,8 @@
<style lang="scss" scoped>
.wscn-http403-container {
transform: translate(-50%, -50%);
transform: translate(-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http403 {

View File

@ -1,8 +1,10 @@
<template>
<div class="app-container">
<div class="app-container-inner">
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__parent" src="@/assets/404_images/404_bg.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
@ -15,15 +17,16 @@
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
</script>
<style lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
transform: translate(-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {

View File

@ -0,0 +1,7 @@
<template>
<div class="app-container">
<div class="app-container-inner">
简易版本分支在 vue-admin-simple
</div>
</div>
</template>

View File

@ -78,10 +78,10 @@ const showPwd = () => {
}
}
const submitForm = (formEl: FormInstance | undefined) => {
loading.value = true
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
loading.value = true
//
setTimeout(async ()=>{
await UserStore.login( ruleForm)

View File

@ -1,29 +0,0 @@
<template>
<u-container-layout class="m-permission-page">
<div style="margin-bottom: 20px"> 权限列表{{ UserStore.roles }} </div>
<el-radio-group v-model="switchRoles">
<el-radio-button label="other" />
<el-radio-button label="admin" />
</el-radio-group>
</u-container-layout>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue'
import {useUserStore} from "@/store/modules/user"
const UserStore = useUserStore()
const switchRoles = computed({
get() {
return UserStore.roles[0]
},
set(val) {
;(async () => {
await UserStore.getInfo([val])
location.reload()
})()
},
})
</script>
<style></style>