tongshangming 3 éve
szülő
commit
301da0701d

+ 2 - 0
package.json

@@ -11,6 +11,7 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.0.9",
+    "@icon-park/vue-next": "^1.4.2",
     "@vueuse/core": "^9.1.1",
     "axios": "^0.27.2",
     "element-plus": "^2.2.15",
@@ -33,6 +34,7 @@
     "eslint-plugin-vue": "^9.3.0",
     "npm-run-all": "^4.1.5",
     "prettier": "^2.7.1",
+    "sass": "^1.54.9",
     "typescript": "~4.7.4",
     "unocss": "^0.45.14",
     "unplugin-auto-import": "^0.11.2",

+ 34 - 6
pnpm-lock.yaml

@@ -2,6 +2,7 @@ lockfileVersion: 5.4
 
 specifiers:
   '@element-plus/icons-vue': ^2.0.9
+  '@icon-park/vue-next': ^1.4.2
   '@iconify-json/ep': ^1.1.7
   '@rushstack/eslint-patch': ^1.1.4
   '@types/node': ^16.11.47
@@ -20,6 +21,7 @@ specifiers:
   nprogress: ^0.2.0
   pinia: ^2.0.17
   prettier: ^2.7.1
+  sass: ^1.54.9
   typescript: ~4.7.4
   unocss: ^0.45.14
   unplugin-auto-import: ^0.11.2
@@ -32,6 +34,7 @@ specifiers:
 
 dependencies:
   '@element-plus/icons-vue': 2.0.9_vue@3.2.38
+  '@icon-park/vue-next': 1.4.2_vue@3.2.38
   '@vueuse/core': 9.1.1_vue@3.2.38
   axios: 0.27.2
   element-plus: 2.2.15_vue@3.2.38
@@ -54,12 +57,13 @@ devDependencies:
   eslint-plugin-vue: 9.4.0_eslint@8.23.0
   npm-run-all: 4.1.5
   prettier: 2.7.1
+  sass: 1.54.9
   typescript: 4.7.4
   unocss: 0.45.14_vite@3.0.9
   unplugin-auto-import: 0.11.2_jfxn32i2eq37u3jpfk3zc3f4hy
   unplugin-icons: 0.14.9_vite@3.0.9
   unplugin-vue-components: 0.22.4_vite@3.0.9+vue@3.2.38
-  vite: 3.0.9
+  vite: 3.0.9_sass@1.54.9
   vue-tsc: 0.39.5_typescript@4.7.4
 
 packages:
@@ -445,6 +449,15 @@ packages:
     resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
     dev: true
 
+  /@icon-park/vue-next/1.4.2_vue@3.2.38:
+    resolution: {integrity: sha512-+QklF255wkfBOabY+xw6FAI0Bwln/RhdwCunNy/9sKdKuChtaU67QZqU67KGAvZUTeeBgsL+yaHHxqfQeGZXEQ==}
+    engines: {node: '>= 8.0.0', npm: '>= 5.0.0'}
+    peerDependencies:
+      vue: 3.x
+    dependencies:
+      vue: 3.2.38
+    dev: false
+
   /@iconify-json/ep/1.1.7:
     resolution: {integrity: sha512-GhXWVKalXFlrGgfrCXAgqBre5hv3pPAknuxyywmjamcrL5gl5Mq9WOZtuhb4cB6cJ5pMiKOMtegt73FheqWscA==}
     dependencies:
@@ -847,7 +860,7 @@ packages:
       '@unocss/scope': 0.45.14
       '@unocss/transformer-directives': 0.45.14
       magic-string: 0.26.3
-      vite: 3.0.9
+      vite: 3.0.9_sass@1.54.9
     dev: true
 
   /@vitejs/plugin-vue-jsx/2.0.0_vite@3.0.9+vue@3.2.38:
@@ -861,7 +874,7 @@ packages:
       '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.18.13
       '@babel/plugin-transform-typescript': 7.18.12_@babel+core@7.18.13
       '@vue/babel-plugin-jsx': 1.1.1_@babel+core@7.18.13
