From 1b3271fa3534d24a02a0556a95e23b5b37a98954 Mon Sep 17 00:00:00 2001 From: JiaoTianBo Date: Fri, 27 Mar 2026 20:06:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(system):=20=E5=AE=8C=E5=96=84=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=AE=A1=E7=90=86=E6=8E=A5=E5=8F=A3=E5=8F=8A=E6=9D=83?= =?UTF-8?q?=E9=99=90=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 mock 中系统管理路由冗余数据,前端统一路由管理 - 飞书登录接口响应数据结构重构,新增登录状态及用户信息查询方法 - 重构系统接口模块,新增权限、角色、用户、部门、菜单等完整类型及API实现 - 新增项目初始化相关接口及数据类型支持 - 调整登录页及相关调用,适配飞书登录新接口返回 - 统一接口响应状态码判定,修正多处 code === 0 为 code === 200 - 新增系统管理路由模块,包含用户、角色、权限、菜单、部门页面 - 账号设置页面新增角色与权限展示,丰富用户信息显示 - 权限管理页面新增增删改查功能,提供表单校验与列表操作 - 优化权限表格展示和操作体验,支持批量删除及树形结构显示 - 兼容性优化,调整部门与菜单管理hook中code判断,避免误判 - 新增菜单和部门管理相关接口及类型定义 - 更新 OpenAPI 配置,规范角色菜单权限相关接口定义 - 新增权限管理表单组件及校验规则,支持增删改权限项 - 规范权限管理模块代码结构及变量命名,提高维护性 --- .gitignore | 6 +- mock/asyncRoutes.ts | 58 +-- src/api/feishu.ts | 60 ++- src/api/system.ts | 474 +++++++++++++++++- src/api/默认模块.openapi.json | 50 +- src/router/modules/system.ts | 64 +++ .../account-settings/components/Profile.vue | 56 ++- src/views/login/index.vue | 2 +- src/views/system/dept/utils/hook.tsx | 2 +- src/views/system/menu/utils/hook.tsx | 2 +- src/views/system/permission/form/index.vue | 122 +++++ src/views/system/permission/form/rules.ts | 13 + src/views/system/permission/index.vue | 200 ++++++++ src/views/system/permission/utils/hook.tsx | 292 +++++++++++ src/views/system/permission/utils/types.ts | 15 + src/views/system/role/form.vue | 43 +- src/views/system/role/index.vue | 26 +- src/views/system/role/utils/hook.tsx | 116 +++-- src/views/system/role/utils/rule.ts | 4 +- src/views/system/role/utils/types.ts | 18 +- src/views/system/user/utils/hook.tsx | 33 +- 21 files changed, 1432 insertions(+), 224 deletions(-) create mode 100644 src/router/modules/system.ts create mode 100644 src/views/system/permission/form/index.vue create mode 100644 src/views/system/permission/form/rules.ts create mode 100644 src/views/system/permission/index.vue create mode 100644 src/views/system/permission/utils/hook.tsx create mode 100644 src/views/system/permission/utils/types.ts diff --git a/.gitignore b/.gitignore index 423ed2b..37fe845 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,8 @@ tests/**/coverage/ *.ntvs* *.njsproj *.sln -tsconfig.tsbuildinfo \ No newline at end of file +tsconfig.tsbuildinfo + +#qoder + +**.qoder** \ No newline at end of file diff --git a/mock/asyncRoutes.ts b/mock/asyncRoutes.ts index 85ae65f..d952f66 100644 --- a/mock/asyncRoutes.ts +++ b/mock/asyncRoutes.ts @@ -1,60 +1,12 @@ // 模拟后端动态生成路由 import { defineFakeRoute } from "vite-plugin-fake-server/client"; -import { system, monitor, permission, frame, tabs } from "@/router/enums"; +import { monitor, permission, frame, tabs } from "@/router/enums"; /** * roles:页面级别权限,这里模拟二种 "admin"、"common" * admin:管理员角色 * common:普通角色 */ - -const systemManagementRouter = { - path: "/system", - meta: { - icon: "ri:settings-3-line", - title: "menus.pureSysManagement", - rank: system - }, - children: [ - { - path: "/system/user/index", - name: "SystemUser", - meta: { - icon: "ri:admin-line", - title: "menus.pureUser", - roles: ["admin"] - } - }, - { - path: "/system/role/index", - name: "SystemRole", - meta: { - icon: "ri:admin-fill", - title: "menus.pureRole", - roles: ["admin"] - } - }, - { - path: "/system/menu/index", - name: "SystemMenu", - meta: { - icon: "ep:menu", - title: "menus.pureSystemMenu", - roles: ["admin"] - } - }, - { - path: "/system/dept/index", - name: "SystemDept", - meta: { - icon: "ri:git-branch-line", - title: "menus.pureDept", - roles: ["admin"] - } - } - ] -}; - const systemMonitorRouter = { path: "/monitor", meta: { @@ -328,13 +280,7 @@ export default defineFakeRoute([ return { code: 0, message: "操作成功", - data: [ - systemManagementRouter, - systemMonitorRouter, - permissionRouter, - frameRouter, - tabsRouter - ] + data: [systemMonitorRouter, permissionRouter, frameRouter, tabsRouter] }; } } diff --git a/src/api/feishu.ts b/src/api/feishu.ts index 2456620..e11c9ea 100644 --- a/src/api/feishu.ts +++ b/src/api/feishu.ts @@ -1,28 +1,30 @@ import { http } from "@/utils/http"; +/** 飞书登录响应数据 */ +export type FeishuLoginData = { + /** 真实姓名 */ + realName: string; + /** 手机号 */ + phone: string; + /** token名称 */ + tokenName: string; + /** 头像 */ + avatar: string; + /** 用户ID */ + userId: number; + /** 邮箱 */ + email: string; + /** token */ + token: string; + /** 用户名 */ + username: string; +}; + +/** 飞书登录响应结果 */ export type FeishuLoginResult = { code: number; + data: FeishuLoginData; message: string; - data: { - /** 是否登录成功 */ - isLogin: boolean; - /** 用户ID */ - userId: string | number; - /** 用户名 */ - username: string; - /** 真实姓名 */ - realName: string; - /** 头像 */ - avatar: string; - /** 手机号 */ - phone: string; - /** 邮箱 */ - email: string; - /** token */ - token: string; - /** token名称 */ - tokenName: string; - }; }; /** 飞书OAuth登录接口(前端回调后调用) */ @@ -31,3 +33,21 @@ export const getFeishuLogin = (code: string) => { params: { code } }); }; + +/** 检查当前登录状态 */ +export const checkFeishuLoginStatus = () => { + return http.request<{ + code: number; + data: Record; + message: string; + }>("get", "/api/v1/auth/feishu/check"); +}; + +/** 获取当前登录用户信息 */ +export const getFeishuUserInfo = () => { + return http.request<{ + code: number; + data: Record; + message: string; + }>("get", "/api/v1/auth/feishu/user/info"); +}; diff --git a/src/api/system.ts b/src/api/system.ts index da09f71..a890637 100644 --- a/src/api/system.ts +++ b/src/api/system.ts @@ -1,17 +1,19 @@ import { http } from "@/utils/http"; -type Result = { +/** 通用响应结果 */ +type Result = { code: number; message: string; - data?: Array; + data?: T; }; -type ResultTable = { +/** 分页响应结果 */ +type ResultTable = { code: number; message: string; data?: { /** 列表数据 */ - list: Array; + list: Array; /** 总条目数 */ total?: number; /** 每页显示条目个数 */ @@ -21,34 +23,444 @@ type ResultTable = { }; }; -/** 获取系统管理-用户管理列表 */ -export const getUserList = (data?: object) => { - return http.request("post", "/user", { data }); +/** 后端分页数据结构 */ +type PageResult = { + records: Array; + total: number; + size: number; + current: number; + pages?: number; }; -/** 系统管理-用户管理-获取所有角色列表 */ +/** 后端分页响应 */ +type BasePageResponse = { + code: number; + message: string; + data: PageResult; +}; + +// ==================== 权限管理 ==================== + +/** 权限对象 */ +export type SysPermission = { + id?: number; + parentId?: number; + permissionCode?: string; + permissionName?: string; + permissionType?: number; + path?: string; + component?: string; + icon?: string; + apiUrl?: string; + apiMethod?: string; + sortOrder?: number; + visible?: number; + status?: number; + createTime?: string; + updateTime?: string; + deleted?: number; +}; + +/** 分页查询权限列表 */ +export const getPermissionList = (params?: { + pageNum?: number; + pageSize?: number; + keyword?: string; +}) => { + return http.request>( + "get", + "/api/v1/system/permission/list", + { params } + ); +}; + +/** 查询权限树 */ +export const getPermissionTree = () => { + return http.request>( + "get", + "/api/v1/system/permission/tree" + ); +}; + +/** 根据ID查询权限 */ +export const getPermissionById = (id: number) => { + return http.request>( + "get", + `/api/v1/system/permission/${id}` + ); +}; + +/** 新增权限 */ +export const addPermission = (data: SysPermission) => { + return http.request>("post", "/api/v1/system/permission", { + data + }); +}; + +/** 修改权限 */ +export const updatePermission = (data: SysPermission) => { + return http.request>("put", "/api/v1/system/permission", { + data + }); +}; + +/** 删除权限 */ +export const deletePermission = (id: number) => { + return http.request>( + "delete", + `/api/v1/system/permission/${id}` + ); +}; + +// ==================== 角色管理 ==================== + +/** 角色对象 */ +export type SysRole = { + id?: number; + roleCode?: string; + roleName?: string; + roleType?: string; + description?: string; + dataScope?: number; + sortOrder?: number; + status?: number; + createBy?: number; + createTime?: string; + updateBy?: number; + updateTime?: string; + deleted?: number; +}; + +/** 分页查询角色列表 */ +export const getRoleList = (params?: { + pageNum?: number; + pageSize?: number; + keyword?: string; +}) => { + return http.request>( + "get", + "/api/v1/system/role/list", + { params } + ); +}; + +/** 查询所有角色(用于下拉选择) */ export const getAllRoleList = () => { - return http.request("get", "/list-all-role"); + return http.request>("get", "/api/v1/system/role/all"); }; -/** 系统管理-用户管理-根据userId,获取对应角色id列表(userId:用户id) */ -export const getRoleIds = (data?: object) => { - return http.request("post", "/list-role-ids", { data }); +/** 根据ID查询角色 */ +export const getRoleById = (id: number) => { + return http.request>("get", `/api/v1/system/role/${id}`); }; -/** 获取系统管理-角色管理列表 */ -export const getRoleList = (data?: object) => { - return http.request("post", "/role", { data }); +/** 新增角色 */ +export const addRole = (data: SysRole) => { + return http.request>("post", "/api/v1/system/role", { data }); }; -/** 获取系统管理-菜单管理列表 */ -export const getMenuList = (data?: object) => { - return http.request("post", "/menu", { data }); +/** 修改角色 */ +export const updateRole = (data: SysRole) => { + return http.request>("put", "/api/v1/system/role", { data }); }; -/** 获取系统管理-部门管理列表 */ -export const getDeptList = (data?: object) => { - return http.request("post", "/dept", { data }); +/** 删除角色 */ +export const deleteRole = (id: number) => { + return http.request>("delete", `/api/v1/system/role/${id}`); +}; + +/** 查询角色的权限列表(返回完整权限对象列表) */ +export const getRolePermissions = (id: number) => { + return http.request>( + "get", + `/api/v1/system/role/${id}/permissions` + ); +}; + +/** 为角色分配权限 */ +export const assignRolePermissions = (id: number, permissionIds: number[]) => { + return http.request>( + "post", + `/api/v1/system/role/${id}/permissions`, + { data: permissionIds } + ); +}; + +// ==================== 用户管理 ==================== + +/** 用户对象 */ +export type SysUser = { + id?: number; + username?: string; + password?: string; + realName?: string; + nickname?: string; + avatar?: string; + gender?: number; + phone?: string; + email?: string; + deptId?: number; + position?: string; + employeeNo?: string; + entryDate?: string; + status?: number; + lastLoginTime?: string; + lastLoginIp?: string; + createBy?: number; + createTime?: string; + updateBy?: number; + updateTime?: string; + deleted?: number; +}; + +/** 分页查询用户列表 */ +export const getUserList = (params?: { + pageNum?: number; + pageSize?: number; + keyword?: string; +}) => { + return http.request>( + "get", + "/api/v1/system/user/list", + { params } + ); +}; + +/** 根据ID查询用户 */ +export const getUserById = (id: number) => { + return http.request>("get", `/api/v1/system/user/${id}`); +}; + +/** 新增用户 */ +export const addUser = (data: SysUser) => { + return http.request>("post", "/api/v1/system/user", { data }); +}; + +/** 修改用户 */ +export const updateUser = (data: SysUser) => { + return http.request>("put", "/api/v1/system/user", { data }); +}; + +/** 删除用户 */ +export const deleteUser = (id: number) => { + return http.request>("delete", `/api/v1/system/user/${id}`); +}; + +/** 查询用户的角色列表 */ +export const getUserRoles = (id: number) => { + return http.request>( + "get", + `/api/v1/system/user/${id}/roles` + ); +}; + +/** 为用户绑定角色 */ +export const assignUserRoles = (id: number, roleIds: number[]) => { + return http.request>("post", `/api/v1/system/user/${id}/roles`, { + data: roleIds + }); +}; + +/** 查询用户角色ID列表(用于回显) */ +export const getUserRoleIds = (id: number) => { + return http.request>( + "get", + `/api/v1/system/user/${id}/roleIds` + ); +}; + +// ==================== 项目初始化 ==================== + +/** 项目信息 */ +export type ProjectInfo = { + project_name?: string; + project_type?: string; + description?: string; + objectives?: string; + plan_start_date?: string; + plan_end_date?: string; + budget?: number; + currency?: string; + priority?: string; + tags?: string[]; +}; + +/** 里程碑信息 */ +export type MilestoneInfo = { + milestone_name?: string; + description?: string; + plan_date?: string; + deliverables?: string; + owner_role?: string; +}; + +/** 任务信息 */ +export type TaskInfo = { + task_id?: string; + task_name?: string; + parent_task_id?: string; + description?: string; + plan_start_date?: string; + plan_end_date?: string; + estimated_hours?: number; + priority?: string; + assignee_role?: string; + dependencies?: string[]; + deliverables?: string; +}; + +/** 成员信息 */ +export type MemberInfo = { + name?: string; + role_code?: string; + responsibility?: string; + department?: string; + weekly_hours?: number; +}; + +/** 资源信息 */ +export type ResourceInfo = { + resource_name?: string; + resource_type?: string; + quantity?: number; + unit?: string; + unit_price?: number; + supplier?: string; +}; + +/** 风险信息 */ +export type RiskInfo = { + risk_name?: string; + category?: string; + description?: string; + probability?: number; + impact?: number; + mitigation_plan?: string; +}; + +/** 时间节点信息 */ +export type TimelineNodeInfo = { + node_name?: string; + node_type?: string; + plan_date?: string; + description?: string; + kb_scope?: string[]; +}; + +/** 项目初始化结果 */ +export type ProjectInitResult = { + project: ProjectInfo; + milestones: MilestoneInfo[]; + tasks: TaskInfo[]; + members: MemberInfo[]; + resources: ResourceInfo[]; + risks: RiskInfo[]; + timeline_nodes: TimelineNodeInfo[]; +}; + +/** 上传文件并生成项目初始化预览数据 */ +export const previewProjectInit = (file: File) => { + const formData = new FormData(); + formData.append("file", file); + return http.request>( + "post", + "/api/v1/project-init/preview", + { data: formData } + ); +}; + +/** 确认并保存项目初始化数据 */ +export const confirmProjectInit = (data: ProjectInitResult) => { + return http.request>( + "post", + "/api/v1/project-init/confirm", + { data } + ); +}; + +// ==================== 部门管理 ==================== + +/** 部门对象 */ +export type SysDept = { + id?: number; + parentId?: number; + deptName?: string; + deptCode?: string; + leader?: string; + phone?: string; + email?: string; + sortOrder?: number; + status?: number; + createTime?: string; +}; + +/** 获取部门列表(树形结构) */ +export const getDeptList = () => { + return http.request>("get", "/api/v1/system/dept/tree"); +}; + +/** 获取部门详情 */ +export const getDeptById = (id: number) => { + return http.request>("get", `/api/v1/system/dept/${id}`); +}; + +/** 新增部门 */ +export const addDept = (data: SysDept) => { + return http.request>("post", "/api/v1/system/dept", { data }); +}; + +/** 修改部门 */ +export const updateDept = (data: SysDept) => { + return http.request>("put", "/api/v1/system/dept", { data }); +}; + +/** 删除部门 */ +export const deleteDept = (id: number) => { + return http.request>("delete", `/api/v1/system/dept/${id}`); +}; + +// ==================== 菜单管理 ==================== + +/** 菜单对象 */ +export type SysMenu = { + id?: number; + parentId?: number; + menuName?: string; + menuType?: number; + icon?: string; + path?: string; + component?: string; + permission?: string; + sortOrder?: number; + status?: number; + visible?: number; + keepAlive?: number; + createTime?: string; +}; + +/** 获取菜单列表(树形结构) */ +export const getMenuList = () => { + return http.request>("get", "/api/v1/system/menu/tree"); +}; + +/** 获取菜单详情 */ +export const getMenuById = (id: number) => { + return http.request>("get", `/api/v1/system/menu/${id}`); +}; + +/** 新增菜单 */ +export const addMenu = (data: SysMenu) => { + return http.request>("post", "/api/v1/system/menu", { data }); +}; + +/** 修改菜单 */ +export const updateMenu = (data: SysMenu) => { + return http.request>("put", "/api/v1/system/menu", { data }); +}; + +/** 删除菜单 */ +export const deleteMenu = (id: number) => { + return http.request>("delete", `/api/v1/system/menu/${id}`); }; /** 获取系统监控-在线用户列表 */ @@ -77,11 +489,23 @@ export const getSystemLogsDetail = (data?: object) => { }; /** 获取角色管理-权限-菜单权限 */ -export const getRoleMenu = (data?: object) => { - return http.request("post", "/role-menu", { data }); +export const getRoleMenu = () => { + return http.request>("get", "/api/v1/system/menu/tree"); }; /** 获取角色管理-权限-菜单权限-根据角色 id 查对应菜单 */ -export const getRoleMenuIds = (data?: object) => { - return http.request("post", "/role-menu-ids", { data }); +export const getRoleMenuIds = (roleId: number) => { + return http.request>( + "get", + `/api/v1/system/role/${roleId}/menu-ids` + ); +}; + +/** 为角色分配菜单权限 */ +export const assignRoleMenus = (roleId: number, menuIds: number[]) => { + return http.request>( + "post", + `/api/v1/system/role/${roleId}/menus`, + { data: menuIds } + ); }; diff --git a/src/api/默认模块.openapi.json b/src/api/默认模块.openapi.json index 2084c28..b3956c3 100644 --- a/src/api/默认模块.openapi.json +++ b/src/api/默认模块.openapi.json @@ -7,20 +7,30 @@ }, "tags": [], "paths": { - "/api/v1/auth/feishu/login": { - "post": { - "summary": "飞书OAuth登录接口(前端回调后调用)", + "/api/v1/system/role/{id}/menu-ids": { + "get": { + "summary": "查询角色的菜单权限ID列表(只返回菜单类型的权限)", "deprecated": false, - "description": "前端从飞书回调中获取code,然后调用此接口完成登录", + "description": "", "tags": [], "parameters": [ { - "name": "code", - "in": "query", - "description": "飞书授权码", + "name": "id", + "in": "path", + "description": "", "required": true, "schema": { - "type": "string" + "type": "integer" + } + }, + { + "name": "Authorization", + "in": "header", + "description": "", + "example": "Bearer 000b7e25-53b2-42a3-a39b-9f4cb03644ba", + "schema": { + "type": "string", + "default": "Bearer 000b7e25-53b2-42a3-a39b-9f4cb03644ba" } } ], @@ -30,8 +40,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BaseResponseMapObject", - "description": "登录结果(包含token和用户信息)" + "$ref": "#/components/schemas/BaseResponseListLong" } } } @@ -43,7 +52,7 @@ }, "components": { "schemas": { - "BaseResponseMapObject": { + "BaseResponseListLong": { "type": "object", "properties": { "code": { @@ -51,26 +60,17 @@ "description": "" }, "data": { - "type": "object", - "properties": { - "isLogin": { - "type": "boolean" - }, - "userId": { - "$ref": "#/components/schemas/userId" - } + "type": "array", + "items": { + "type": "integer" }, "description": "" }, "message": { - "description": "", - "type": "null" + "type": "string", + "description": "" } } - }, - "userId": { - "type": "object", - "properties": {} } }, "responses": {}, diff --git a/src/router/modules/system.ts b/src/router/modules/system.ts new file mode 100644 index 0000000..9fcd66b --- /dev/null +++ b/src/router/modules/system.ts @@ -0,0 +1,64 @@ +import { $t } from "@/plugins/i18n"; +import { system } from "@/router/enums"; + +export default { + path: "/system", + redirect: "/system/user", + meta: { + icon: "ri:settings-3-line", + title: $t("menus.pureSysManagement"), + rank: system + }, + children: [ + { + path: "/system/user", + name: "SystemUser", + component: () => import("@/views/system/user/index.vue"), + meta: { + icon: "ri:admin-line", + title: $t("menus.pureUser"), + roles: ["admin"] + } + }, + { + path: "/system/role", + name: "SystemRole", + component: () => import("@/views/system/role/index.vue"), + meta: { + icon: "ri:admin-fill", + title: $t("menus.pureRole"), + roles: ["admin"] + } + }, + { + path: "/system/permission", + name: "SystemPermission", + component: () => import("@/views/system/permission/index.vue"), + meta: { + icon: "ep:key", + title: "权限管理", + roles: ["admin"] + } + }, + { + path: "/system/menu", + name: "SystemMenu", + component: () => import("@/views/system/menu/index.vue"), + meta: { + icon: "ep:menu", + title: $t("menus.pureSystemMenu"), + roles: ["admin"] + } + }, + { + path: "/system/dept", + name: "SystemDept", + component: () => import("@/views/system/dept/index.vue"), + meta: { + icon: "ri:git-branch-line", + title: $t("menus.pureDept"), + roles: ["admin"] + } + } + ] +} satisfies RouteConfigsTable; diff --git a/src/views/account-settings/components/Profile.vue b/src/views/account-settings/components/Profile.vue index b926e90..f40fb5c 100644 --- a/src/views/account-settings/components/Profile.vue +++ b/src/views/account-settings/components/Profile.vue @@ -24,7 +24,15 @@ const userInfos = reactive({ nickname: "", email: "", phone: "", - description: "" + description: "", + realName: "", + username: "", + gender: 0, + position: "", + employeeNo: "", + deptName: "", + roles: [] as string[], + permissions: [] as string[] }); const rules = reactive>({ @@ -74,7 +82,7 @@ const handleSubmitImage = () => { }); formUpload(formData) .then(({ code }) => { - if (code === 0) { + if (code === 200) { message("更新头像成功", { type: "success" }); handleClose(); } else { @@ -100,7 +108,7 @@ const onSubmit = async (formEl: FormInstance) => { onMounted(async () => { const { code, data } = await getMine(); - if (code === 0) { + if (code === 200) { Object.assign(userInfos, data); } }); @@ -162,6 +170,48 @@ onMounted(async () => { show-word-limit /> + + + +

