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 0000000..1b42c14 Binary files /dev/null and b/cdTMP/src/assets/img/avatar.jpg differ diff --git a/cdTMP/src/config/setting.json b/cdTMP/src/config/setting.json new file mode 100644 index 0000000..a5de1e7 --- /dev/null +++ b/cdTMP/src/config/setting.json @@ -0,0 +1,17 @@ +{ + "theme": "light", + "colorWeak": false, + "navbar": true, + "menu": true, + "topMenu": false, + "hideMenu": false, + "menuCollapse": false, + "footer": true, + "themeColor": "#165DFF", + "menuWidth": 220, + "globalSettings": false, + "device": "desktop", + "tabBar": true, + "menuFromServer": false, + "serverMenu": [] +} diff --git a/cdTMP/src/env.d.ts b/cdTMP/src/env.d.ts new file mode 100644 index 0000000..f8268e4 --- /dev/null +++ b/cdTMP/src/env.d.ts @@ -0,0 +1 @@ +declare module "@arco-design/web-vue/dist/arco-vue-icon" diff --git a/cdTMP/src/hooks/logout.js b/cdTMP/src/hooks/logout.js new file mode 100644 index 0000000..7cb62f0 --- /dev/null +++ b/cdTMP/src/hooks/logout.js @@ -0,0 +1,24 @@ +import { useRouter } from 'vue-router' +import { Message } from '@arco-design/web-vue' + +import { useUserStore } from '@/store' + +export default function useUser() { + const router = useRouter() + const userStore = useUserStore() + const logout = async (logoutTo) => { + 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 @@