-      vite: 3.0.9
+      vite: 3.0.9_sass@1.54.9
       vue: 3.2.38
     transitivePeerDependencies:
       - supports-color
@@ -874,7 +887,7 @@ packages:
       vite: ^3.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 3.0.9
+      vite: 3.0.9_sass@1.54.9
       vue: 3.2.38
     dev: true
 
@@ -2164,6 +2177,10 @@ packages:
     engines: {node: '>= 4'}
     dev: true
 
+  /immutable/4.1.0:
+    resolution: {integrity: sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==}
+    dev: true
+
   /import-fresh/3.3.0:
     resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
     engines: {node: '>=6'}
@@ -2870,6 +2887,16 @@ packages:
     resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
     dev: true
 
+  /sass/1.54.9:
+    resolution: {integrity: sha512-xb1hjASzEH+0L0WI9oFjqhRi51t/gagWnxLiwUNMltA0Ab6jIDkAacgKiGYKM9Jhy109osM7woEEai6SXeJo5Q==}
+    engines: {node: '>=12.0.0'}
+    hasBin: true
+    dependencies:
+      chokidar: 3.5.3
+      immutable: 4.1.0
+      source-map-js: 1.0.2
+    dev: true
+
   /scule/0.3.2:
     resolution: {integrity: sha512-zIvPdjOH8fv8CgrPT5eqtxHQXmPNnV/vHJYffZhE43KZkvULvpCTvOt1HPlFaCZx287INL9qaqrZg34e8NgI4g==}
     dev: true
@@ -3286,7 +3313,7 @@ packages:
     dependencies:
       acorn: 8.8.0
       chokidar: 3.5.3
-      vite: 3.0.9
+      vite: 3.0.9_sass@1.54.9
       webpack-sources: 3.2.3
       webpack-virtual-modules: 0.4.4
     dev: true
@@ -3319,7 +3346,7 @@ packages:
       spdx-expression-parse: 3.0.1
     dev: true
 
-  /vite/3.0.9:
+  /vite/3.0.9_sass@1.54.9:
     resolution: {integrity: sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw==}
     engines: {node: ^14.18.0 || >=16.0.0}
     hasBin: true
@@ -3342,6 +3369,7 @@ packages:
       postcss: 8.4.16
       resolve: 1.22.1
       rollup: 2.77.3
+      sass: 1.54.9
     optionalDependencies:
       fsevents: 2.3.2
     dev: true

+ 2 - 0
src/assets/base.css

@@ -52,6 +52,8 @@
 
 :root {
   --el-color-primary: #3454cd;
+  --el-aside-width: 200px;
+  --el-main-padding: 16px;
 }
 
 *,

BIN
src/assets/logo.png


+ 1 - 0
src/components.d.ts

