diff --git a/new-ui/projects/admin/src/components/PermissionRender.vue b/new-ui/projects/admin/src/components/PermissionRender.vue new file mode 100644 index 00000000..07493888 --- /dev/null +++ b/new-ui/projects/admin/src/components/PermissionRender.vue @@ -0,0 +1,10 @@ + + diff --git a/new-ui/projects/admin/src/composables/useSubmit.ts b/new-ui/projects/admin/src/composables/useSubmit.ts index 35a34aee..9f44b196 100644 --- a/new-ui/projects/admin/src/composables/useSubmit.ts +++ b/new-ui/projects/admin/src/composables/useSubmit.ts @@ -1,5 +1,4 @@ import { ref, reactive, unref } from "vue"; -import { Message } from "@arco-design/web-vue"; import type { BaseResponse } from "@chatgpt-plus/packages/type"; function useSubmit = Record, R = any>(defaultData?: T) { const formRef = ref(); @@ -10,14 +9,16 @@ function useSubmit = Record, R = any> submitting.value = true; try { const hasError = await formRef.value?.validate(); - if (!hasError) { - const { data, message } = await api({ ...formData ?? {}, ...unref(params) }); - message && Message.success(message); - return Promise.resolve({ formData, data }); + if (hasError) return Promise.reject({ validateErrors: hasError }); + + const { data, code, message } = await api({ ...formData ?? {}, ...unref(params) }); + if (code) { + return Promise.reject({ requestErrors: message }) } - return Promise.reject(false); + + return Promise.resolve({ formData, data }); } catch (err) { - return Promise.reject(err); + return Promise.reject({ errors: err }); } finally { submitting.value = false; } diff --git a/new-ui/projects/admin/src/directives/permission.ts b/new-ui/projects/admin/src/directives/permission.ts new file mode 100644 index 00000000..35c51897 --- /dev/null +++ b/new-ui/projects/admin/src/directives/permission.ts @@ -0,0 +1,32 @@ +import { useAuthStore } from "@/stores/auth"; + +// 判断操作权限 +export function hasPermission(permissionTag: string | string[] | boolean) { + const authStore = useAuthStore(); + const { is_super_admin, permissions = [] } = authStore; + if (is_super_admin) { + return true; + } + if (Array.isArray(permissionTag)) { + return permissionTag.every((tag) => permissions.includes(tag)); + } + if (typeof permissionTag === "string") { + return permissions.includes(permissionTag); + } + return permissionTag; +} + +function checkPermission(el, binding) { + if (!hasPermission(binding.value)) { + el.parentNode && el.parentNode.removeChild(el); + } +} + +export const permission = { + mounted(el, binding) { + checkPermission(el, binding); + }, + updated(el, binding) { + checkPermission(el, binding); + }, +}; diff --git a/new-ui/projects/admin/src/http/config.ts b/new-ui/projects/admin/src/http/config.ts index d7f24509..14ddc588 100644 --- a/new-ui/projects/admin/src/http/config.ts +++ b/new-ui/projects/admin/src/http/config.ts @@ -8,8 +8,9 @@ export const uploadUrl = import.meta.env.VITE_PROXY_BASE_URL + "/api/admin/uploa export const instance = createInstance(import.meta.env.VITE_PROXY_BASE_URL) instance.interceptors.request.use((config) => { - config.headers[__AUTH_KEY] = localStorage.getItem(__AUTH_KEY); - config.headers["Authorization"] = localStorage.getItem(__AUTH_KEY); + const TOKEN = JSON.parse(localStorage.getItem(__AUTH_KEY))?.token + config.headers[__AUTH_KEY] = TOKEN; + config.headers["Authorization"] = TOKEN; return config; }); diff --git a/new-ui/projects/admin/src/http/login.ts b/new-ui/projects/admin/src/http/login.ts index 8e159b65..8b083330 100644 --- a/new-ui/projects/admin/src/http/login.ts +++ b/new-ui/projects/admin/src/http/login.ts @@ -1,9 +1,6 @@ import http from "@/http/config"; -export const userLogin = (data: { - username: string; - password: string; -}) => { +export const userLogin = (data) => { return http({ url: "/api/admin/login", method: "post", @@ -26,7 +23,7 @@ export const getSession = () => { }; -export const loginLog = (params?: Record) => { +export const loginLog = (params) => { return http({ url: "/api/admin/user/loginLog", method: "get", diff --git a/new-ui/projects/admin/src/main.ts b/new-ui/projects/admin/src/main.ts index 559f2f7c..08dba0c6 100644 --- a/new-ui/projects/admin/src/main.ts +++ b/new-ui/projects/admin/src/main.ts @@ -3,6 +3,8 @@ import { createPinia } from "pinia"; import ArcoVue from "@arco-design/web-vue"; import ArcoVueIcon from "@arco-design/web-vue/es/icon"; import "@arco-design/web-vue/dist/arco.css"; +import PermissionRender from "@/components/PermissionRender.vue"; +import { permission } from "@/directives/permission"; import App from "./App.vue"; import router from "./router"; @@ -14,6 +16,9 @@ app.use(router); app.use(ArcoVue); app.use(ArcoVueIcon); +app.component("PermissionRender", PermissionRender); +app.directive("permission", permission); + app.mount("#app"); app.config.warnHandler = (msg, vm, trace) => { if (msg.includes('Invalid prop name: "key" is a reserved property.')) { diff --git a/new-ui/projects/admin/src/router/index.ts b/new-ui/projects/admin/src/router/index.ts index a578cbd3..dabd58d1 100644 --- a/new-ui/projects/admin/src/router/index.ts +++ b/new-ui/projects/admin/src/router/index.ts @@ -1,14 +1,27 @@ import { createRouter, createWebHashHistory } from 'vue-router' import { useAuthStore } from "@/stores/auth"; import CustomLayout from '@/components/CustomLayout.vue' +import { hasPermission } from "@/directives/permission"; import menu from './menu' +declare module 'vue-router' { + interface RouteMeta { + title?: string + permission?: string + } +} + const whiteListRoutes = [ { path: "/login", name: "Login", component: () => import("@/views/LoginView.vue"), }, + { + path: "/403", + name: "403", + component: () => import("@/views/NoPermission.vue"), + }, { path: "/:pathMatch(.*)*", name: "404", @@ -44,9 +57,13 @@ router.beforeEach((to, _, next) => { return; } if (!authStore.token) { + authStore.$reset(); next({ name: "Login" }); return; } + if (to.meta.permission) { + next(!hasPermission(to.meta.permission) ? { name: "403" } : undefined); + } next(); }); diff --git a/new-ui/projects/admin/src/router/menu.ts b/new-ui/projects/admin/src/router/menu.ts index 926dac74..4f037cba 100644 --- a/new-ui/projects/admin/src/router/menu.ts +++ b/new-ui/projects/admin/src/router/menu.ts @@ -12,6 +12,7 @@ import { IconCodepen, IconWechatpay, IconRobot, + IconSafe, } from "@arco-design/web-vue/es/icon"; const menu = [ @@ -133,6 +134,15 @@ const menu = [ }, component: () => import("@/views/SysAdmin/SysAdminContainer.vue"), }, + { + path: "/sysPermission", + name: "SysPermission", + meta: { + title: "权限配置", + icon: IconSafe, + }, + component: () => import("@/views/SysPermission/SysPermissionContainer.vue"), + }, ]; export default menu; diff --git a/new-ui/projects/admin/src/stores/auth.ts b/new-ui/projects/admin/src/stores/auth.ts index 1e9e90b4..9027648a 100644 --- a/new-ui/projects/admin/src/stores/auth.ts +++ b/new-ui/projects/admin/src/stores/auth.ts @@ -3,19 +3,29 @@ import { Message } from '@arco-design/web-vue' import { userLogin, userLogout } from '@/http/login' import router from '@/router' +const defaultState: { + token: string + is_super_admin?: boolean; + permissions?: string[] +} = { + token: null, + is_super_admin: false, + permissions: [] +} + export const useAuthStore = defineStore({ - id: __AUTH_KEY, - state: () => ({ token: null } as { token: string | null }), + id: Symbol(__AUTH_KEY).toString(), + state: () => ({ ...defaultState }), actions: { init() { - this.$state.token = localStorage.getItem(__AUTH_KEY); + this.$state = JSON.parse(localStorage.getItem(__AUTH_KEY)); }, async login(params: any) { try { const { data } = await userLogin(params) if (data) { - this.$state.token = data; - localStorage.setItem(__AUTH_KEY, data) + this.$state = data; + localStorage.setItem(__AUTH_KEY, JSON.stringify(data)) Message.success('登录成功'); router.replace({ name: 'home' }) return Promise.resolve(data) diff --git a/new-ui/projects/admin/src/stores/counter.ts b/new-ui/projects/admin/src/stores/counter.ts deleted file mode 100644 index b6757ba5..00000000 --- a/new-ui/projects/admin/src/stores/counter.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ref, computed } from 'vue' -import { defineStore } from 'pinia' - -export const useCounterStore = defineStore('counter', () => { - const count = ref(0) - const doubleCount = computed(() => count.value * 2) - function increment() { - count.value++ - } - - return { count, doubleCount, increment } -}) diff --git a/new-ui/projects/admin/src/views/NoPermission.vue b/new-ui/projects/admin/src/views/NoPermission.vue new file mode 100644 index 00000000..64b7e278 --- /dev/null +++ b/new-ui/projects/admin/src/views/NoPermission.vue @@ -0,0 +1,11 @@ + + + + + diff --git a/new-ui/projects/admin/src/views/Order/OrderContainer.vue b/new-ui/projects/admin/src/views/Order/OrderContainer.vue index 9757d3b5..9358450c 100644 --- a/new-ui/projects/admin/src/views/Order/OrderContainer.vue +++ b/new-ui/projects/admin/src/views/Order/OrderContainer.vue @@ -1,8 +1,9 @@ - diff --git a/new-ui/projects/admin/src/views/Order/api.ts b/new-ui/projects/admin/src/views/Order/api.ts index 6bb73492..45abec0d 100644 --- a/new-ui/projects/admin/src/views/Order/api.ts +++ b/new-ui/projects/admin/src/views/Order/api.ts @@ -6,4 +6,12 @@ export const getList = (data?: Record) => { method: "post", data }) +} + +export const remove = (data) => { + return http({ + url: "/api/admin/order/remove", + method: "post", + data + }) } \ No newline at end of file diff --git a/new-ui/projects/admin/src/views/SysPermission/SysPermissionContainer.vue b/new-ui/projects/admin/src/views/SysPermission/SysPermissionContainer.vue new file mode 100644 index 00000000..4724ebbc --- /dev/null +++ b/new-ui/projects/admin/src/views/SysPermission/SysPermissionContainer.vue @@ -0,0 +1,82 @@ + + diff --git a/new-ui/projects/admin/src/views/SysPermission/SysPermissionForm.vue b/new-ui/projects/admin/src/views/SysPermission/SysPermissionForm.vue new file mode 100644 index 00000000..60fdf480 --- /dev/null +++ b/new-ui/projects/admin/src/views/SysPermission/SysPermissionForm.vue @@ -0,0 +1,63 @@ + + + diff --git a/new-ui/projects/admin/src/views/SysPermission/api.ts b/new-ui/projects/admin/src/views/SysPermission/api.ts new file mode 100644 index 00000000..49ba6b6e --- /dev/null +++ b/new-ui/projects/admin/src/views/SysPermission/api.ts @@ -0,0 +1,25 @@ +import http from "@/http/config"; + +export const getList = (params) => { + return http({ + url: "/api/admin/sysPermission/list", + method: "get", + params + }) +} + +export const save = (data) => { + return http({ + url: "/api/admin/sysPermission/save", + method: "post", + data + }) +} + +export const remove = (data) => { + return http({ + url: "/api/admin/sysPermission/remove", + method: "post", + data + }) +} \ No newline at end of file