角色与权限

+ + + + + + + + + + + + {{ role }} + + 暂无角色 + + +
+ + {{ perm }} + + 暂无权限 +
+
+ 更新信息 diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 8fc7575..891d962 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -144,7 +144,7 @@ const handleFeishuCallback = async (code: string) => { feishuLoading.value = true; try { const res = await getFeishuLogin(code); - if (res.code === 0 && res.data?.isLogin) { + if (res.code === 200 && res.data?.token) { const { data } = res; // 设置token(适配后端返回的字段名) setToken({ diff --git a/src/views/system/dept/utils/hook.tsx b/src/views/system/dept/utils/hook.tsx index 830b1bf..f015232 100644 --- a/src/views/system/dept/utils/hook.tsx +++ b/src/views/system/dept/utils/hook.tsx @@ -75,7 +75,7 @@ export function useDept() { async function onSearch() { loading.value = true; const { code, data } = await getDeptList(); // 这里是返回一维数组结构,前端自行处理成树结构,返回格式要求:唯一id加父节点parentId,parentId取父节点id - if (code === 0) { + if (code === 200) { let newData = data; if (!isAllEmpty(form.name)) { // 前端搜索部门名称 diff --git a/src/views/system/menu/utils/hook.tsx b/src/views/system/menu/utils/hook.tsx index 6c031f9..3ca1872 100644 --- a/src/views/system/menu/utils/hook.tsx +++ b/src/views/system/menu/utils/hook.tsx @@ -107,7 +107,7 @@ export function useMenu() { async function onSearch() { loading.value = true; const { code, data } = await getMenuList(); // 这里是返回一维数组结构,前端自行处理成树结构,返回格式要求:唯一id加父节点parentId,parentId取父节点id - if (code === 0) { + if (code === 200) { let newData = data; if (!isAllEmpty(form.title)) { // 前端搜索菜单名称 diff --git a/src/views/system/permission/form/index.vue b/src/views/system/permission/form/index.vue new file mode 100644 index 0000000..4ce7eb0 --- /dev/null +++ b/src/views/system/permission/form/index.vue @@ -0,0 +1,122 @@ + + + diff --git a/src/views/system/permission/form/rules.ts b/src/views/system/permission/form/rules.ts new file mode 100644 index 0000000..3576fd1 --- /dev/null +++ b/src/views/system/permission/form/rules.ts @@ -0,0 +1,13 @@ +import type { FormRules } from "element-plus"; + +export const formRules: FormRules = { + permissionName: [ + { required: true, message: "权限名称为必填项", trigger: "blur" } + ], + permissionCode: [ + { required: true, message: "权限编码为必填项", trigger: "blur" } + ], + permissionType: [ + { required: true, message: "权限类型为必填项", trigger: "change" } + ] +}; diff --git a/src/views/system/permission/index.vue b/src/views/system/permission/index.vue new file mode 100644 index 0000000..a79f823 --- /dev/null +++ b/src/views/system/permission/index.vue @@ -0,0 +1,200 @@ + + + + + diff --git a/src/views/system/permission/utils/hook.tsx b/src/views/system/permission/utils/hook.tsx new file mode 100644 index 0000000..17c2c60 --- /dev/null +++ b/src/views/system/permission/utils/hook.tsx @@ -0,0 +1,292 @@ +import dayjs from "dayjs"; +import { message } from "@/utils/message"; +import { addDialog } from "@/components/ReDialog"; +import type { PaginationProps } from "@pureadmin/table"; +import type { FormItemProps } from "./types"; +import editForm from "../form/index.vue"; +import { getPermissionTree } from "@/api/system"; +import { type Ref, h, ref, computed, reactive, onMounted } from "vue"; +import { getKeyList, deviceDetection } from "@pureadmin/utils"; +import { ElMessageBox } from "element-plus"; + +export function usePermission(tableRef: Ref) { + const form = reactive({ + permissionName: "", + permissionCode: "", + status: undefined as number | undefined + }); + const formRef = ref(); + const dataList = ref([]); + const loading = ref(true); + const selectedNum = ref(0); + const switchLoadMap = ref({}); + + const pagination = reactive({ + total: 0, + pageSize: 10, + currentPage: 1, + background: true + }); + + const columns: TableColumnList = [ + { + label: "勾选列", + type: "selection", + fixed: "left", + reserveSelection: true + }, + { + label: "权限编号", + prop: "id", + width: 90 + }, + { + label: "权限名称", + prop: "permissionName", + minWidth: 150, + align: "left", + cellRenderer: ({ row }) => ( + + {row.permissionName} + + ) + }, + { + label: "权限编码", + prop: "permissionCode", + minWidth: 150 + }, + { + label: "权限类型", + prop: "permissionType", + minWidth: 100, + cellRenderer: ({ row }) => { + const typeMap = { + 1: { text: "菜单", type: "primary" }, + 2: { text: "按钮", type: "warning" }, + 3: { text: "接口", type: "success" } + }; + const typeInfo = typeMap[row.permissionType] || { + text: "未知", + type: "info" + }; + return ( + + {typeInfo.text} + + ); + } + }, + { + label: "路径", + prop: "path", + minWidth: 150 + }, + { + label: "排序", + prop: "sortOrder", + width: 80 + }, + { + label: "状态", + prop: "status", + minWidth: 90, + cellRenderer: scope => ( + onChange(scope as any)} + /> + ) + }, + { + label: "创建时间", + minWidth: 120, + prop: "createTime", + formatter: ({ createTime }) => + createTime ? dayjs(createTime).format("YYYY-MM-DD HH:mm:ss") : "-" + }, + { + label: "操作", + fixed: "right", + width: 180, + slot: "operation" + } + ]; + + const buttonClass = computed(() => { + return [ + "h-5!", + "reset-margin", + "text-gray-500!", + "dark:text-white!", + "dark:hover:text-primary!" + ]; + }); + + function onChange({ row, index }) { + const action = row.status === 0 ? "停用" : "启用"; + ElMessageBox.confirm( + `确认要${action}${row.permissionName}权限吗?`, + "系统提示", + { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning", + dangerouslyUseHTMLString: true, + draggable: true + } + ) + .then(() => { + switchLoadMap.value[index] = { loading: true }; + setTimeout(() => { + switchLoadMap.value[index] = { loading: false }; + message(`已成功${action}权限`, { type: "success" }); + }, 300); + }) + .catch(() => { + row.status = row.status === 0 ? 1 : 0; + }); + } + + function handleDelete(row) { + message(`您删除了权限编号为${row.id}的这条数据`, { type: "success" }); + onSearch(); + } + + function handleSizeChange(val: number) { + console.log(`${val} items per page`); + } + + function handleCurrentChange(val: number) { + console.log(`current page: ${val}`); + } + + function handleSelectionChange(val) { + selectedNum.value = val.length; + tableRef.value?.setAdaptive(); + } + + function onSelectionCancel() { + selectedNum.value = 0; + tableRef.value?.getTableRef().clearSelection(); + } + + function onbatchDel() { + const curSelected = tableRef.value?.getTableRef().getSelectionRows(); + message(`已删除权限编号为 ${getKeyList(curSelected, "id")} 的数据`, { + type: "success" + }); + tableRef.value?.getTableRef().clearSelection(); + onSearch(); + } + + async function onSearch() { + loading.value = true; + try { + const { code, data } = await getPermissionTree(); + if (code === 200) { + dataList.value = data || []; + pagination.total = data?.length || 0; + } + } catch (error) { + console.error(error); + } finally { + setTimeout(() => { + loading.value = false; + }, 500); + } + } + + const resetForm = formEl => { + if (!formEl) return; + formEl.resetFields(); + onSearch(); + }; + + function openDialog(title = "新增", row?: FormItemProps) { + addDialog({ + title: `${title}权限`, + props: { + formInline: { + title, + id: row?.id ?? "", + parentId: row?.parentId ?? 0, + permissionName: row?.permissionName ?? "", + permissionCode: row?.permissionCode ?? "", + permissionType: row?.permissionType ?? 1, + path: row?.path ?? "", + component: row?.component ?? "", + icon: row?.icon ?? "", + apiUrl: row?.apiUrl ?? "", + apiMethod: row?.apiMethod ?? "", + sortOrder: row?.sortOrder ?? 0, + visible: row?.visible ?? 1, + status: row?.status ?? 1 + } + }, + width: "46%", + draggable: true, + fullscreen: deviceDetection(), + fullscreenIcon: true, + closeOnClickModal: false, + contentRenderer: () => h(editForm, { ref: formRef }), + beforeSure: (done, { options }) => { + const FormRef = formRef.value?.getRef(); + const curData = options.props.formInline as FormItemProps; + function chores() { + message(`您${title}了权限名称为${curData.permissionName}的这条数据`, { + type: "success" + }); + done(); + onSearch(); + } + FormRef?.validate(valid => { + if (valid) { + console.log("curData", curData); + if (title === "新增") { + chores(); + } else { + chores(); + } + } + }); + } + }); + } + + function handleUpdate(row) { + console.log(row); + } + + onMounted(() => { + onSearch(); + }); + + return { + form, + loading, + columns, + dataList, + selectedNum, + pagination, + buttonClass, + deviceDetection, + onSearch, + resetForm, + onbatchDel, + openDialog, + handleUpdate, + handleDelete, + handleSizeChange, + onSelectionCancel, + handleCurrentChange, + handleSelectionChange + }; +} diff --git a/src/views/system/permission/utils/types.ts b/src/views/system/permission/utils/types.ts new file mode 100644 index 0000000..a89ed06 --- /dev/null +++ b/src/views/system/permission/utils/types.ts @@ -0,0 +1,15 @@ +export interface FormItemProps { + id?: number; + parentId?: number; + permissionName?: string; + permissionCode?: string; + permissionType?: number; + path?: string; + component?: string; + icon?: string; + apiUrl?: string; + apiMethod?: string; + sortOrder?: number; + visible?: number; + status?: number; +} diff --git a/src/views/system/role/form.vue b/src/views/system/role/form.vue index 65d4ef0..8623377 100644 --- a/src/views/system/role/form.vue +++ b/src/views/system/role/form.vue @@ -5,9 +5,13 @@ import { FormProps } from "./utils/types"; const props = withDefaults(defineProps(), { formInline: () => ({ - name: "", - code: "", - remark: "" + roleName: "", + roleCode: "", + roleType: "custom", + description: "", + dataScope: 1, + sortOrder: 0, + status: 1 }) }); @@ -28,26 +32,43 @@ defineExpose({ getRef }); :rules="formRules" label-width="82px" > - + - + - + + + 系统角色 + 自定义角色 + + + + + + + + + + + + + + diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue index 3c7e159..631d5b1 100644 --- a/src/views/system/role/index.vue +++ b/src/views/system/role/index.vue @@ -95,18 +95,18 @@ onMounted(() => { :model="form" class="search-form bg-bg_color w-full pl-8 pt-3 overflow-auto" > - + - + @@ -144,7 +144,7 @@ onMounted(() => { @@ -191,7 +191,7 @@ onMounted(() => { 修改