From d778ceab613630fc98bacf0c5c795baed498dd3d Mon Sep 17 00:00:00 2001 From: ks3356143 <314298729@qq.com> Date: Mon, 5 Jun 2023 21:02:25 +0800 Subject: [PATCH] 0099 --- cdTMP/.eslintrc.cjs | 15 +- cdTMP/babel.config.js | 3 + cdTMP/package-lock.json | 86 +++++++- cdTMP/package.json | 4 + cdTMP/src/api/login.js | 53 +++++ cdTMP/src/assets/img/avatar.jpg | Bin 0 -> 27531 bytes cdTMP/src/config/setting.json | 17 ++ cdTMP/src/env.d.ts | 1 + cdTMP/src/hooks/logout.js | 24 ++ cdTMP/src/hooks/permission.js | 33 +++ cdTMP/src/layout/components/menu.vue | 146 ++++++++++++ cdTMP/src/layout/components/navbar.vue | 207 ++++++++++++++++++ cdTMP/src/layout/components/tab-bar.vue | 90 ++++++++ cdTMP/src/layout/components/use-menu-tree.js | 66 ++++++ cdTMP/src/layout/default-layout.vue | 168 ++++++++++++++ cdTMP/src/router/app-menus/index.js | 15 ++ cdTMP/src/router/constants.js | 2 +- cdTMP/src/router/guard/index.js | 16 ++ cdTMP/src/router/guard/permisstion.js | 48 ++++ cdTMP/src/router/guard/userLoginInfo.js | 40 ++++ cdTMP/src/router/index.js | 2 + cdTMP/src/router/routes/base.js | 8 +- cdTMP/src/router/routes/guard/index.js | 1 - cdTMP/src/router/routes/guard/permisstion.js | 1 - .../src/router/routes/guard/userLoginInfo.js | 0 cdTMP/src/router/routes/modules/dashboard.js | 8 +- cdTMP/src/store/index.js | 3 + cdTMP/src/store/modules/app.js | 73 ++++++ cdTMP/src/store/modules/user.js | 101 +++++++++ cdTMP/src/utils/index.ts | 22 ++ cdTMP/src/views/dashboard/workplace/index.vue | 7 + cdTMP/src/views/login.vue | 23 +- cdTMP/vite.config.js | 3 +- cdtestplant/tsconfig.json | 2 +- .../src/layout/components/banner/index.vue | 9 - .../src/layout/components/classic/index.vue | 9 - .../components/classic/ma-classic-header.vue | 9 - .../components/classic/ma-classic-slider.vue | 9 - .../components/components/children-banner.vue | 9 - .../components/components/children-menu.vue | 10 - chengduTestPlant/src/layout/setting.vue | 10 - 41 files changed, 1259 insertions(+), 94 deletions(-) create mode 100644 cdTMP/babel.config.js create mode 100644 cdTMP/src/api/login.js create mode 100644 cdTMP/src/assets/img/avatar.jpg create mode 100644 cdTMP/src/config/setting.json create mode 100644 cdTMP/src/env.d.ts create mode 100644 cdTMP/src/hooks/logout.js create mode 100644 cdTMP/src/hooks/permission.js create mode 100644 cdTMP/src/layout/components/menu.vue create mode 100644 cdTMP/src/layout/components/navbar.vue create mode 100644 cdTMP/src/layout/components/tab-bar.vue create mode 100644 cdTMP/src/layout/components/use-menu-tree.js create mode 100644 cdTMP/src/layout/default-layout.vue create mode 100644 cdTMP/src/router/app-menus/index.js create mode 100644 cdTMP/src/router/guard/index.js create mode 100644 cdTMP/src/router/guard/permisstion.js create mode 100644 cdTMP/src/router/guard/userLoginInfo.js delete mode 100644 cdTMP/src/router/routes/guard/index.js delete mode 100644 cdTMP/src/router/routes/guard/permisstion.js delete mode 100644 cdTMP/src/router/routes/guard/userLoginInfo.js create mode 100644 cdTMP/src/store/modules/app.js create mode 100644 cdTMP/src/utils/index.ts create mode 100644 cdTMP/src/views/dashboard/workplace/index.vue diff --git a/cdTMP/.eslintrc.cjs b/cdTMP/.eslintrc.cjs index 4b58fd1..4c45932 100644 --- a/cdTMP/.eslintrc.cjs +++ b/cdTMP/.eslintrc.cjs @@ -3,10 +3,19 @@ module.exports = { browser: true, es2015: true }, - extends: ["eslint:recommended", "plugin:vue/vue3-essential", "plugin:prettier/recommended"], + extends: [ + "eslint:recommended", + "plugin:vue/vue3-essential", + "plugin:prettier/recommended", + "@vue/typescript/recommended", + "plugin:@typescript-eslint/recommended" + ], parserOptions: { - ecmaVersion: "latest", - sourceType: "module" + sourceType: "module", + ecmaVersion: 2020, + ecmaFeatures: { + jsx: true + } }, plugins: ["vue"], rules: { diff --git a/cdTMP/babel.config.js b/cdTMP/babel.config.js new file mode 100644 index 0000000..b84c5dc --- /dev/null +++ b/cdTMP/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + plugins: ['@vue/babel-plugin-jsx'], +} diff --git a/cdTMP/package-lock.json b/cdTMP/package-lock.json index 154e880..45a2b36 100644 --- a/cdTMP/package-lock.json +++ b/cdTMP/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@arco-design/color": "^0.4.0", "@arco-design/web-vue": "^2.46.2", + "@vueuse/core": "^10.1.2", "axios": "^1.4.0", "crypto-js": "^4.1.1", "dayjs": "^1.11.7", @@ -30,9 +31,12 @@ }, "devDependencies": { "@types/lodash": "^4.14.195", + "@types/node": "^20.2.5", + "@types/nprogress": "^0.2.0", "@types/qs": "^6.9.7", "@vitejs/plugin-vue": "^4.1.0", "@vitejs/plugin-vue-jsx": "^3.0.1", + "@vue/babel-plugin-jsx": "^1.1.1", "autoprefixer": "^10.4.14", "eslint": "^8.42.0", "eslint-plugin-vue": "^9.14.1", @@ -1113,8 +1117,13 @@ "version": "20.2.5", "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.2.5.tgz", "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==", - "dev": true, - "peer": true + "dev": true + }, + "node_modules/@types/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/@types/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==", + "dev": true }, "node_modules/@types/qs": { "version": "6.9.7", @@ -1122,6 +1131,11 @@ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", "dev": true }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.17", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz", + "integrity": "sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA==" + }, "node_modules/@vitejs/plugin-vue": { "version": "4.2.3", "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz", @@ -1283,6 +1297,74 @@ "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.4.tgz", "integrity": "sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==" }, + "node_modules/@vueuse/core": { + "version": "10.1.2", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-10.1.2.tgz", + "integrity": "sha512-roNn8WuerI56A5uiTyF/TEYX0Y+VKlhZAF94unUfdhbDUI+NfwQMn4FUnUscIRUhv3344qvAghopU4bzLPNFlA==", + "dependencies": { + "@types/web-bluetooth": "^0.0.17", + "@vueuse/metadata": "10.1.2", + "@vueuse/shared": "10.1.2", + "vue-demi": ">=0.14.0" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.5", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.5.tgz", + "integrity": "sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "10.1.2", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.1.2.tgz", + "integrity": "sha512-3mc5BqN9aU2SqBeBuWE7ne4OtXHoHKggNgxZR2K+zIW4YLsy6xoZ4/9vErQs6tvoKDX6QAqm3lvsrv0mczAwIQ==" + }, + "node_modules/@vueuse/shared": { + "version": "10.1.2", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.1.2.tgz", + "integrity": "sha512-1uoUTPBlgyscK9v6ScGeVYDDzlPSFXBlxuK7SfrDGyUTBiznb3mNceqhwvZHjtDRELZEN79V5uWPTF1VDV8svA==", + "dependencies": { + "vue-demi": ">=0.14.0" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.5", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.5.tgz", + "integrity": "sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.11.6.tgz", diff --git a/cdTMP/package.json b/cdTMP/package.json index ec6c343..2b4ec15 100644 --- a/cdTMP/package.json +++ b/cdTMP/package.json @@ -12,6 +12,7 @@ "dependencies": { "@arco-design/color": "^0.4.0", "@arco-design/web-vue": "^2.46.2", + "@vueuse/core": "^10.1.2", "axios": "^1.4.0", "crypto-js": "^4.1.1", "dayjs": "^1.11.7", @@ -32,9 +33,12 @@ }, "devDependencies": { "@types/lodash": "^4.14.195", + "@types/node": "^20.2.5", + "@types/nprogress": "^0.2.0", "@types/qs": "^6.9.7", "@vitejs/plugin-vue": "^4.1.0", "@vitejs/plugin-vue-jsx": "^3.0.1", + "@vue/babel-plugin-jsx": "^1.1.1", "autoprefixer": "^10.4.14", "eslint": "^8.42.0", "eslint-plugin-vue": "^9.14.1", diff --git a/cdTMP/src/api/login.js b/cdTMP/src/api/login.js new file mode 100644 index 0000000..94e8e5c --- /dev/null +++ b/cdTMP/src/api/login.js @@ -0,0 +1,53 @@ +import { request } from "@/api/request" + +export default { + /** + * 获取验证码 + * @returns + */ + getCaptch() { + return request({ + url: "system/captcha", + method: "get" + }) + }, + + /** + * 用户登录 + * @param {object} params + * @returns + */ + login(params = {}) { + return request({ + url: "system/login", + method: "post", + data: params + }) + }, + + /** + * 用户退出 + * @param {object} params + * @returns + */ + logout(params = {}) { + return request({ + url: "system/logout", + method: "post", + data: params + }) + }, + + /** + * 获取登录用户信息 + * @param {object} params + * @returns + */ + getInfo(params = {}) { + return request({ + url: "system/getInfo", + method: "get", + data: params + }) + } +} diff --git a/cdTMP/src/assets/img/avatar.jpg b/cdTMP/src/assets/img/avatar.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1b42c145f35c06dfa7296491e5f03a5c3810f579 GIT binary patch literal 27531 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 s1~{X>4zfM7l}PD6;3q-1{1AR#{c9g?@LdApq { + await userStore.logout() + const currentRoute = router.currentRoute.value + Message.success('登出成功') + router.push({ + name: logoutTo && typeof logoutTo === 'string' ? logoutTo : 'login', + query: { + ...router.currentRoute.value.query, + redirect: currentRoute.name, + }, + }) + } + return { + logout, + } +} diff --git a/cdTMP/src/hooks/permission.js b/cdTMP/src/hooks/permission.js new file mode 100644 index 0000000..f477e12 --- /dev/null +++ b/cdTMP/src/hooks/permission.js @@ -0,0 +1,33 @@ +import { useUserStore } from "@/store" + +export default function usePermission() { + const userStore = useUserStore() + return { + // 返回的函数可自定义路由权限 + accessRouter(route) { + return ( + !route.meta?.requiresAuth || + !route.meta?.roles || + route.meta?.roles?.includes("*") || + route.meta?.roles?.includes(userStore.role) + ) + }, + findFirstPermissionRoute(_routers, role = "admin") { + const cloneRouters = [..._routers] + while (cloneRouters.length) { + const firstElement = cloneRouters.shift() + if ( + firstElement?.meta?.roles?.find((el) => { + return el.includes("*") || el.includes(role) + }) + ) + return { name: firstElement.name } + if (firstElement?.children) { + cloneRouters.push(...firstElement.children) + } + } + return null + } + // 你可以添加任何规则 + } +} diff --git a/cdTMP/src/layout/components/menu.vue b/cdTMP/src/layout/components/menu.vue new file mode 100644 index 0000000..fd7ee7b --- /dev/null +++ b/cdTMP/src/layout/components/menu.vue @@ -0,0 +1,146 @@ + + + diff --git a/cdTMP/src/layout/components/navbar.vue b/cdTMP/src/layout/components/navbar.vue new file mode 100644 index 0000000..481b14f --- /dev/null +++ b/cdTMP/src/layout/components/navbar.vue @@ -0,0 +1,207 @@ + + + + + + + diff --git a/cdTMP/src/layout/components/tab-bar.vue b/cdTMP/src/layout/components/tab-bar.vue new file mode 100644 index 0000000..f55216e --- /dev/null +++ b/cdTMP/src/layout/components/tab-bar.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/cdTMP/src/layout/components/use-menu-tree.js b/cdTMP/src/layout/components/use-menu-tree.js new file mode 100644 index 0000000..e731332 --- /dev/null +++ b/cdTMP/src/layout/components/use-menu-tree.js @@ -0,0 +1,66 @@ +import { computed } from 'vue' +import usePermission from '@/hooks/permission' +import { useAppStore } from '@/store' +import appClientMenus from '@/router/app-menus' +import { cloneDeep } from 'lodash' + +export default function useMenuTree() { + const permission = usePermission() + const appStore = useAppStore() + const appRoute = computed(() => { + if (appStore.menuFromServer) { + return appStore.appAsyncMenus + } + return appClientMenus + }) + const menuTree = computed(() => { + const copyRouter = cloneDeep(appRoute.value) + copyRouter.sort((a, b) => { + return 0 - 0 + }) + function travel(_routes, layer) { + if (!_routes) return null + + const collector = _routes.map((element) => { + // no access + if (!permission.accessRouter(element)) { + return null + } + + // leaf node + if (element.meta?.hideChildrenInMenu || !element.children) { + element.children = [] + return element + } + + // route filter hideInMenu true + element.children = element.children.filter((x) => x.meta?.hideInMenu !== true) + + // Associated child node + const subItem = travel(element.children, layer + 1) + + if (subItem.length) { + element.children = subItem + return element + } + // the else logic + if (layer > 1) { + element.children = subItem + return element + } + + if (element.meta?.hideInMenu === false) { + return element + } + + return null + }) + return collector.filter(Boolean) + } + return travel(copyRouter, 0) + }) + + return { + menuTree, + } +} diff --git a/cdTMP/src/layout/default-layout.vue b/cdTMP/src/layout/default-layout.vue new file mode 100644 index 0000000..a3beae8 --- /dev/null +++ b/cdTMP/src/layout/default-layout.vue @@ -0,0 +1,168 @@ + + + + + diff --git a/cdTMP/src/router/app-menus/index.js b/cdTMP/src/router/app-menus/index.js new file mode 100644 index 0000000..c7133d9 --- /dev/null +++ b/cdTMP/src/router/app-menus/index.js @@ -0,0 +1,15 @@ +import { appRoutes, appExternalRoutes } from "@/router/routes" + +const mixinRoutes = [...appRoutes, appExternalRoutes] +const appClientMenus = mixinRoutes.map((el) => { + const { name, path, meta, redirect, children } = el + return { + name, + path, + meta, + redirect, + children + } +}) + +export default appClientMenus diff --git a/cdTMP/src/router/constants.js b/cdTMP/src/router/constants.js index c357d6a..e1cf696 100644 --- a/cdTMP/src/router/constants.js +++ b/cdTMP/src/router/constants.js @@ -23,7 +23,7 @@ export const DEFAULT_ROUTE_NAME = "Workplace" * @type: object */ export const DEFAULT_ROUTE = { - title: "menu.dashboard.workplace", + title: "工作台", name: DEFAULT_ROUTE_NAME, fullPath: "/dashboard/workplace" } diff --git a/cdTMP/src/router/guard/index.js b/cdTMP/src/router/guard/index.js new file mode 100644 index 0000000..2020104 --- /dev/null +++ b/cdTMP/src/router/guard/index.js @@ -0,0 +1,16 @@ +import { setRouteEmitter } from "@/utils/route-listener" +import setupUserLoginInfoGuard from "./userLoginInfo" +import setupPermissionGuard from "@/router/guard/permisstion" + +function setupPageGuard(router) { + router.beforeEach(async (to) => { + // 发出路由改变的事件 + setRouteEmitter(to) + }) +} + +export default function createRouteGuard(router) { + setupPageGuard(router) + setupUserLoginInfoGuard(router) + setupPermissionGuard(router) +} diff --git a/cdTMP/src/router/guard/permisstion.js b/cdTMP/src/router/guard/permisstion.js new file mode 100644 index 0000000..436665c --- /dev/null +++ b/cdTMP/src/router/guard/permisstion.js @@ -0,0 +1,48 @@ +import NProgress from "nprogress" // progress bar + +import usePermission from "@/hooks/permission" +import { useUserStore, useAppStore } from "@/store" +import { appRoutes } from "../routes" +import { WHITE_LIST, NOT_FOUND } from "../constants" +// 权限守卫 +export default function setupPermissionGuard(router) { + router.beforeEach(async (to, from, next) => { + const appStore = useAppStore() + const userStore = useUserStore() + const Permission = usePermission() + // 该函数根据角色返回是否有这个路由 + const permissionsAllow = Permission.accessRouter(to) + if (appStore.menuFromServer) { + // 针对来自服务端的菜单配置进行处理 + // Handle routing configuration from the server + + // 根据需要自行完善来源于服务端的菜单配置的permission逻辑 + // Refine the permission logic from the server's menu configuration as needed + if (!appStore.appAsyncMenus.length && !WHITE_LIST.find((el) => el.name === to.name)) { + await appStore.fetchServerMenuConfig() + } + const serverMenuConfig = [...appStore.appAsyncMenus, ...WHITE_LIST] + + let exist = false + while (serverMenuConfig.length && !exist) { + const element = serverMenuConfig.shift() + if (element?.name === to.name) exist = true + + if (element?.children) { + serverMenuConfig.push(...element.children) + } + } + if (exist && permissionsAllow) { + next() + } else next(NOT_FOUND) + } else { + // eslint-disable-next-line no-lonely-if + if (permissionsAllow) next() + else { + const destination = Permission.findFirstPermissionRoute(appRoutes, userStore.role) || NOT_FOUND + next(destination) + } + } + NProgress.done() + }) +} diff --git a/cdTMP/src/router/guard/userLoginInfo.js b/cdTMP/src/router/guard/userLoginInfo.js new file mode 100644 index 0000000..767361a --- /dev/null +++ b/cdTMP/src/router/guard/userLoginInfo.js @@ -0,0 +1,40 @@ +import NProgress from "nprogress" // progress bar +import { useUserStore } from "@/store" +// userInfo守卫 +export default function setupUserLoginInfoGuard(router) { + router.beforeEach(async (to, from, next) => { + NProgress.start() + const userStore = useUserStore() + if (userStore.isLogin()) { + if (userStore.role) { + next() + } else { + try { + await userStore.info() + next() + } catch (error) { + await userStore.logout() + next({ + name: "login", + query: { + redirect: to.name, + ...to.query + } + }) + } + } + } else { + if (to.name === "login") { + next() + return + } + next({ + name: "login", + query: { + redirect: to.name, + ...to.query + } + }) + } + }) +} diff --git a/cdTMP/src/router/index.js b/cdTMP/src/router/index.js index 5c3b66e..5f82340 100644 --- a/cdTMP/src/router/index.js +++ b/cdTMP/src/router/index.js @@ -3,6 +3,7 @@ import { createRouter, createWebHistory } from "vue-router" import { appRoutes } from "./routes" // 引入基本重定向路由和notFound路由 import { REDIRECT_MAIN, NOT_FOUND_ROUTE } from "./routes/base" +import createRouteGuard from "@/router/guard/index" const router = createRouter({ history: createWebHistory(), @@ -28,4 +29,5 @@ const router = createRouter({ return { top: 0 } } }) +createRouteGuard(router) export default router diff --git a/cdTMP/src/router/routes/base.js b/cdTMP/src/router/routes/base.js index ead0e4b..16c68a1 100644 --- a/cdTMP/src/router/routes/base.js +++ b/cdTMP/src/router/routes/base.js @@ -1,15 +1,15 @@ import { REDIRECT_ROUTE_NAME } from "@/router/constants" /** - * @description: 返回import("@/layout/default-layout.vue") + * @description: import('@/layout/default-layout.vue') * @type: Promise对象 */ -export const DEFAULT_LAYOUT = () => import("@/layout/default-layout.vue") +export const DEFAULT_LAYOUT = () => import('@/layout/default-layout.vue') export const REDIRECT_MAIN = { path: "/redirect", name: "redirectWrapper", - component: DEFAULT_LAYOUT, // () => import("@/layout/default-layout.vue") + component: DEFAULT_LAYOUT, // import('@/layout/default-layout.vue') meta: { requiresAuth: true, hideInMenu: true @@ -30,5 +30,5 @@ export const REDIRECT_MAIN = { export const NOT_FOUND_ROUTE = { path: "/:pathMatch(.*)*", name: "notFound", - component: () => import("@/views/not-found/index.vue") + component: () => import("@/layout/404.vue") } diff --git a/cdTMP/src/router/routes/guard/index.js b/cdTMP/src/router/routes/guard/index.js deleted file mode 100644 index 9238b57..0000000 --- a/cdTMP/src/router/routes/guard/index.js +++ /dev/null @@ -1 +0,0 @@ -import { setRouteEmitter } from "@/utils/route-listener" diff --git a/cdTMP/src/router/routes/guard/permisstion.js b/cdTMP/src/router/routes/guard/permisstion.js deleted file mode 100644 index 1452f24..0000000 --- a/cdTMP/src/router/routes/guard/permisstion.js +++ /dev/null @@ -1 +0,0 @@ -import NProgress from 'nprogress' diff --git a/cdTMP/src/router/routes/guard/userLoginInfo.js b/cdTMP/src/router/routes/guard/userLoginInfo.js deleted file mode 100644 index e69de29..0000000 diff --git a/cdTMP/src/router/routes/modules/dashboard.js b/cdTMP/src/router/routes/modules/dashboard.js index cb249d0..3166805 100644 --- a/cdTMP/src/router/routes/modules/dashboard.js +++ b/cdTMP/src/router/routes/modules/dashboard.js @@ -5,10 +5,10 @@ const DASHBOARD = { name: "dashboard", component: DEFAULT_LAYOUT, // () => import("@/layout/default-layout.vue") meta: { - locale: "menu.dashboard", requiresAuth: true, icon: "icon-dashboard", - order: 0 + order: 0, + locale:"图标展示" }, children: [ { @@ -16,9 +16,9 @@ const DASHBOARD = { name: "Workplace", component: () => import("@/views/dashboard/workplace/index.vue"), meta: { - locale: "menu.dashboard.workplace", requiresAuth: true, - roles: ["*"] + roles: ["*"], + locale:"工作台" } }, ] diff --git a/cdTMP/src/store/index.js b/cdTMP/src/store/index.js index 4c451a1..b67f936 100644 --- a/cdTMP/src/store/index.js +++ b/cdTMP/src/store/index.js @@ -1,5 +1,8 @@ import { createPinia } from "pinia" +import useUserStore from "./modules/user" +import useAppStore from "./modules/app" const pinia = createPinia() +export { useUserStore, useAppStore } export default pinia diff --git a/cdTMP/src/store/modules/app.js b/cdTMP/src/store/modules/app.js new file mode 100644 index 0000000..01f4485 --- /dev/null +++ b/cdTMP/src/store/modules/app.js @@ -0,0 +1,73 @@ +import { defineStore } from "pinia" +import { Notification } from "@arco-design/web-vue" +import defaultSettings from "@/config/setting.json" + +const useAppStore = defineStore("app", { + state: () => ({ + ...defaultSettings + }), + getters: { + appCurrentSetting(state) { + return { ...state } + }, + appDevice(state) { + return state.device + }, + appAsyncMenus(state) { + return state.serverMenu + } + }, + actions: { + // 更新设置 + updateSettings(partial) { + this.$patch(partial) + }, + // 改变主题 + toggleTheme(dark) { + if (dark) { + this.theme = "dark" + document.body.setAttribute("arco-theme", "dark") + } else { + this.theme = "light" + document.body.setAttribute("arco-theme", "light") + } + }, + // 切换用户设备 + toggleDevice(device) { + this.device = device + }, + // 切换菜单 + toggleMenu(value) { + this.hideMenu = value + }, + async fetchServerMenuConfig() { + let notifyInstance = null + try { + notifyInstance = Notification.info({ + id: "menuNotice", // Keep the instance id the same + content: "loading", + closable: true + }) + const { data } = await getMenuList() + this.serverMenu = data + notifyInstance = Notification.success({ + id: "menuNotice", + content: "success", + closable: true + }) + } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + notifyInstance = Notification.error({ + id: "menuNotice", + content: "error", + closable: true + }) + } + }, + clearServerMenu() { + this.serverMenu = [] + } + } +}) + +export default useAppStore diff --git a/cdTMP/src/store/modules/user.js b/cdTMP/src/store/modules/user.js index e69de29..739a914 100644 --- a/cdTMP/src/store/modules/user.js +++ b/cdTMP/src/store/modules/user.js @@ -0,0 +1,101 @@ +import { defineStore } from "pinia" +import tool from "@/utils/tool" +import { removeRouteListener } from "@/utils/route-listener" +import loginAPI from "@/api/login" +import { useAppStore } from "@/store" + +const useUserStore = defineStore("user", { + state: () => ({ + name: undefined, + avatar: undefined, + job: undefined, + organization: undefined, + location: undefined, + email: undefined, + introduction: undefined, + personalWebsite: undefined, + jobName: undefined, + organizationName: undefined, + locationName: undefined, + phone: undefined, + registrationDate: undefined, + accountId: undefined, + certification: undefined, + role: '', + }), + + getters: { + setUserInfo(state) { + return { ...state } + } + }, + + actions: { + // 切换角色函数 + switchRoles() { + return new Promise((resolve) => { + this.role = this.role === "user" ? "admin" : "user" + resolve(this.role) + }) + }, + // token相关操作 + isLogin() { + return !!localStorage.getItem(import.meta.env.VITE_APP_TOKEN_PREFIX) + }, + setToken(token) { + tool.local.set(import.meta.env.VITE_APP_TOKEN_PREFIX, token) + }, + getToken() { + return tool.local.get(import.meta.env.VITE_APP_TOKEN_PREFIX) + }, + clearToken() { + tool.local.remove(import.meta.env.VITE_APP_TOKEN_PREFIX) + }, + // 全局更新userInfo信息 + setInfo(data) { + this.$patch(data) + }, + // 请求用户信息 + async info() { + const res = await loginAPI.getInfo() + this.setInfo(res.data) + }, + resetUserInfo() { + this.$reset() + }, + + // login函数,传一个form信息 + login(form) { + return loginAPI + .login(form) + .then((res) => { + if (res.success) { + this.setToken(res.data.token) + return true + } else { + return false + } + }) + .catch((e) => { + console.log(e) + return false + }) + }, + logoutCallBack() { + const appStore = useAppStore() + this.resetUserInfo() + this.clearToken() + removeRouteListener() + appStore.clearServerMenu() + }, + async logout() { + try { + await loginAPI.logout() + } finally { + this.logoutCallBack() + } + } + } +}) + +export default useUserStore diff --git a/cdTMP/src/utils/index.ts b/cdTMP/src/utils/index.ts new file mode 100644 index 0000000..53d7bc4 --- /dev/null +++ b/cdTMP/src/utils/index.ts @@ -0,0 +1,22 @@ +type TargetContext = '_self' | '_parent' | '_blank' | '_top' + +export const openWindow = (url: string, opts?: { target?: TargetContext; [key: string]: any }) => { + const { target = '_blank', ...others } = opts || {} + window.open( + url, + target, + Object.entries(others) + .reduce((preValue: string[], curValue) => { + const [key, value] = curValue + return [...preValue, `${key}=${value}`] + }, []) + .join(',') + ) +} + +export const regexUrl = new RegExp( + '^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', + 'i' +) + +export default null diff --git a/cdTMP/src/views/dashboard/workplace/index.vue b/cdTMP/src/views/dashboard/workplace/index.vue new file mode 100644 index 0000000..dd9e02c --- /dev/null +++ b/cdTMP/src/views/dashboard/workplace/index.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/cdTMP/src/views/login.vue b/cdTMP/src/views/login.vue index cbd274e..6a5c271 100644 --- a/cdTMP/src/views/login.vue +++ b/cdTMP/src/views/login.vue @@ -76,9 +76,9 @@