@@ -25,6 +25,7 @@ declare module '@vue/runtime-core' {
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElPagination: typeof import('element-plus/es')['ElPagination']
+    ElSpace: typeof import('element-plus/es')['ElSpace']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
     Exception: typeof import('./components/Exception.vue')['default']

+ 57 - 20
src/components/GlobalHeader.vue

@@ -1,35 +1,72 @@
-<script setup></script>
+<script setup>
+import { useMenuStore } from '@/stores/menu'
+import logo from '@/assets/logo.png'
+
+const menuStore = useMenuStore()
+const [value, toggle] = useToggle()
+
+const handleToggle = () => {
+  toggle()
+  menuStore.setCollapse(value.value)
+}
+</script>
 
 <template>
   <header class="layout-header">
-    <div></div>
-    <div class="flex items-center">
-      <el-avatar class="mr-10px"></el-avatar>
-      <el-dropdown>
-        <span class="el-dropdown-link">
-          Dropdown List
-          <el-icon class="el-icon--right">
-            <i-ep-arrow-down />
-          </el-icon>
-        </span>
-        <template #dropdown>
-          <el-dropdown-menu>
-            <el-dropdown-item>Action 1</el-dropdown-item>
-            <el-dropdown-item>Action 2</el-dropdown-item>
-            <el-dropdown-item>Action 3</el-dropdown-item>
-          </el-dropdown-menu>
-        </template>
-      </el-dropdown>
+    <div class="logo flex items-center justify-center pl-2" :class="{ collapse: value }">
+      <img :src="logo" />
+      <div class="ml-2" :class="{ hidden: value }">方是科技管理系统</div>
+    </div>
+    <div class="flex flex-grow items-center justify-between px-4">
+      <el-space :size="20">
+        <el-button link @click="handleToggle">
+          <icon-menu-unfold-one size="22" :strokeWidth="2" v-if="!value" />
+          <icon-menu-fold-one size="22" :strokeWidth="2" v-else />
+        </el-button>
+        <el-button link @click=""><icon-refresh size="22" :strokeWidth="2" /></el-button>
+      </el-space>
+      <div class="flex items-center">
+        <el-avatar class="mr-10px"></el-avatar>
+        <el-dropdown>
+          <span class="el-dropdown-link">
+            Dropdown List
+            <el-icon class="el-icon--right">
+              <i-ep-arrow-down />
+            </el-icon>
+          </span>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item>Action 1</el-dropdown-item>
+              <el-dropdown-item>Action 2</el-dropdown-item>
+              <el-dropdown-item>Action 3</el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
     </div>
   </header>
 </template>
 
-<style scoped>
+<style scoped lang="scss">
 .layout-header {
   background-color: #fff;
   height: 100%;
   display: flex;
   justify-content: space-between;
   align-items: center;
+
+  .logo {
+    width: var(--el-aside-width);
+
+    img {
+      height: 36px;
+    }
+  }
+  .collapse {
+    width: calc(var(--el-menu-icon-width) + var(--el-menu-base-level-padding) * 2);
+  }
+}
+.hidden {
+  display: none;
 }
 </style>

+ 15 - 4
src/components/GlobalMenu.vue

@@ -1,6 +1,10 @@
 <script setup lang="ts">
 import { useRouterStore } from '@/stores/router'
+import { useMenuStore } from '@/stores/menu'
 
+const menuStore = useMenuStore()
+
+// 生成菜单部分开始
 const routerStore = useRouterStore()
 const menuRouter = routerStore.menuRouter
 const generatorMenu = (routes: any[]) => {
@@ -13,20 +17,23 @@ const generatorMenu = (routes: any[]) => {
     }
   })
 }
-
 generatorMenu(menuRouter)
+// 生成菜单部分结束
+
+const route = useRoute()
 </script>
 
 <template>
-  <el-menu default-active="2" router class="layout-menu">
+  <el-menu :default-active="route.path" :collapse="menuStore.collapse" router class="layout-menu">
     <component :is="item.menuName" :index="item.path" v-for="item in menuRouter">
+      <el-icon v-if="item.menuName === 'el-menu-item'"><component :is="item.meta?.icon"></component></el-icon>
       <template #title>
-        <el-icon><component :is="item.meta?.icon"></component></el-icon>
+        <el-icon v-if="item.menuName === 'el-sub-menu'"><component :is="item.meta?.icon"></component></el-icon>
         <span>{{ item.meta.title }}</span>
       </template>
       <el-menu-item :index="item.path + '/' + subItem.path" v-for="subItem in item.children">
         <template #title>
-          <el-icon><component :is="subItem.meta?.icon"></component></el-icon>
+          <!-- <el-icon><component :is="subItem.meta?.icon"></component></el-icon> -->
           <span>{{ subItem.meta.title }}</span>
         </template>
       </el-menu-item>
@@ -39,4 +46,8 @@ generatorMenu(menuRouter)
   background-color: #fff;
   height: 100%;
 }
+.layout-menu .el-menu-item.is-active {
+  background-color: var(--el-color-primary);
+  color: #fff;
+}
 </style>

+ 5 - 1
src/components/ProTable.vue

