From 6ef4cf9fb623127e3fce7ba6ad980b9d6d723cf4 Mon Sep 17 00:00:00 2001 From: RealityBoy <1923740402@qq.com> Date: Tue, 25 Oct 2022 12:17:13 +0800 Subject: [PATCH] refactor: permission (#357) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: permission * chore: update * chore: update * chore: update * chore: update * chore: update * chore: update * chore: update * chore: update * chore: update * fix: 修复`mix`混合模式导航在生产环境左侧菜单一定机率不显示的问题 * chore: update * chore: update * chore: update * chore: update * chore: update * chore: update * chore: update * chore: update --- locales/en.yaml | 2 +- locales/zh-CN.yaml | 2 +- mock/asyncRoutes.ts | 75 +++++++-------- mock/list.ts | 2 +- mock/login.ts | 36 +++++++ mock/map.ts | 4 +- mock/refreshToken.ts | 27 ++++++ mock/system.ts | 6 +- package.json | 2 +- pnpm-lock.yaml | 8 +- src/api/list.ts | 3 +- src/api/mock.ts | 4 +- src/api/routes.ts | 8 +- src/api/system.ts | 5 +- src/api/user.ts | 43 ++++++--- src/components/ReAuth/index.ts | 5 + src/components/ReAuth/src/auth.tsx | 20 ++++ src/components/ReFlowChart/src/Control.vue | 3 +- src/components/ReMap/src/Amap.vue | 4 +- src/directives/auth/index.ts | 13 +++ src/directives/index.ts | 2 +- src/directives/permission/index.ts | 18 ---- .../search/components/SearchModal.vue | 3 +- src/layout/components/setting/index.vue | 4 +- src/layout/components/sidebar/mixNav.vue | 2 +- src/layout/components/sidebar/vertical.vue | 5 +- src/layout/hooks/useNav.ts | 22 ++--- src/layout/types.ts | 2 +- src/main.ts | 4 + src/router/index.ts | 21 ++-- src/router/modules/error.ts | 2 +- src/router/types.ts | 1 + src/router/utils.ts | 91 ++++++++++++------ src/store/modules/permission.ts | 37 +------ src/store/modules/types.ts | 4 +- src/store/modules/user.ts | 84 ++++++++-------- src/style/element-plus.scss | 2 +- src/utils/auth.ts | 88 +++++++++++------ src/utils/http/index.ts | 52 +++++----- src/views/able/line-tree.vue | 4 +- src/views/able/menu-tree.vue | 4 +- src/views/login/index.vue | 58 +++++------ src/views/permission/button/index.vue | 96 ++++++++++++++----- src/views/permission/page/index.vue | 96 +++++++++++-------- src/views/tabs/index.vue | 10 +- types/global.d.ts | 1 + types/index.ts | 6 +- 47 files changed, 605 insertions(+), 386 deletions(-) create mode 100644 mock/login.ts create mode 100644 mock/refreshToken.ts create mode 100644 src/components/ReAuth/index.ts create mode 100644 src/components/ReAuth/src/auth.tsx create mode 100644 src/directives/auth/index.ts delete mode 100644 src/directives/permission/index.ts diff --git a/locales/en.yaml b/locales/en.yaml index d737a7fa3..7ef91f83a 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -32,7 +32,7 @@ menus: hsRole: Role Manage hsDept: Dept Manage hseditor: Editor - hserror: Error Page + hsabnormal: Abnormal Page hsfourZeroFour: "404" hsfourZeroOne: "403" hsFive: "500" diff --git a/locales/zh-CN.yaml b/locales/zh-CN.yaml index b15dec00e..6cf4c248f 100644 --- a/locales/zh-CN.yaml +++ b/locales/zh-CN.yaml @@ -32,7 +32,7 @@ menus: hsRole: 角色管理 hsDept: 部门管理 hseditor: 编辑器 - hserror: 错误页面 + hsabnormal: 异常页面 hsfourZeroFour: "404" hsfourZeroOne: "403" hsFive: "500" diff --git a/mock/asyncRoutes.ts b/mock/asyncRoutes.ts index cf56a74bb..d7f02c2a1 100644 --- a/mock/asyncRoutes.ts +++ b/mock/asyncRoutes.ts @@ -1,7 +1,12 @@ -// 根据角色动态生成路由 +// 模拟后端动态生成路由 import { MockMethod } from "vite-plugin-mock"; -// http://mockjs.com/examples.html#Object +/** + * roles:页面级别权限,这里模拟二种 "admin"、"common" + * admin:管理员角色 + * common:普通角色 + */ + const systemRouter = { path: "/system", meta: { @@ -15,7 +20,8 @@ const systemRouter = { name: "User", meta: { icon: "flUser", - title: "menus.hsUser" + title: "menus.hsUser", + roles: ["admin"] } }, { @@ -23,7 +29,8 @@ const systemRouter = { name: "Role", meta: { icon: "role", - title: "menus.hsRole" + title: "menus.hsRole", + roles: ["admin"] } }, { @@ -31,7 +38,8 @@ const systemRouter = { name: "Dept", meta: { icon: "dept", - title: "menus.hsDept" + title: "menus.hsDept", + roles: ["admin"] } }, { @@ -41,7 +49,8 @@ const systemRouter = { meta: { icon: "dict", title: "menus.hsDict", - keepAlive: true + keepAlive: true, + roles: ["admin"] } } ] @@ -52,13 +61,14 @@ const permissionRouter = { meta: { title: "menus.permission", icon: "lollipop", - rank: 7 + rank: 10 }, children: [ { path: "/permission/page/index", name: "PermissionPage", meta: { + roles: ["admin", "common"], title: "menus.permissionPage" } }, @@ -67,7 +77,8 @@ const permissionRouter = { name: "PermissionButton", meta: { title: "menus.permissionButton", - authority: [] + roles: ["admin", "common"], + auths: ["btn_add", "btn_edit", "btn_delete"] } } ] @@ -78,7 +89,7 @@ const frameRouter = { meta: { icon: "monitor", title: "menus.hsExternalPage", - rank: 10 + rank: 7 }, children: [ { @@ -86,14 +97,16 @@ const frameRouter = { name: "FramePure", meta: { title: "menus.hsPureDocument", - frameSrc: "http://yiming_chang.gitee.io/pure-admin-doc" + frameSrc: "http://yiming_chang.gitee.io/pure-admin-doc", + roles: ["admin", "common"] } }, { path: "/external", name: "http://yiming_chang.gitee.io/pure-admin-doc", meta: { - title: "menus.externalLink" + title: "menus.externalLink", + roles: ["admin", "common"] } }, { @@ -101,7 +114,8 @@ const frameRouter = { name: "FrameEp", meta: { title: "menus.hsEpDocument", - frameSrc: "https://element-plus.org/zh-CN/" + frameSrc: "https://element-plus.org/zh-CN/", + roles: ["admin", "common"] } } ] @@ -119,7 +133,8 @@ const tabsRouter = { path: "/tabs/index", name: "Tabs", meta: { - title: "menus.hstabs" + title: "menus.hstabs", + roles: ["admin", "common"] } }, { @@ -127,7 +142,8 @@ const tabsRouter = { name: "TabQueryDetail", meta: { // 不在menu菜单中显示 - showLink: false + showLink: false, + roles: ["admin", "common"] } }, { @@ -135,39 +151,22 @@ const tabsRouter = { component: "params-detail", name: "TabParamsDetail", meta: { - showLink: false + showLink: false, + roles: ["admin", "common"] } } ] }; -// 添加不同按钮权限到/permission/button页面中 -function setDifAuthority(authority, routes) { - routes.children[1].meta.authority = [authority]; - return routes; -} - export default [ { url: "/getAsyncRoutes", method: "get", - response: ({ query }) => { - if (query.name === "admin") { - return { - code: 0, - info: [ - tabsRouter, - frameRouter, - systemRouter, - setDifAuthority("v-admin", permissionRouter) - ] - }; - } else { - return { - code: 0, - info: [tabsRouter, setDifAuthority("v-test", permissionRouter)] - }; - } + response: () => { + return { + success: true, + data: [systemRouter, permissionRouter, frameRouter, tabsRouter] + }; } } ] as MockMethod[]; diff --git a/mock/list.ts b/mock/list.ts index e968d1113..283e1964a 100644 --- a/mock/list.ts +++ b/mock/list.ts @@ -6,7 +6,7 @@ export default [ method: "post", response: () => { return { - code: 0, + success: true, data: { list: [ { diff --git a/mock/login.ts b/mock/login.ts new file mode 100644 index 000000000..cddd4e429 --- /dev/null +++ b/mock/login.ts @@ -0,0 +1,36 @@ +// 根据角色动态生成路由 +import { MockMethod } from "vite-plugin-mock"; + +export default [ + { + url: "/login", + method: "post", + response: ({ body }) => { + if (body.username === "admin") { + return { + success: true, + data: { + username: "admin", + // 一个用户可能有多个角色 + roles: ["admin"], + accessToken: "eyJhbGciOiJIUzUxMiJ9.admin", + refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh", + expires: "2023/10/30 00:00:00" + } + }; + } else { + return { + success: true, + data: { + username: "common", + // 一个用户可能有多个角色 + roles: ["common"], + accessToken: "eyJhbGciOiJIUzUxMiJ9.common", + refreshToken: "eyJhbGciOiJIUzUxMiJ9.commonRefresh", + expires: "2023/10/30 00:00:00" + } + }; + } + } + } +] as MockMethod[]; diff --git a/mock/map.ts b/mock/map.ts index 14f072af5..f3a3c58b2 100644 --- a/mock/map.ts +++ b/mock/map.ts @@ -29,8 +29,8 @@ export default [ method: "get", response: () => { return { - code: 0, - info: mapList() + success: true, + data: mapList() }; } } diff --git a/mock/refreshToken.ts b/mock/refreshToken.ts new file mode 100644 index 000000000..dfdb711f5 --- /dev/null +++ b/mock/refreshToken.ts @@ -0,0 +1,27 @@ +import { MockMethod } from "vite-plugin-mock"; + +// 模拟刷新token接口 +export default [ + { + url: "/refreshToken", + method: "post", + response: ({ body }) => { + if (body.refreshToken) { + return { + success: true, + data: { + accessToken: "eyJhbGciOiJIUzUxMiJ9.admin", + refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh", + // `expires`选择这种日期格式是为了方便调试,后端直接设置时间戳或许更方便(每次都应该递增)。如果后端返回的是时间戳格式,前端开发请来到这个目录`src/utils/auth.ts`,把第`38`行的代码换成expires = data.expires即可。 + expires: "2023/10/30 23:59:59" + } + }; + } else { + return { + success: false, + data: {} + }; + } + } + } +] as MockMethod[]; diff --git a/mock/system.ts b/mock/system.ts index a0ef492e2..d07cc116a 100644 --- a/mock/system.ts +++ b/mock/system.ts @@ -6,7 +6,7 @@ export default [ method: "post", response: () => { return { - code: 0, + success: true, data: { list: [ { @@ -71,7 +71,7 @@ export default [ method: "post", response: () => { return { - code: 0, + success: true, data: [ { name: "杭州总公司", @@ -212,7 +212,7 @@ export default [ method: "post", response: () => { return { - code: 0, + success: true, data: { list: [ { diff --git a/package.json b/package.json index 8ed3cfc9b..af2fd9818 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@pureadmin/components": "^1.1.0", "@pureadmin/descriptions": "^1.1.0", "@pureadmin/table": "^1.2.0", - "@pureadmin/utils": "^1.1.4", + "@pureadmin/utils": "^1.1.5", "@vueuse/core": "^9.3.0", "@vueuse/motion": "^2.0.0-beta.12", "@vueuse/shared": "^9.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da00237a4..e439538b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,7 +22,7 @@ specifiers: "@pureadmin/descriptions": ^1.1.0 "@pureadmin/table": ^1.2.0 "@pureadmin/theme": ^2.4.0 - "@pureadmin/utils": ^1.1.4 + "@pureadmin/utils": ^1.1.5 "@types/element-resize-detector": 1.1.3 "@types/js-cookie": ^3.0.1 "@types/lodash": ^4.14.180 @@ -132,7 +132,7 @@ dependencies: "@pureadmin/components": 1.1.0_vue@3.2.40 "@pureadmin/descriptions": 1.1.0 "@pureadmin/table": 1.2.0 - "@pureadmin/utils": 1.1.4_888d42e6b1d4aaf209a7326195b5949d + "@pureadmin/utils": 1.1.5_888d42e6b1d4aaf209a7326195b5949d "@vueuse/core": 9.3.0_vue@3.2.40 "@vueuse/motion": 2.0.0-beta.12_vue@3.2.40 "@vueuse/shared": 9.3.0_vue@3.2.40 @@ -1428,10 +1428,10 @@ packages: string-hash: 1.1.3 dev: true - /@pureadmin/utils/1.1.4_888d42e6b1d4aaf209a7326195b5949d: + /@pureadmin/utils/1.1.5_888d42e6b1d4aaf209a7326195b5949d: resolution: { - integrity: sha512-c3Zl9v6usKUqz6y8wYhk89g/hXz/I5QzHS7dTum8/YomqDMBph7c70u0J1dAgruDnEIIB2SNDuEWyGD8054WsQ== + integrity: sha512-5nQZyFAbs59gkMBj0WLox7BlY7llILR/ENo2QNEKW6avMt8sDL1+858EFjEbELl6enPsVvJpoCTxatmZzVjyAw== } peerDependencies: dayjs: "*" diff --git a/src/api/list.ts b/src/api/list.ts index 53c5ad5d0..5e59d6ffd 100644 --- a/src/api/list.ts +++ b/src/api/list.ts @@ -1,12 +1,11 @@ import { http } from "../utils/http"; type Result = { + success: boolean; data?: { /** 列表数据 */ list: Array; }; - code?: number; - msg?: string; }; /** 卡片列表 */ diff --git a/src/api/mock.ts b/src/api/mock.ts index 4ec12acb5..289c5b421 100644 --- a/src/api/mock.ts +++ b/src/api/mock.ts @@ -1,8 +1,8 @@ import { http } from "../utils/http"; type Result = { - code: number; - info: Array; + success: boolean; + data: Array; }; /** 地图数据 */ diff --git a/src/api/routes.ts b/src/api/routes.ts index 7da04fbee..37f5e8f8c 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -1,10 +1,10 @@ import { http } from "../utils/http"; type Result = { - code: number; - info: Array; + success: boolean; + data: Array; }; -export const getAsyncRoutes = (params?: object) => { - return http.request("get", "/getAsyncRoutes", { params }); +export const getAsyncRoutes = () => { + return http.request("get", "/getAsyncRoutes"); }; diff --git a/src/api/system.ts b/src/api/system.ts index 0faa0dacc..be3db8ef9 100644 --- a/src/api/system.ts +++ b/src/api/system.ts @@ -1,14 +1,13 @@ import { http } from "../utils/http"; type Result = { + success: boolean; data?: { /** 列表数据 */ list: Array; /** 总数 */ - total: number; + total?: number; }; - code?: number; - msg?: string; }; /** 获取用户管理列表 */ diff --git a/src/api/user.ts b/src/api/user.ts index 3965ab586..8893dba2f 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -1,26 +1,39 @@ import { http } from "../utils/http"; -type Result = { - svg?: string; - code?: number; - info?: object; +export type UserResult = { + success: boolean; + data: { + /** 用户名 */ + username: string; + /** 当前登陆用户的角色 */ + roles: Array; + /** `token` */ + accessToken: string; + /** 用于调用刷新`accessToken`的接口时所需的`token` */ + refreshToken: string; + /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */ + expires: Date; + }; }; -/** 获取验证码 */ -export const getVerify = () => { - return http.request("get", "/captcha"); +export type RefreshTokenResult = { + success: boolean; + data: { + /** `token` */ + accessToken: string; + /** 用于调用刷新`accessToken`的接口时所需的`token` */ + refreshToken: string; + /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */ + expires: Date; + }; }; /** 登录 */ -export const getLogin = (data: object) => { - return http.request("post", "/login", { data }); +export const getLogin = (data?: object) => { + return http.request("post", "/login", { data }); }; /** 刷新token */ -export const refreshToken = (data: object) => { - return http.request("post", "/refreshToken", { data }); +export const refreshTokenApi = (data?: object) => { + return http.request("post", "/refreshToken", { data }); }; - -// export const searchVague = (data: object) => { -// return http.request("post", "/searchVague", { data }); -// }; diff --git a/src/components/ReAuth/index.ts b/src/components/ReAuth/index.ts new file mode 100644 index 000000000..975ed2cac --- /dev/null +++ b/src/components/ReAuth/index.ts @@ -0,0 +1,5 @@ +import auth from "./src/auth"; + +const Auth = auth; + +export { Auth }; diff --git a/src/components/ReAuth/src/auth.tsx b/src/components/ReAuth/src/auth.tsx new file mode 100644 index 000000000..442565874 --- /dev/null +++ b/src/components/ReAuth/src/auth.tsx @@ -0,0 +1,20 @@ +import { defineComponent, Fragment } from "vue"; +import { hasAuth } from "/@/router/utils"; + +export default defineComponent({ + name: "Auth", + props: { + value: { + type: undefined, + default: [] + } + }, + setup(props, { slots }) { + return () => { + if (!slots) return null; + return hasAuth(props.value) ? ( + {slots.default?.()} + ) : null; + }; + } +}); diff --git a/src/components/ReFlowChart/src/Control.vue b/src/components/ReFlowChart/src/Control.vue index 3ae96a4fe..1496b3ef2 100644 --- a/src/components/ReFlowChart/src/Control.vue +++ b/src/components/ReFlowChart/src/Control.vue @@ -116,7 +116,8 @@ onMounted(() => { :disabled="item.disabled" :style="{ cursor: item.disabled === false ? 'pointer' : 'not-allowed', - color: item.disabled === false ? '' : '#00000040' + color: item.disabled === false ? '' : '#00000040', + background: 'transparent' }" @click="onControl(item, key)" > diff --git a/src/components/ReMap/src/Amap.vue b/src/components/ReMap/src/Amap.vue index e4a341514..6ab36a5c5 100644 --- a/src/components/ReMap/src/Amap.vue +++ b/src/components/ReMap/src/Amap.vue @@ -92,8 +92,8 @@ onBeforeMount(() => { // 获取模拟车辆信息 mapJson() - .then(({ info }) => { - let points: object = info.map(v => { + .then(({ data }) => { + let points: object = data.map(v => { return { lnglat: [v.lng, v.lat], ...v diff --git a/src/directives/auth/index.ts b/src/directives/auth/index.ts new file mode 100644 index 000000000..306dfd336 --- /dev/null +++ b/src/directives/auth/index.ts @@ -0,0 +1,13 @@ +import { hasAuth } from "/@/router/utils"; +import { Directive, type DirectiveBinding } from "vue"; + +export const auth: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const { value } = binding; + if (value) { + !hasAuth(value) && el.parentNode.removeChild(el); + } else { + throw new Error("need auths! Like v-auth=\"['btn.add','btn.edit']\""); + } + } +}; diff --git a/src/directives/index.ts b/src/directives/index.ts index ca528d7b3..d6d6592d8 100644 --- a/src/directives/index.ts +++ b/src/directives/index.ts @@ -1,2 +1,2 @@ -export * from "./permission"; +export * from "./auth"; export * from "./elResizeDetector"; diff --git a/src/directives/permission/index.ts b/src/directives/permission/index.ts deleted file mode 100644 index c31150887..000000000 --- a/src/directives/permission/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { usePermissionStoreHook } from "/@/store/modules/permission"; -import { Directive } from "vue"; -import type { DirectiveBinding } from "vue"; - -export const auth: Directive = { - mounted(el: HTMLElement, binding: DirectiveBinding) { - const { value } = binding; - if (value) { - const authRoles = value; - const hasAuth = usePermissionStoreHook().buttonAuth.includes(authRoles); - if (!hasAuth) { - el.parentNode.removeChild(el); - } - } else { - throw new Error("need roles! Like v-auth=\"['admin','test']\""); - } - } -}; diff --git a/src/layout/components/search/components/SearchModal.vue b/src/layout/components/search/components/SearchModal.vue index 593e63193..5b6cd242a 100644 --- a/src/layout/components/search/components/SearchModal.vue +++ b/src/layout/components/search/components/SearchModal.vue @@ -1,5 +1,6 @@