@@ -1,4 +1,6 @@
 <script setup lang="ts">
+import { ElMessageBox } from 'element-plus'
+
 const props = defineProps({
   request: {
     type: Function,
@@ -54,7 +56,9 @@ const handleUpdate = () => {
   query.value = {}
 }
 const handleDelete = () => {
-  query.value = {}
+  ElMessageBox.confirm('您确定要删除该项吗', '提示', {
+    type: 'warning'
+  }).then(() => {})
 }
 const handlePatchDelete = () => {}
 // ============== crud部分结束 ===============

+ 4 - 6
src/layouts/BasicLayout.vue

@@ -2,13 +2,11 @@
 
 <template>
   <el-container class="layout-container">
-    <el-aside width="200px">
-      <global-menu />
-    </el-aside>
+    <el-header>
+      <global-header />
+    </el-header>
     <el-container>
-      <el-header>
-        <global-header />
-      </el-header>
+      <global-menu />
       <el-main>
         <router-view></router-view>
       </el-main>

+ 4 - 0
src/main.ts

@@ -6,6 +6,8 @@ import 'uno.css'
 import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
 
+import {install} from '@icon-park/vue-next/es/all'
+
 import App from './App.vue'
 import router from './router'
 
@@ -17,4 +19,6 @@ app.use(createPinia())
 app.use(router)
 app.use(ElementPlus)
 
+install(app)
+
 app.mount('#app')

+ 10 - 7
src/router/asyncRouter.ts

@@ -2,26 +2,29 @@ import type { RouteRecordRaw } from 'vue-router'
 
 const asyncRouter:RouteRecordRaw[] = [
   {
-    path: 'home',
+    path: '/home',
     name: 'home',
     component: () => import('@/views/exception/403.vue'),
     meta: {
-      title: '首页'
+      title: '首页',
+      icon: 'icon-home'
     }
   },
   {
-    path: 'setting',
+    path: '/setting',
     name: 'setting',
     component: () => import('@/views/exception/403.vue'),
     meta: {
-      title: '设置'
+      title: '设置',
+      icon: 'icon-setting'
     }
   },
   {
     path: '/system',
     name: 'system',
     meta: {
-      title: '系统管理'
+      title: '系统管理',
+      icon: 'icon-system'
     },
     children: [
       {
@@ -30,7 +33,7 @@ const asyncRouter:RouteRecordRaw[] = [
         component: () => import('@/views/system/user.vue'),
         meta: {
           title: '用户管理',
-          icon: 'i-ep-user'
+          icon: 'icon-user'
         }
       },
       {
@@ -39,7 +42,7 @@ const asyncRouter:RouteRecordRaw[] = [
         component: () => import('@/views/system/menu.vue'),
         meta: {
           title: '菜单管理',
-          icon: 'i-ep-menu'
+          icon: 'icon-menu'
         }
       },
     ]

+ 12 - 0
src/stores/menu.ts

@@ -0,0 +1,12 @@
+
+export const useMenuStore = defineStore({
+  id: 'menu',
+  state: () => ({
+    collapse: false
+  }),
+  actions: {
+    setCollapse(collapse: boolean) {
+      this.collapse = collapse
+    }
+  }
+})

+ 4 - 1
src/utils/request.ts

@@ -10,6 +10,8 @@ const request = axios.create({
 // 异常拦截处理器
 const errorHandler = (error: any) => {
   const status = error.response.status
+  const userStore = useUserStore()
+
   if (status === 401) {
     ElMessageBox.confirm(
       '当前用户无权限访问当前资源,请尝试重新登录后再操作。',
@@ -21,13 +23,14 @@ const errorHandler = (error: any) => {
         confirmButtonText: '重新登录',
       }
     ).then(() => {
+      userStore.$reset()
       router.replace({ path: '/login' })
     })
   } else if (status === 403) {
     ElMessage.error('无权限访问')
   }
   return Promise.reject(error.response)
-};
+}
 
 request.interceptors.request.use(config => {
   const userStore = useUserStore()