refactor: 分页响应数据结构调整
This commit is contained in:
120
mock/ai.mock.ts
120
mock/ai.mock.ts
@@ -1,120 +0,0 @@
|
||||
import { defineMock } from "./base";
|
||||
|
||||
export default defineMock([
|
||||
{
|
||||
url: "ai/assistant/parse",
|
||||
method: ["POST"],
|
||||
body: ({ body }) => {
|
||||
return {
|
||||
code: "00000",
|
||||
data: {
|
||||
parseLogId: "10001",
|
||||
success: true,
|
||||
functionCalls: [
|
||||
{
|
||||
name: "navigate",
|
||||
arguments: {
|
||||
path: "/system/user",
|
||||
},
|
||||
},
|
||||
],
|
||||
explanation: `Mock: 已解析命令:${body?.command ?? ""}`,
|
||||
confidence: 0.92,
|
||||
},
|
||||
msg: "一切ok",
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
url: "ai/assistant/execute",
|
||||
method: ["POST"],
|
||||
body: {
|
||||
code: "00000",
|
||||
data: {
|
||||
success: true,
|
||||
message: "Mock: 执行成功",
|
||||
},
|
||||
msg: "一切ok",
|
||||
},
|
||||
},
|
||||
{
|
||||
url: "ai/assistant/records",
|
||||
method: ["GET"],
|
||||
body: ({ query }) => {
|
||||
const pageNum = Number(query?.pageNum ?? 1);
|
||||
const pageSize = Number(query?.pageSize ?? 10);
|
||||
const total = 2;
|
||||
|
||||
return {
|
||||
code: "00000",
|
||||
data: [
|
||||
{
|
||||
id: "10001",
|
||||
userId: 1,
|
||||
username: "admin",
|
||||
originalCommand: "跳转到用户管理",
|
||||
aiProvider: "qwen",
|
||||
aiModel: "qwen-plus",
|
||||
parseStatus: 1,
|
||||
functionCalls: JSON.stringify(
|
||||
[
|
||||
{
|
||||
name: "navigate",
|
||||
arguments: { path: "/system/user" },
|
||||
},
|
||||
],
|
||||
null,
|
||||
0
|
||||
),
|
||||
explanation: "Mock: 识别到跳转用户管理",
|
||||
confidence: 0.92,
|
||||
parseDurationMs: 128,
|
||||
functionName: "navigate",
|
||||
functionArguments: JSON.stringify({ path: "/system/user" }),
|
||||
executeStatus: 1,
|
||||
ipAddress: "127.0.0.1",
|
||||
createTime: "2025-12-17 15:00:00",
|
||||
updateTime: "2025-12-17 15:00:00",
|
||||
},
|
||||
{
|
||||
id: "10002",
|
||||
userId: 1,
|
||||
username: "admin",
|
||||
originalCommand: "获取姓名为张三的用户信息",
|
||||
aiProvider: "qwen",
|
||||
aiModel: "qwen-plus",
|
||||
parseStatus: 0,
|
||||
functionCalls: "[]",
|
||||
explanation: "Mock: 解析失败示例",
|
||||
confidence: 0.2,
|
||||
parseErrorMessage: "Mock: 无法匹配函数",
|
||||
parseDurationMs: 256,
|
||||
executeStatus: 0,
|
||||
ipAddress: "127.0.0.1",
|
||||
createTime: "2025-12-17 15:01:00",
|
||||
updateTime: "2025-12-17 15:01:00",
|
||||
},
|
||||
].slice((pageNum - 1) * pageSize, pageNum * pageSize),
|
||||
page: {
|
||||
pageNum,
|
||||
pageSize,
|
||||
total,
|
||||
},
|
||||
msg: "一切ok",
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
url: "ai/assistant/records/:ids",
|
||||
method: ["DELETE"],
|
||||
body: ({ params }) => {
|
||||
return {
|
||||
code: "00000",
|
||||
data: {
|
||||
ids: params?.ids,
|
||||
},
|
||||
msg: "一切ok",
|
||||
};
|
||||
},
|
||||
},
|
||||
]);
|
||||
@@ -6,17 +6,15 @@ export default defineMock([
|
||||
method: ["GET"],
|
||||
body: {
|
||||
code: "00000",
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
name: "性别",
|
||||
dictCode: "gender",
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
page: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
data: {
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
name: "性别",
|
||||
dictCode: "gender",
|
||||
status: 1,
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
},
|
||||
msg: "一切ok",
|
||||
@@ -103,38 +101,36 @@ export default defineMock([
|
||||
method: ["GET"],
|
||||
body: {
|
||||
code: "00000",
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
dictCode: "gender",
|
||||
label: "男",
|
||||
value: "1",
|
||||
sort: 1,
|
||||
status: 1,
|
||||
tagType: "P",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
dictCode: "gender",
|
||||
label: "女",
|
||||
value: "2",
|
||||
sort: 2,
|
||||
status: 1,
|
||||
tagType: "D",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
dictCode: "gender",
|
||||
label: "保密",
|
||||
value: "0",
|
||||
sort: 3,
|
||||
status: 1,
|
||||
tagType: "I",
|
||||
},
|
||||
],
|
||||
page: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
data: {
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
dictCode: "gender",
|
||||
label: "男",
|
||||
value: "1",
|
||||
sort: 1,
|
||||
status: 1,
|
||||
tagType: "P",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
dictCode: "gender",
|
||||
label: "女",
|
||||
value: "2",
|
||||
sort: 2,
|
||||
status: 1,
|
||||
tagType: "D",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
dictCode: "gender",
|
||||
label: "保密",
|
||||
value: "0",
|
||||
sort: 3,
|
||||
status: 1,
|
||||
tagType: "I",
|
||||
},
|
||||
],
|
||||
total: 3,
|
||||
},
|
||||
msg: "一切ok",
|
||||
|
||||
308
mock/log.mock.ts
308
mock/log.mock.ts
@@ -6,161 +6,159 @@ export default defineMock([
|
||||
method: ["GET"],
|
||||
body: {
|
||||
code: "00000",
|
||||
data: [
|
||||
{
|
||||
id: 36192,
|
||||
module: "菜单",
|
||||
content: "菜单列表",
|
||||
requestUri: "/api/v1/menus",
|
||||
method: null,
|
||||
ip: "183.156.148.241",
|
||||
region: "浙江省 杭州市",
|
||||
browser: "Chrome 109.0.0.0",
|
||||
os: "OSX",
|
||||
executionTime: 5,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:47",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36190,
|
||||
module: "字典",
|
||||
content: "字典分页列表",
|
||||
requestUri: "/api/v1/dicts",
|
||||
method: null,
|
||||
ip: "183.156.148.241",
|
||||
region: "浙江省 杭州市",
|
||||
browser: "Chrome 109.0.0.0",
|
||||
os: "OSX",
|
||||
executionTime: 9,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:45",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36193,
|
||||
module: "部门",
|
||||
content: "部门列表",
|
||||
requestUri: "/api/v1/depts",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 27,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:45",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36191,
|
||||
module: "菜单",
|
||||
content: "菜单列表",
|
||||
requestUri: "/api/v1/menus",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 39,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:44",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36189,
|
||||
module: "角色",
|
||||
content: "角色分页列表",
|
||||
requestUri: "/api/v1/roles",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 55,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:43",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36188,
|
||||
module: "用户",
|
||||
content: "用户分页列表",
|
||||
requestUri: "/api/v1/users",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 92,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:42",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36187,
|
||||
module: "登录",
|
||||
content: "登录",
|
||||
requestUri: "/api/v1/auth/login",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 19340,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:09",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36186,
|
||||
module: "登录",
|
||||
content: "登录",
|
||||
requestUri: "/api/v1/auth/login",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 19869,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:37:59",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36185,
|
||||
module: "登录",
|
||||
content: "登录",
|
||||
requestUri: "/api/v1/auth/login",
|
||||
method: null,
|
||||
ip: "112.103.111.59",
|
||||
region: "黑龙江省 哈尔滨市",
|
||||
browser: "Chrome 97.0.4692.98",
|
||||
os: "Android",
|
||||
executionTime: 96,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:37:21",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36184,
|
||||
module: "登录",
|
||||
content: "登录",
|
||||
requestUri: "/api/v1/auth/login",
|
||||
method: null,
|
||||
ip: "114.86.204.190",
|
||||
region: "上海 上海市",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 89,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:29:37",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
],
|
||||
page: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
data: {
|
||||
list: [
|
||||
{
|
||||
id: 36192,
|
||||
module: "菜单",
|
||||
content: "菜单列表",
|
||||
requestUri: "/api/v1/menus",
|
||||
method: null,
|
||||
ip: "183.156.148.241",
|
||||
region: "浙江省 杭州市",
|
||||
browser: "Chrome 109.0.0.0",
|
||||
os: "OSX",
|
||||
executionTime: 5,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:47",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36190,
|
||||
module: "字典",
|
||||
content: "字典分页列表",
|
||||
requestUri: "/api/v1/dicts",
|
||||
method: null,
|
||||
ip: "183.156.148.241",
|
||||
region: "浙江省 杭州市",
|
||||
browser: "Chrome 109.0.0.0",
|
||||
os: "OSX",
|
||||
executionTime: 9,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:45",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36193,
|
||||
module: "部门",
|
||||
content: "部门列表",
|
||||
requestUri: "/api/v1/depts",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 27,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:45",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36191,
|
||||
module: "菜单",
|
||||
content: "菜单列表",
|
||||
requestUri: "/api/v1/menus",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 39,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:44",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36189,
|
||||
module: "角色",
|
||||
content: "角色分页列表",
|
||||
requestUri: "/api/v1/roles",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 55,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:43",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36188,
|
||||
module: "用户",
|
||||
content: "用户分页列表",
|
||||
requestUri: "/api/v1/users",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 92,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:42",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36187,
|
||||
module: "登录",
|
||||
content: "登录",
|
||||
requestUri: "/api/v1/auth/login",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 19340,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:38:09",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36186,
|
||||
module: "登录",
|
||||
content: "登录",
|
||||
requestUri: "/api/v1/auth/login",
|
||||
method: null,
|
||||
ip: "192.168.31.134",
|
||||
region: "0 内网IP",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 19869,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:37:59",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36185,
|
||||
module: "登录",
|
||||
content: "登录",
|
||||
requestUri: "/api/v1/auth/login",
|
||||
method: null,
|
||||
ip: "112.103.111.59",
|
||||
region: "黑龙江省 哈尔滨市",
|
||||
browser: "Chrome 97.0.4692.98",
|
||||
os: "Android",
|
||||
executionTime: 96,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:37:21",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
{
|
||||
id: 36184,
|
||||
module: "登录",
|
||||
content: "登录",
|
||||
requestUri: "/api/v1/auth/login",
|
||||
method: null,
|
||||
ip: "114.86.204.190",
|
||||
region: "上海 上海市",
|
||||
browser: "Chrome 125.0.0.0",
|
||||
os: "Windows 10 or Windows Server 2016",
|
||||
executionTime: 89,
|
||||
createBy: null,
|
||||
createTime: "2024-07-07 20:29:37",
|
||||
operator: "系统管理员",
|
||||
},
|
||||
],
|
||||
total: 36188,
|
||||
},
|
||||
msg: "一切ok",
|
||||
|
||||
@@ -6,141 +6,139 @@ export default defineMock([
|
||||
method: ["GET"],
|
||||
body: {
|
||||
code: "00000",
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
title: "v2.12.0 新增系统日志,访问趋势统计功能。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:21",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "v2.13.0 新增菜单搜索。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:22",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "\r\nv2.14.0 新增个人中心。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:23",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "v2.15.0 登录页面改造。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:24",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "v2.16.0 通知公告、字典翻译组件。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:25",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "系统将于本周六凌晨 2 点进行维护,预计维护时间为 2 小时。",
|
||||
publishStatus: 1,
|
||||
type: 2,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:26",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: "最近发现一些钓鱼邮件,请大家提高警惕,不要点击陌生链接。",
|
||||
publishStatus: 1,
|
||||
type: 3,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:27",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: "国庆假期从 10 月 1 日至 10 月 7 日放假,共 7 天。",
|
||||
publishStatus: 1,
|
||||
type: 4,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:28",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: "公司将在 10 月 15 日举办新产品发布会,敬请期待。",
|
||||
publishStatus: 1,
|
||||
type: 5,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:29",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: "v2.16.1 版本修复了 WebSocket 重复连接导致的后台线程阻塞问题,优化了通知公告。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:30",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
],
|
||||
page: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
data: {
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
title: "v2.12.0 新增系统日志,访问趋势统计功能。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:21",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "v2.13.0 新增菜单搜索。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:22",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "\r\nv2.14.0 新增个人中心。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:23",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "v2.15.0 登录页面改造。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:24",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "v2.16.0 通知公告、字典翻译组件。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:25",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "系统将于本周六凌晨 2 点进行维护,预计维护时间为 2 小时。",
|
||||
publishStatus: 1,
|
||||
type: 2,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:26",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: "最近发现一些钓鱼邮件,请大家提高警惕,不要点击陌生链接。",
|
||||
publishStatus: 1,
|
||||
type: 3,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:27",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: "国庆假期从 10 月 1 日至 10 月 7 日放假,共 7 天。",
|
||||
publishStatus: 1,
|
||||
type: 4,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:28",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: "公司将在 10 月 15 日举办新产品发布会,敬请期待。",
|
||||
publishStatus: 1,
|
||||
type: 5,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:29",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: "v2.16.1 版本修复了 WebSocket 重复连接导致的后台线程阻塞问题,优化了通知公告。",
|
||||
publishStatus: 1,
|
||||
type: 1,
|
||||
publisherName: "系统管理员",
|
||||
level: "L",
|
||||
publishTime: "2024-09-30 17:30",
|
||||
isRead: null,
|
||||
targetType: 1,
|
||||
createTime: "2024-09-28 11:21",
|
||||
revokeTime: "2024-09-30 17:21",
|
||||
},
|
||||
],
|
||||
total: 10,
|
||||
},
|
||||
msg: "一切ok",
|
||||
@@ -217,56 +215,54 @@ export default defineMock([
|
||||
method: ["GET"],
|
||||
body: {
|
||||
code: "00000",
|
||||
data: [
|
||||
{
|
||||
id: 10,
|
||||
title: "v2.16.1 版本修复了 WebSocket 重复连接导致的后台线程阻塞问题,优化了通知公告。",
|
||||
type: 1,
|
||||
level: "L",
|
||||
publisherName: "系统管理员",
|
||||
publishTime: "2024-09-30 17:30",
|
||||
isRead: 0,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: "公司将在 10 月 15 日举办新产品发布会,敬请期待。",
|
||||
type: 5,
|
||||
level: "L",
|
||||
publisherName: "系统管理员",
|
||||
publishTime: "2024-09-30 17:29",
|
||||
isRead: 0,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: "国庆假期从 10 月 1 日至 10 月 7 日放假,共 7 天。",
|
||||
type: 4,
|
||||
level: "L",
|
||||
publisherName: "系统管理员",
|
||||
publishTime: "2024-09-30 17:28",
|
||||
isRead: 0,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: "最近发现一些钓鱼邮件,请大家提高警惕,不要点击陌生链接。",
|
||||
type: 3,
|
||||
level: "L",
|
||||
publisherName: "系统管理员",
|
||||
publishTime: "2024-09-30 17:27",
|
||||
isRead: 0,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "系统将于本周六凌晨 2 点进行维护,预计维护时间为 2 小时。",
|
||||
type: 2,
|
||||
level: "L",
|
||||
publisherName: "系统管理员",
|
||||
publishTime: "2024-09-30 17:26",
|
||||
isRead: 0,
|
||||
},
|
||||
],
|
||||
page: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
data: {
|
||||
list: [
|
||||
{
|
||||
id: 10,
|
||||
title: "v2.16.1 版本修复了 WebSocket 重复连接导致的后台线程阻塞问题,优化了通知公告。",
|
||||
type: 1,
|
||||
level: "L",
|
||||
publisherName: "系统管理员",
|
||||
publishTime: "2024-09-30 17:30",
|
||||
isRead: 0,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: "公司将在 10 月 15 日举办新产品发布会,敬请期待。",
|
||||
type: 5,
|
||||
level: "L",
|
||||
publisherName: "系统管理员",
|
||||
publishTime: "2024-09-30 17:29",
|
||||
isRead: 0,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: "国庆假期从 10 月 1 日至 10 月 7 日放假,共 7 天。",
|
||||
type: 4,
|
||||
level: "L",
|
||||
publisherName: "系统管理员",
|
||||
publishTime: "2024-09-30 17:28",
|
||||
isRead: 0,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: "最近发现一些钓鱼邮件,请大家提高警惕,不要点击陌生链接。",
|
||||
type: 3,
|
||||
level: "L",
|
||||
publisherName: "系统管理员",
|
||||
publishTime: "2024-09-30 17:27",
|
||||
isRead: 0,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: "系统将于本周六凌晨 2 点进行维护,预计维护时间为 2 小时。",
|
||||
type: 2,
|
||||
level: "L",
|
||||
publisherName: "系统管理员",
|
||||
publishTime: "2024-09-30 17:26",
|
||||
isRead: 0,
|
||||
},
|
||||
],
|
||||
total: 10,
|
||||
},
|
||||
msg: "一切ok",
|
||||
|
||||
@@ -61,101 +61,99 @@ export default defineMock([
|
||||
method: ["GET"],
|
||||
body: {
|
||||
code: "00000",
|
||||
data: [
|
||||
{
|
||||
id: 2,
|
||||
name: "系统管理员",
|
||||
code: "ADMIN",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "访问游客",
|
||||
code: "GUEST",
|
||||
status: 1,
|
||||
sort: 3,
|
||||
createTime: "2021-05-26 15:49:05",
|
||||
updateTime: "2019-05-05 16:00:00",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "系统管理员1",
|
||||
code: "ADMIN1",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "系统管理员2",
|
||||
code: "ADMIN2",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "系统管理员3",
|
||||
code: "ADMIN3",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "系统管理员4",
|
||||
code: "ADMIN4",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "系统管理员5",
|
||||
code: "ADMIN5",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "系统管理员6",
|
||||
code: "ADMIN6",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: "2023-12-04 11:43:15",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "系统管理员7",
|
||||
code: "ADMIN7",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "系统管理员8",
|
||||
code: "ADMIN8",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
],
|
||||
page: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
data: {
|
||||
list: [
|
||||
{
|
||||
id: 2,
|
||||
name: "系统管理员",
|
||||
code: "ADMIN",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "访问游客",
|
||||
code: "GUEST",
|
||||
status: 1,
|
||||
sort: 3,
|
||||
createTime: "2021-05-26 15:49:05",
|
||||
updateTime: "2019-05-05 16:00:00",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "系统管理员1",
|
||||
code: "ADMIN1",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "系统管理员2",
|
||||
code: "ADMIN2",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "系统管理员3",
|
||||
code: "ADMIN3",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "系统管理员4",
|
||||
code: "ADMIN4",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "系统管理员5",
|
||||
code: "ADMIN5",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "系统管理员6",
|
||||
code: "ADMIN6",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: "2023-12-04 11:43:15",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: "系统管理员7",
|
||||
code: "ADMIN7",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: "系统管理员8",
|
||||
code: "ADMIN8",
|
||||
status: 1,
|
||||
sort: 2,
|
||||
createTime: "2021-03-25 12:39:54",
|
||||
updateTime: null,
|
||||
},
|
||||
],
|
||||
total: 10,
|
||||
},
|
||||
msg: "一切ok",
|
||||
@@ -214,7 +212,7 @@ export default defineMock([
|
||||
},
|
||||
// 获取角色拥有的菜单ID
|
||||
{
|
||||
url: "roles/:id/menuIds",
|
||||
url: "roles/:id/menu-ids",
|
||||
method: ["GET"],
|
||||
body: () => {
|
||||
return {
|
||||
|
||||
@@ -70,35 +70,33 @@ export default defineMock([
|
||||
method: ["GET"],
|
||||
body: {
|
||||
code: "00000",
|
||||
data: [
|
||||
{
|
||||
id: 2,
|
||||
username: "admin",
|
||||
nickname: "系统管理员",
|
||||
mobile: "17621210366",
|
||||
gender: 1,
|
||||
avatar: "https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif",
|
||||
email: "",
|
||||
status: 1,
|
||||
deptId: 1,
|
||||
roleIds: [2],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
username: "test",
|
||||
nickname: "测试小用户",
|
||||
mobile: "17621210366",
|
||||
gender: 1,
|
||||
avatar: "https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif",
|
||||
email: "youlaitech@163.com",
|
||||
status: 1,
|
||||
deptId: 3,
|
||||
roleIds: [3],
|
||||
},
|
||||
],
|
||||
page: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
data: {
|
||||
list: [
|
||||
{
|
||||
id: 2,
|
||||
username: "admin",
|
||||
nickname: "系统管理员",
|
||||
mobile: "17621210366",
|
||||
gender: 1,
|
||||
avatar: "https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif",
|
||||
email: "",
|
||||
status: 1,
|
||||
deptId: 1,
|
||||
roleIds: [2],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
username: "test",
|
||||
nickname: "测试小用户",
|
||||
mobile: "17621210366",
|
||||
gender: 1,
|
||||
avatar: "https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif",
|
||||
email: "youlaitech@163.com",
|
||||
status: 1,
|
||||
deptId: 3,
|
||||
roleIds: [3],
|
||||
},
|
||||
],
|
||||
total: 2,
|
||||
},
|
||||
msg: "一切ok",
|
||||
|
||||
@@ -94,9 +94,9 @@ const DictAPI = {
|
||||
url: `${DICT_BASE_URL}/${dictCode}/items`,
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
}).then((res) => ({
|
||||
...res,
|
||||
data: (res.data ?? []).map((item) => ({
|
||||
}).then((data) => ({
|
||||
...data,
|
||||
list: (data.list ?? []).map((item) => ({
|
||||
...item,
|
||||
tagType: decodeDictTagType((item as any).tagType),
|
||||
})),
|
||||
|
||||
@@ -18,7 +18,7 @@ const RoleAPI = {
|
||||
},
|
||||
/** 获取角色的菜单ID集合 */
|
||||
getRoleMenuIds(roleId: string) {
|
||||
return request<any, string[]>({ url: `${ROLE_BASE_URL}/${roleId}/menuIds`, method: "get" });
|
||||
return request<any, string[]>({ url: `${ROLE_BASE_URL}/${roleId}/menu-ids`, method: "get" });
|
||||
},
|
||||
/** 分配菜单权限 */
|
||||
updateRoleMenus(roleId: string, data: number[]) {
|
||||
@@ -28,6 +28,10 @@ const RoleAPI = {
|
||||
getFormData(id: string) {
|
||||
return request<any, RoleForm>({ url: `${ROLE_BASE_URL}/${id}/form`, method: "get" });
|
||||
},
|
||||
/** 获取角色的部门ID集合(自定义数据权限) */
|
||||
getRoleDeptIds(roleId: string) {
|
||||
return request<any, number[]>({ url: `${ROLE_BASE_URL}/${roleId}/dept-ids`, method: "get" });
|
||||
},
|
||||
/** 新增角色 */
|
||||
create(data: RoleForm) {
|
||||
return request({ url: `${ROLE_BASE_URL}`, method: "post", data });
|
||||
|
||||
@@ -51,14 +51,20 @@ function isDashboard(route: RouteLocationMatched) {
|
||||
function handleLink(item: any) {
|
||||
const { redirect, path } = item;
|
||||
if (redirect) {
|
||||
router.push(redirect).catch((err) => {
|
||||
console.warn(err);
|
||||
});
|
||||
router.push(redirect).then(
|
||||
() => {},
|
||||
(err) => {
|
||||
console.warn(err);
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
router.push(pathCompile(path)).catch((err) => {
|
||||
console.warn(err);
|
||||
});
|
||||
router.push(pathCompile(path)).then(
|
||||
() => {},
|
||||
(err) => {
|
||||
console.warn(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
watch(
|
||||
|
||||
@@ -509,24 +509,29 @@ function handleDelete(id?: number | string) {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(function () {
|
||||
}).then(
|
||||
function () {
|
||||
if (props.contentConfig.deleteAction) {
|
||||
props.contentConfig
|
||||
.deleteAction(ids)
|
||||
.then(() => {
|
||||
props.contentConfig.deleteAction(ids).then(
|
||||
() => {
|
||||
ElMessage.success("删除成功");
|
||||
removeIds.value = [];
|
||||
// 清空选中项
|
||||
tableRef.value?.clearSelection();
|
||||
handleRefresh(true);
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
() => {
|
||||
// 交由全局错误处理
|
||||
}
|
||||
);
|
||||
} else {
|
||||
ElMessage.error("未配置deleteAction");
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
() => {
|
||||
// 用户取消
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 导出表单
|
||||
@@ -591,14 +596,14 @@ function handleExports() {
|
||||
worksheet.columns = columns;
|
||||
if (exportsFormData.origin === ExportsOriginEnum.REMOTE) {
|
||||
if (props.contentConfig.exportsAction) {
|
||||
props.contentConfig.exportsAction(lastFormData).then((res) => {
|
||||
worksheet.addRows(res);
|
||||
workbook.xlsx
|
||||
.writeBuffer()
|
||||
.then((buffer) => {
|
||||
props.contentConfig.exportsAction(lastFormData).then((data) => {
|
||||
worksheet.addRows(data);
|
||||
workbook.xlsx.writeBuffer().then(
|
||||
(buffer) => {
|
||||
saveXlsx(buffer, filename as string);
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
},
|
||||
(error) => console.log(error)
|
||||
);
|
||||
});
|
||||
} else {
|
||||
ElMessage.error("未配置exportsAction");
|
||||
@@ -607,12 +612,12 @@ function handleExports() {
|
||||
worksheet.addRows(
|
||||
exportsFormData.origin === ExportsOriginEnum.SELECTED ? selectionData.value : pageData.value
|
||||
);
|
||||
workbook.xlsx
|
||||
.writeBuffer()
|
||||
.then((buffer) => {
|
||||
workbook.xlsx.writeBuffer().then(
|
||||
(buffer) => {
|
||||
saveXlsx(buffer, filename as string);
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
},
|
||||
(error) => console.log(error)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,9 +715,8 @@ function handleImports() {
|
||||
if (ev.target !== null && ev.target.result !== null) {
|
||||
const result = ev.target.result as ArrayBuffer;
|
||||
// 从 buffer 中加载并解析数据
|
||||
workbook.xlsx
|
||||
.load(result)
|
||||
.then((workbook) => {
|
||||
workbook.xlsx.load(result).then(
|
||||
(workbook) => {
|
||||
// 解析后的数据
|
||||
const data = [];
|
||||
// 获取第一个worksheet内容
|
||||
@@ -745,14 +749,14 @@ function handleImports() {
|
||||
handleCloseImportModal();
|
||||
handleRefresh(true);
|
||||
});
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
},
|
||||
(error) => console.log(error)
|
||||
);
|
||||
} else {
|
||||
ElMessage.error("读取文件失败");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 操作人"
|
||||
function handleToolbar(name: string) {
|
||||
switch (name) {
|
||||
@@ -864,17 +868,21 @@ function fetchPageData(formData: IObject = {}, isRestart = false) {
|
||||
? {
|
||||
[request.pageName]: pagination.currentPage,
|
||||
[request.limitName]: pagination.pageSize,
|
||||
...getFilterParams(),
|
||||
...formData,
|
||||
}
|
||||
: {
|
||||
...getFilterParams(),
|
||||
...formData,
|
||||
}
|
||||
: formData
|
||||
)
|
||||
.then((data) => {
|
||||
if (showPagination) {
|
||||
const pageResult = Array.isArray(data) ? { data, page: null } : data;
|
||||
pagination.total = pageResult.page?.total ?? 0;
|
||||
pageData.value = pageResult.data ?? [];
|
||||
const pageResult = Array.isArray(data) ? { list: data, total: 0 } : data;
|
||||
pagination.total = pageResult?.total ?? 0;
|
||||
pageData.value = pageResult?.list ?? [];
|
||||
} else {
|
||||
pageData.value = Array.isArray(data) ? data : (data.data ?? []);
|
||||
pageData.value = Array.isArray(data) ? data : (data?.list ?? data?.data ?? []);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import type PageContent from "./PageContent.vue";
|
||||
import type PageModal from "./PageModal.vue";
|
||||
import type PageSearch from "./PageSearch.vue";
|
||||
import type { CSSProperties } from "vue";
|
||||
import type { PageResult } from "@/types/api/common";
|
||||
|
||||
export type PageSearchInstance = InstanceType<typeof PageSearch>;
|
||||
export type PageContentInstance = InstanceType<typeof PageContent>;
|
||||
@@ -78,7 +79,7 @@ export interface IContentConfig<TQuery = any, TItem = any> {
|
||||
pageName: string;
|
||||
limitName: string;
|
||||
};
|
||||
// 分页接口统一返回 PageResult { data, page }
|
||||
// 分页接口统一返回 PageResult { list, total }
|
||||
// 修改属性的网络请求函数(需返回promise)
|
||||
modifyAction?: (data: {
|
||||
[key: string]: any;
|
||||
|
||||
@@ -27,15 +27,15 @@ const props = defineProps({
|
||||
function handleClipboard() {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
// 使用 Clipboard API
|
||||
navigator.clipboard
|
||||
.writeText(props.text)
|
||||
.then(() => {
|
||||
navigator.clipboard.writeText(props.text).then(
|
||||
() => {
|
||||
ElMessage.success("Copy successfully");
|
||||
})
|
||||
.catch((error) => {
|
||||
},
|
||||
(error) => {
|
||||
ElMessage.warning("Copy failed");
|
||||
console.log("[CopyButton] Copy failed", error);
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// 兼容性处理(useClipboard 有兼容性问题)
|
||||
const input = document.createElement("input");
|
||||
@@ -44,19 +44,18 @@ function handleClipboard() {
|
||||
input.setAttribute("value", props.text);
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
let successful = false;
|
||||
try {
|
||||
const successful = document.execCommand("copy");
|
||||
if (successful) {
|
||||
ElMessage.success("Copy successfully!");
|
||||
} else {
|
||||
ElMessage.warning("Copy failed!");
|
||||
}
|
||||
} catch (err) {
|
||||
ElMessage.error("Copy failed.");
|
||||
console.log("[CopyButton] Copy failed.", err);
|
||||
successful = document.execCommand("copy");
|
||||
} finally {
|
||||
document.body.removeChild(input);
|
||||
}
|
||||
|
||||
if (successful) {
|
||||
ElMessage.success("Copy successfully!");
|
||||
} else {
|
||||
ElMessage.warning("Copy failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -261,9 +261,9 @@ function fetchPageData(isRestart = false) {
|
||||
}
|
||||
props.selectConfig
|
||||
.indexAction(queryParams)
|
||||
.then((res) => {
|
||||
total.value = res.page?.total ?? 0;
|
||||
pageData.value = res.data ?? [];
|
||||
.then((data) => {
|
||||
total.value = data.total ?? 0;
|
||||
pageData.value = data.list ?? [];
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
|
||||
@@ -172,13 +172,14 @@ function handleUpload(options: UploadRequestOptions) {
|
||||
if (fileItem) {
|
||||
fileItem.percentage = percent;
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
resolve(res);
|
||||
})
|
||||
.catch((err) => {
|
||||
}).then(
|
||||
(data) => {
|
||||
resolve(data);
|
||||
},
|
||||
(err) => {
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -156,13 +156,14 @@ function handleUpload(options: UploadRequestOptions) {
|
||||
formData.append(key, props.data[key]);
|
||||
});
|
||||
|
||||
FileAPI.upload(formData)
|
||||
.then((data) => {
|
||||
FileAPI.upload(formData).then(
|
||||
(data) => {
|
||||
resolve(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
},
|
||||
(error) => {
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -137,13 +137,14 @@ function handleUpload(options: UploadRequestOptions) {
|
||||
formData.append(key, props.data[key]);
|
||||
});
|
||||
|
||||
FileAPI.upload(formData)
|
||||
.then((data) => {
|
||||
FileAPI.upload(formData).then(
|
||||
(data) => {
|
||||
resolve(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
},
|
||||
(error) => {
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -64,9 +64,9 @@ const editorConfig = ref<Partial<IEditorConfig>>({
|
||||
uploadImage: {
|
||||
customUpload(file: File, insertFn: InsertFnType) {
|
||||
// 上传图片
|
||||
FileAPI.uploadFile(file).then((res) => {
|
||||
FileAPI.uploadFile(file).then((data) => {
|
||||
// 插入图片
|
||||
insertFn(res.url, res.name, res.url);
|
||||
insertFn(data.url, data.name, data.url);
|
||||
});
|
||||
},
|
||||
} as any,
|
||||
|
||||
@@ -24,6 +24,11 @@ export const enum ApiCodeEnum {
|
||||
*/
|
||||
REFRESH_TOKEN_INVALID = "A0231",
|
||||
|
||||
/**
|
||||
* 权限不足
|
||||
*/
|
||||
PERMISSION_DENIED = "A0301",
|
||||
|
||||
/**
|
||||
* 需要选择租户
|
||||
*/
|
||||
|
||||
@@ -105,15 +105,15 @@ const showTenantSwitcher = computed(() => {
|
||||
});
|
||||
|
||||
function handleTenantChange(tenantId: number) {
|
||||
tenantStore
|
||||
.switchTenant(tenantId)
|
||||
.then(() => {
|
||||
tenantStore.switchTenant(tenantId).then(
|
||||
() => {
|
||||
ElMessage.success("切换租户成功");
|
||||
window.location.href = "/";
|
||||
})
|
||||
.catch((error: any) => {
|
||||
},
|
||||
(error: any) => {
|
||||
ElMessage.error(error.message || "切换租户失败");
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { RouteRecordRaw } from "vue-router";
|
||||
import { constantRoutes } from "@/router";
|
||||
import { store } from "@/store";
|
||||
import router from "@/router";
|
||||
import { useUserStoreHook } from "@/store/modules/user";
|
||||
|
||||
import MenuAPI from "@/api/system/menu";
|
||||
import { RouteItem } from "@/types";
|
||||
@@ -67,6 +68,60 @@ export const usePermissionStore = defineStore("permission", () => {
|
||||
isRouteGenerated.value = false;
|
||||
};
|
||||
|
||||
let reloadPromise: Promise<RouteRecordRaw[]> | null = null;
|
||||
|
||||
/**
|
||||
* 重新加载动态路由(单飞)。
|
||||
*
|
||||
* 典型场景:后端权限变更导致接口返回权限不足(A0301),前端需要刷新路由和菜单以同步最新权限。
|
||||
*
|
||||
* - 会先清理已注册的动态路由(resetRouter)
|
||||
* - 重新从后端拉取路由(generateRoutes)
|
||||
* - 将动态路由注册到 vue-router(router.addRoute)
|
||||
*/
|
||||
async function reloadDynamicRoutesOnce(): Promise<RouteRecordRaw[]> {
|
||||
if (reloadPromise) return reloadPromise;
|
||||
|
||||
reloadPromise = (async () => {
|
||||
try {
|
||||
resetRouter();
|
||||
const dynamicRoutes = await generateRoutes();
|
||||
dynamicRoutes.forEach((route: RouteRecordRaw) => {
|
||||
router.addRoute(route);
|
||||
});
|
||||
return dynamicRoutes;
|
||||
} finally {
|
||||
reloadPromise = null;
|
||||
}
|
||||
})();
|
||||
|
||||
return reloadPromise;
|
||||
}
|
||||
|
||||
let snapshotPromise: Promise<void> | null = null;
|
||||
|
||||
/**
|
||||
* 刷新权限快照(单飞)。
|
||||
*
|
||||
* - 刷新用户信息(包含 perms/roles 等)
|
||||
* - 重新加载动态路由
|
||||
*/
|
||||
async function reloadPermissionSnapshotOnce(): Promise<void> {
|
||||
if (snapshotPromise) return snapshotPromise;
|
||||
|
||||
snapshotPromise = (async () => {
|
||||
try {
|
||||
const userStore = useUserStoreHook();
|
||||
await userStore.getUserInfo();
|
||||
await reloadDynamicRoutesOnce();
|
||||
} finally {
|
||||
snapshotPromise = null;
|
||||
}
|
||||
})();
|
||||
|
||||
return snapshotPromise;
|
||||
}
|
||||
|
||||
return {
|
||||
routes,
|
||||
mixLayoutSideMenus,
|
||||
@@ -74,6 +129,8 @@ export const usePermissionStore = defineStore("permission", () => {
|
||||
generateRoutes,
|
||||
setMixLayoutSideMenus,
|
||||
resetRouter,
|
||||
reloadDynamicRoutesOnce,
|
||||
reloadPermissionSnapshotOnce,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -38,6 +38,23 @@ export const useUserStore = defineStore("user", () => {
|
||||
});
|
||||
}
|
||||
|
||||
let refreshPromise: Promise<void> | null = null;
|
||||
|
||||
/**
|
||||
* 刷新 token(单飞)。
|
||||
*
|
||||
* 多个并发请求遇到 token 过期时,共享同一次 refresh 请求。
|
||||
*/
|
||||
function refreshTokenOnce() {
|
||||
if (refreshPromise) return refreshPromise;
|
||||
|
||||
refreshPromise = refreshToken().finally(() => {
|
||||
refreshPromise = null;
|
||||
});
|
||||
|
||||
return refreshPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*
|
||||
@@ -146,6 +163,7 @@ export const useUserStore = defineStore("user", () => {
|
||||
resetAllState,
|
||||
resetUserState,
|
||||
refreshToken,
|
||||
refreshTokenOnce,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@ export interface ApiResponse<T = any> {
|
||||
data: T;
|
||||
/** 响应消息 */
|
||||
msg: string;
|
||||
|
||||
/** 分页信息(非列表接口通常不存在该字段) */
|
||||
page?: PageMeta | null;
|
||||
}
|
||||
|
||||
/** 基础查询参数 */
|
||||
@@ -29,20 +26,12 @@ export interface BaseQueryParams {
|
||||
order?: string;
|
||||
}
|
||||
|
||||
/** 分页元信息 */
|
||||
export interface PageMeta {
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
/** 列表响应结构(统一) */
|
||||
/** 分页数据结构(仅分页接口) */
|
||||
export interface PageResult<T> {
|
||||
/** 数据列表 */
|
||||
data: T[];
|
||||
|
||||
/** 分页信息,不分页时为 null */
|
||||
page: PageMeta | null;
|
||||
list: T[];
|
||||
/** 总记录数 */
|
||||
total: number;
|
||||
}
|
||||
|
||||
/** 下拉选项 */
|
||||
|
||||
@@ -38,8 +38,10 @@ export interface RoleForm {
|
||||
name?: string;
|
||||
/** 排序 */
|
||||
sort?: number;
|
||||
/** 数据权限 */
|
||||
/** 数据权限(1-所有数据 2-部门及子部门数据 3-本部门数据 4-本人数据 5-自定义部门数据) */
|
||||
dataScope?: number;
|
||||
/** 自定义数据权限部门ID列表(当dataScope=5时有效) */
|
||||
deptIds?: number[];
|
||||
/** 角色状态 */
|
||||
status?: number;
|
||||
/** 备注 */
|
||||
|
||||
@@ -2,6 +2,7 @@ import axios, { type InternalAxiosRequestConfig, type AxiosResponse } from "axio
|
||||
import qs from "qs";
|
||||
import { ApiCodeEnum } from "@/enums/api";
|
||||
import { useUserStoreHook } from "@/store/modules/user";
|
||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||
import { AuthStorage, redirectToLogin } from "@/utils/auth";
|
||||
|
||||
// ============================================
|
||||
@@ -49,13 +50,9 @@ http.interceptors.response.use(
|
||||
const { code, data, msg } = response.data;
|
||||
|
||||
if (code === ApiCodeEnum.SUCCESS) {
|
||||
// 分页接口需要同时返回 data 与 page 元信息
|
||||
const page = (response.data as any)?.page;
|
||||
if (page != null) return { data, page };
|
||||
return data;
|
||||
}
|
||||
ElMessage.error(msg || "系统出错");
|
||||
return Promise.reject(new Error(msg || "Error"));
|
||||
return rejectWithMessage(msg, "系统出错");
|
||||
},
|
||||
|
||||
async (error) => {
|
||||
@@ -68,54 +65,78 @@ http.interceptors.response.use(
|
||||
|
||||
const { code, msg } = response.data as ApiResponse;
|
||||
|
||||
// Token 过期处理
|
||||
// Token 过期:尝试刷新 token 后自动重试一次
|
||||
if (code === ApiCodeEnum.ACCESS_TOKEN_INVALID) {
|
||||
return retryWithRefresh(config);
|
||||
}
|
||||
|
||||
// Refresh token 失效:无法续期,跳转登录
|
||||
if (code === ApiCodeEnum.REFRESH_TOKEN_INVALID) {
|
||||
await redirectToLogin("登录已过期,请重新登录");
|
||||
return Promise.reject(new Error(msg || "Token Invalid"));
|
||||
}
|
||||
|
||||
ElMessage.error(msg || "请求失败");
|
||||
return Promise.reject(new Error(msg || "Error"));
|
||||
// 权限不足:刷新权限快照(用户信息 + 动态路由)后提示
|
||||
if (code === ApiCodeEnum.PERMISSION_DENIED) {
|
||||
return handlePermissionDenied(msg);
|
||||
}
|
||||
|
||||
return rejectWithMessage(msg, "请求失败");
|
||||
}
|
||||
);
|
||||
|
||||
export default http;
|
||||
|
||||
// ============================================
|
||||
// Token 刷新重试
|
||||
// ============================================
|
||||
|
||||
type Pending = { resolve: (v: unknown) => void; reject: (e: Error) => void };
|
||||
|
||||
let refreshing = false;
|
||||
const queue: Pending[] = [];
|
||||
|
||||
async function retryWithRefresh(config: InternalAxiosRequestConfig): Promise<unknown> {
|
||||
return new Promise((resolve, reject) => {
|
||||
queue.push({ resolve, reject });
|
||||
|
||||
if (refreshing) return;
|
||||
refreshing = true;
|
||||
|
||||
useUserStoreHook()
|
||||
.refreshToken()
|
||||
.then(() => {
|
||||
const token = AuthStorage.getAccessToken();
|
||||
if (token) config.headers.Authorization = `Bearer ${token}`;
|
||||
|
||||
queue.forEach(({ resolve }) => http(config).then(resolve).catch(reject));
|
||||
})
|
||||
.catch(async () => {
|
||||
queue.forEach(({ reject }) => reject(new Error("Token refresh failed")));
|
||||
await redirectToLogin("登录已过期,请重新登录");
|
||||
})
|
||||
.finally(() => {
|
||||
queue.length = 0;
|
||||
refreshing = false;
|
||||
});
|
||||
});
|
||||
/**
|
||||
* 权限不足处理:刷新权限快照(用户信息 + 动态路由),并给出提示
|
||||
*
|
||||
* 刷新完成后仍然按失败处理,交由调用方的错误流处理
|
||||
*/
|
||||
async function handlePermissionDenied(msg?: string): Promise<never> {
|
||||
const permissionStore = usePermissionStoreHook();
|
||||
await permissionStore.reloadPermissionSnapshotOnce();
|
||||
return rejectWithMessage(msg, "权限不足");
|
||||
}
|
||||
|
||||
/**
|
||||
* access token 过期后的自动续期与重试
|
||||
*
|
||||
* - 刷新 token 走单飞(userStore.refreshTokenOnce)
|
||||
* - 当前请求最多重试一次(__isTokenRetry 标记)
|
||||
*/
|
||||
async function retryWithRefresh(config: InternalAxiosRequestConfig): Promise<unknown> {
|
||||
const retryConfig = config as InternalAxiosRequestConfig & { __isTokenRetry?: boolean };
|
||||
if (retryConfig.__isTokenRetry) {
|
||||
await redirectToLogin("登录已过期,请重新登录");
|
||||
return Promise.reject(new Error("Token Invalid"));
|
||||
}
|
||||
retryConfig.__isTokenRetry = true;
|
||||
|
||||
try {
|
||||
const userStore = useUserStoreHook();
|
||||
await userStore.refreshTokenOnce();
|
||||
|
||||
const token = AuthStorage.getAccessToken();
|
||||
if (token) {
|
||||
retryConfig.headers = retryConfig.headers || ({} as any);
|
||||
(retryConfig.headers as any).Authorization = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
return http(retryConfig);
|
||||
} catch {
|
||||
await redirectToLogin("登录已过期,请重新登录");
|
||||
return Promise.reject(new Error("Token refresh failed"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一处理业务错误提示并拒绝 Promise
|
||||
*
|
||||
* @param msg 错误消息内容
|
||||
* @param fallback 默认兜底消息
|
||||
*/
|
||||
function rejectWithMessage(msg: string | undefined, fallback: string): Promise<never> {
|
||||
const message = msg || fallback;
|
||||
ElMessage.error(message);
|
||||
return Promise.reject(new Error(message));
|
||||
}
|
||||
|
||||
export default http;
|
||||
|
||||
@@ -823,9 +823,9 @@ function handleNextClick() {
|
||||
function handleQuery() {
|
||||
loading.value = true;
|
||||
GeneratorAPI.getTablePage(queryParams)
|
||||
.then((res) => {
|
||||
pageData.value = res.data;
|
||||
total.value = res.page?.total ?? 0;
|
||||
.then((data) => {
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
@@ -1071,18 +1071,16 @@ async function writeFile(dirHandle: any, filePath: string, content: string) {
|
||||
const folderSegments = parts;
|
||||
const targetDir = await ensureDir(dirHandle, folderSegments, true);
|
||||
// @ts-ignore
|
||||
let fileHandle;
|
||||
try {
|
||||
// @ts-ignore
|
||||
fileHandle = await targetDir.getFileHandle(fileName, { create: true });
|
||||
} catch (err: any) {
|
||||
if (err?.name === "TypeMismatchError") {
|
||||
// 存在同名目录(或其它类型冲突),为安全起见不自动删除
|
||||
throw err;
|
||||
} else {
|
||||
const fileHandle = await targetDir.getFileHandle(fileName, { create: true }).then(
|
||||
(handle: any) => handle,
|
||||
(err: any) => {
|
||||
if (err?.name === "TypeMismatchError") {
|
||||
// 存在同名目录(或其它类型冲突),为安全起见不自动删除
|
||||
throw err;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
);
|
||||
// @ts-ignore
|
||||
const writable = await fileHandle.createWritable();
|
||||
await writable.write(content ?? "");
|
||||
@@ -1190,41 +1188,43 @@ const writeGeneratedCode = async () => {
|
||||
while (queue.length) {
|
||||
const item = queue.shift()!;
|
||||
try {
|
||||
const root = resolveRootForItem(item);
|
||||
const relativePath = stripProjectRoot(`${item.path}/${item.fileName}`);
|
||||
writeProgress.current = relativePath;
|
||||
if (overwriteMode.value === "ifChanged") {
|
||||
// 简单差异:已有文件内容与待写内容相同则跳过
|
||||
// @ts-ignore
|
||||
const targetRoot = root === "frontend" ? frontendDirHandle.value : backendDirHandle.value;
|
||||
const existsSame = await isSameFile(targetRoot, relativePath, item.content || "");
|
||||
if (existsSame) {
|
||||
// 视作成功但不处理
|
||||
writeProgress.done++;
|
||||
writeProgress.percent = Math.round((writeProgress.done / writeProgress.total) * 100);
|
||||
continue;
|
||||
await (async () => {
|
||||
const root = resolveRootForItem(item);
|
||||
const relativePath = stripProjectRoot(`${item.path}/${item.fileName}`);
|
||||
writeProgress.current = relativePath;
|
||||
if (overwriteMode.value === "ifChanged") {
|
||||
// 简单差异:已有文件内容与待写内容相同则跳过
|
||||
// @ts-ignore
|
||||
const targetRoot =
|
||||
root === "frontend" ? frontendDirHandle.value : backendDirHandle.value;
|
||||
const existsSame = await isSameFile(targetRoot, relativePath, item.content || "");
|
||||
if (existsSame) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (overwriteMode.value === "skip") {
|
||||
// @ts-ignore
|
||||
const targetRoot = root === "frontend" ? frontendDirHandle.value : backendDirHandle.value;
|
||||
const exists = await pathExists(targetRoot, relativePath);
|
||||
if (exists) {
|
||||
writeProgress.done++;
|
||||
writeProgress.percent = Math.round((writeProgress.done / writeProgress.total) * 100);
|
||||
continue;
|
||||
if (overwriteMode.value === "skip") {
|
||||
// @ts-ignore
|
||||
const targetRoot =
|
||||
root === "frontend" ? frontendDirHandle.value : backendDirHandle.value;
|
||||
const exists = await pathExists(targetRoot, relativePath);
|
||||
if (exists) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (root === "frontend") {
|
||||
await writeFile(frontendDirHandle.value, relativePath, item.content || "");
|
||||
frontCount++;
|
||||
} else {
|
||||
await writeFile(backendDirHandle.value, relativePath, item.content || "");
|
||||
backCount++;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("写入失败:", item.path, err);
|
||||
failed.push(item.path);
|
||||
if (root === "frontend") {
|
||||
await writeFile(frontendDirHandle.value, relativePath, item.content || "");
|
||||
frontCount++;
|
||||
} else {
|
||||
await writeFile(backendDirHandle.value, relativePath, item.content || "");
|
||||
backCount++;
|
||||
}
|
||||
})().then(
|
||||
() => {},
|
||||
(err) => {
|
||||
console.error("写入失败:", item.path, err);
|
||||
failed.push(item.path);
|
||||
}
|
||||
);
|
||||
} finally {
|
||||
writeProgress.done++;
|
||||
writeProgress.percent = Math.round((writeProgress.done / writeProgress.total) * 100);
|
||||
|
||||
@@ -99,13 +99,9 @@ const stateArr = ref<OptionItem[]>([
|
||||
|
||||
// 初始化选项数据
|
||||
const initOptions = async () => {
|
||||
try {
|
||||
const [dept, roles] = await Promise.all([DeptAPI.getOptions(), RoleAPI.getOptions()]);
|
||||
deptArr.value = dept;
|
||||
roleArr.value = roles;
|
||||
} catch (error) {
|
||||
console.error("初始化选项失败:", error);
|
||||
}
|
||||
const [dept, roles] = await Promise.all([DeptAPI.getOptions(), RoleAPI.getOptions()]);
|
||||
deptArr.value = dept;
|
||||
roleArr.value = roles;
|
||||
};
|
||||
|
||||
// ========================= 搜索配置 =========================
|
||||
@@ -191,9 +187,9 @@ const contentConfig: IContentConfig<UserQueryParams, UserItem> = reactive({
|
||||
return Promise.resolve();
|
||||
},
|
||||
async exportsAction(params: any) {
|
||||
const res = await UserAPI.getPage(params);
|
||||
console.log("exportsAction", res.data);
|
||||
return res.data;
|
||||
const data = await UserAPI.getPage(params);
|
||||
console.log("exportsAction", data.list);
|
||||
return data.list;
|
||||
},
|
||||
pk: "id",
|
||||
toolbar: [
|
||||
@@ -560,8 +556,8 @@ const handleOperateClick = (data: IObject) => {
|
||||
ElMessageBox.prompt("请输入用户名" + data.row.username + "」的新密码", "重置密码", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
})
|
||||
.then(({ value }: any) => {
|
||||
}).then(
|
||||
({ value }: any) => {
|
||||
if (!value || value.length < 6) {
|
||||
ElMessage.warning("密码至少需6位字符,请重新输入");
|
||||
return false;
|
||||
@@ -569,8 +565,11 @@ const handleOperateClick = (data: IObject) => {
|
||||
UserAPI.resetPassword(data.row.id, value).then(() => {
|
||||
ElMessage.success("密码重置成功,新密码是:" + value);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
() => {
|
||||
// 用户取消
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ const contentConfig: IContentConfig<UserQueryParams, UserItem> = {
|
||||
},
|
||||
async exportsAction(params) {
|
||||
// 模拟获取到的是全量数据
|
||||
const res = await UserAPI.getPage(params);
|
||||
console.log("exportsAction", res.data);
|
||||
return res.data;
|
||||
const data = await UserAPI.getPage(params);
|
||||
console.log("exportsAction", data.list);
|
||||
return data.list;
|
||||
},
|
||||
pk: "id",
|
||||
toolbar: [
|
||||
|
||||
@@ -18,14 +18,10 @@ export const stateArr = ref<OptionItem[]>([
|
||||
|
||||
// 初始化逻辑,在 onMounted 钩子中调用
|
||||
export const initOptions = async () => {
|
||||
try {
|
||||
// 使用Promise.all并行请求
|
||||
const [dept, roles] = await Promise.all([DeptAPI.getOptions(), RoleAPI.getOptions()]);
|
||||
// 获取部门选项并赋值
|
||||
deptArr.value = dept;
|
||||
// 获取角色选项并赋值
|
||||
roleArr.value = roles;
|
||||
} catch (error) {
|
||||
console.error("初始化选项失败:", error);
|
||||
}
|
||||
// 使用Promise.all并行请求
|
||||
const [dept, roles] = await Promise.all([DeptAPI.getOptions(), RoleAPI.getOptions()]);
|
||||
// 获取部门选项并赋值
|
||||
deptArr.value = dept;
|
||||
// 获取角色选项并赋值
|
||||
roleArr.value = roles;
|
||||
};
|
||||
|
||||
@@ -68,12 +68,8 @@ const contentConfig: IContentConfig<DemoQueryParams, DemoItem> = {
|
||||
const end = start + pageSize;
|
||||
|
||||
return Promise.resolve({
|
||||
data: list.slice(start, end),
|
||||
page: {
|
||||
pageNum,
|
||||
pageSize,
|
||||
total: list.length,
|
||||
},
|
||||
list: list.slice(start, end),
|
||||
total: list.length,
|
||||
});
|
||||
},
|
||||
modifyAction(data) {
|
||||
|
||||
@@ -169,8 +169,8 @@ const handleOperateClick = (data: IObject) => {
|
||||
ElMessageBox.prompt("请输入用户名" + data.row.username + "」的新密码", "重置密码", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
})
|
||||
.then(({ value }) => {
|
||||
}).then(
|
||||
({ value }) => {
|
||||
if (!value || value.length < 6) {
|
||||
ElMessage.warning("密码至少需6位字符,请重新输入");
|
||||
return false;
|
||||
@@ -178,8 +178,11 @@ const handleOperateClick = (data: IObject) => {
|
||||
UserAPI.resetPassword(data.row.id, value).then(() => {
|
||||
ElMessage.success("密码重置成功,新密码是:" + value);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
() => {
|
||||
// 用户取消
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
const handleOperateClick2 = (data: IOperateData) => {
|
||||
|
||||
@@ -218,20 +218,14 @@ const saveDict = async () => {
|
||||
if (!dictForm.value) return;
|
||||
|
||||
saving.value = true;
|
||||
try {
|
||||
// dictForm的类型已经是DictItemForm,直接传递
|
||||
await DictAPI.updateDictItem(DICT_CODE, MALE_ITEM_ID, dictForm.value);
|
||||
// dictForm的类型已经是DictItemForm,直接传递
|
||||
await DictAPI.updateDictItem(DICT_CODE, MALE_ITEM_ID, dictForm.value);
|
||||
|
||||
// 更新时间
|
||||
lastUpdateTime.value = useDateFormat(new Date(), "YYYY-MM-DD HH:mm:ss").value;
|
||||
// 更新时间
|
||||
lastUpdateTime.value = useDateFormat(new Date(), "YYYY-MM-DD HH:mm:ss").value;
|
||||
|
||||
ElMessage.success("保存成功,后端将通过WebSocket通知所有客户端");
|
||||
} catch (error) {
|
||||
console.error("保存字典项失败", error);
|
||||
ElMessage.error("保存失败");
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
ElMessage.success("保存成功,后端将通过WebSocket通知所有客户端");
|
||||
saving.value = false;
|
||||
};
|
||||
|
||||
// 组件挂载时加载性别字典
|
||||
|
||||
@@ -192,27 +192,28 @@ function getCaptcha() {
|
||||
* 登录提交
|
||||
*/
|
||||
async function handleLoginSubmit() {
|
||||
// 1. 表单验证
|
||||
const valid = await loginFormRef.value?.validate().then(
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
if (!valid) return;
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
// 1. 表单验证
|
||||
const valid = await loginFormRef.value?.validate();
|
||||
if (!valid) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
// 2. 执行登录
|
||||
try {
|
||||
await userStore.login(loginFormData.value);
|
||||
// 登录成功,跳转到目标页面
|
||||
const redirectPath = (route.query.redirect as string) || "/";
|
||||
await router.push(decodeURIComponent(redirectPath));
|
||||
} catch (error) {
|
||||
// 登录失败,刷新验证码
|
||||
getCaptcha();
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
// 统一错误处理
|
||||
console.error("登录失败:", error);
|
||||
await userStore.login(loginFormData.value).then(
|
||||
async () => {
|
||||
// 登录成功,跳转到目标页面
|
||||
const redirectPath = (route.query.redirect as string) || "/";
|
||||
await router.push(decodeURIComponent(redirectPath));
|
||||
},
|
||||
(error) => {
|
||||
// 登录失败,刷新验证码
|
||||
getCaptcha();
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -586,18 +586,13 @@ const handleFileChange = async (event: Event) => {
|
||||
const file = target.files ? target.files[0] : null;
|
||||
if (file) {
|
||||
// 调用文件上传API
|
||||
try {
|
||||
const data = await FileAPI.uploadFile(file);
|
||||
// 更新用户信息
|
||||
await UserAPI.updateProfile({
|
||||
avatar: data.url,
|
||||
});
|
||||
// 更新用户头像
|
||||
userStore.userInfo.avatar = data.url;
|
||||
} catch (error) {
|
||||
console.error("头像上传失败:" + error);
|
||||
ElMessage.error("头像上传失败");
|
||||
}
|
||||
const data = await FileAPI.uploadFile(file);
|
||||
// 更新用户信息
|
||||
await UserAPI.updateProfile({
|
||||
avatar: data.url,
|
||||
});
|
||||
// 更新用户头像
|
||||
userStore.userInfo.avatar = data.url;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@ defineOptions({
|
||||
});
|
||||
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import NoticeAPI from "@/api/system/notice";
|
||||
import type { NoticeDetail, NoticeItem, NoticeQueryParams } from "@/types/api";
|
||||
|
||||
@@ -134,12 +133,9 @@ const noticeDetail = ref<NoticeDetail | null>(null);
|
||||
async function handleQuery() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await NoticeAPI.getMyNoticePage(queryParams);
|
||||
pageData.value = res.data;
|
||||
total.value = res.page?.total ?? 0;
|
||||
} catch (error) {
|
||||
ElMessage.error("获取通知列表失败");
|
||||
console.error("获取我的通知失败", error);
|
||||
const data = await NoticeAPI.getMyNoticePage(queryParams);
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -152,14 +148,9 @@ function handleResetQuery() {
|
||||
}
|
||||
|
||||
async function handleReadNotice(id: string) {
|
||||
try {
|
||||
const data = await NoticeAPI.getDetail(id);
|
||||
noticeDetail.value = data;
|
||||
noticeDialogVisible.value = true;
|
||||
} catch (error) {
|
||||
ElMessage.error("获取通知详情失败");
|
||||
console.error("获取通知详情失败", error);
|
||||
}
|
||||
const data = await NoticeAPI.getDetail(id);
|
||||
noticeDetail.value = data;
|
||||
noticeDialogVisible.value = true;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -185,9 +185,9 @@ const rules = reactive({
|
||||
function fetchData() {
|
||||
loading.value = true;
|
||||
ConfigAPI.getPage(queryParams)
|
||||
.then((res) => {
|
||||
pageData.value = res.data;
|
||||
total.value = res.page?.total ?? 0;
|
||||
.then((data) => {
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
|
||||
@@ -199,9 +199,9 @@ const computedRules = computed(() => {
|
||||
function fetchData() {
|
||||
loading.value = true;
|
||||
DictAPI.getDictItemPage(dictCode.value, queryParams)
|
||||
.then((res) => {
|
||||
tableData.value = res.data;
|
||||
total.value = res.page?.total ?? 0;
|
||||
.then((data) => {
|
||||
tableData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
|
||||
@@ -176,9 +176,9 @@ const computedRules = computed(() => {
|
||||
function fetchData() {
|
||||
loading.value = true;
|
||||
DictAPI.getPage(queryParams)
|
||||
.then((res) => {
|
||||
tableData.value = res.data;
|
||||
total.value = res.page?.total ?? 0;
|
||||
.then((data) => {
|
||||
tableData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
|
||||
@@ -90,9 +90,9 @@ const pageData = ref<LogItem[]>();
|
||||
function fetchData() {
|
||||
loading.value = true;
|
||||
LogAPI.getPage(queryParams)
|
||||
.then((res) => {
|
||||
pageData.value = res.data;
|
||||
total.value = res.page?.total ?? 0;
|
||||
.then((data) => {
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
|
||||
@@ -349,9 +349,9 @@ function handleQuery() {
|
||||
function fetchData() {
|
||||
loading.value = true;
|
||||
NoticeAPI.getPage(queryParams)
|
||||
.then((res) => {
|
||||
pageData.value = res.data;
|
||||
total.value = res.page?.total ?? 0;
|
||||
.then((data) => {
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
|
||||
@@ -47,6 +47,14 @@
|
||||
<el-table-column label="角色名称" prop="name" min-width="100" />
|
||||
<el-table-column label="角色编码" prop="code" width="150" />
|
||||
|
||||
<el-table-column label="数据权限" align="center" width="140">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getDataScopeTagType(scope.row.dataScope)">
|
||||
{{ getDataScopeLabel(scope.row.dataScope) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status === 1" type="success">正常</el-tag>
|
||||
@@ -103,7 +111,7 @@
|
||||
<el-dialog
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
width="500px"
|
||||
width="600px"
|
||||
@close="handleCloseDialog"
|
||||
>
|
||||
<el-form ref="roleFormRef" :model="formData" :rules="rules" label-width="100px">
|
||||
@@ -116,14 +124,28 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="数据权限" prop="dataScope">
|
||||
<el-select v-model="formData.dataScope">
|
||||
<el-select v-model="formData.dataScope" placeholder="请选择数据权限" style="width: 100%">
|
||||
<el-option :key="1" label="全部数据" :value="1" />
|
||||
<el-option :key="2" label="部门及子部门数据" :value="2" />
|
||||
<el-option :key="3" label="本部门数据" :value="3" />
|
||||
<el-option :key="4" label="本人数据" :value="4" />
|
||||
<el-option :key="5" label="自定义部门数据" :value="5" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 自定义部门选择 -->
|
||||
<el-form-item v-if="formData.dataScope === 5" label="选择部门" prop="deptIds">
|
||||
<el-tree-select
|
||||
v-model="formData.deptIds"
|
||||
:data="deptOptions"
|
||||
multiple
|
||||
:render-after-expand="false"
|
||||
check-strictly
|
||||
placeholder="请选择部门"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :value="1">正常</el-radio>
|
||||
@@ -220,6 +242,7 @@ import { DeviceEnum } from "@/enums/settings";
|
||||
import RoleAPI from "@/api/system/role";
|
||||
import type { RoleItem, RoleForm, RoleQueryParams } from "@/types/api";
|
||||
import MenuAPI from "@/api/system/menu";
|
||||
import DeptAPI from "@/api/system/dept";
|
||||
|
||||
defineOptions({
|
||||
name: "Role",
|
||||
@@ -245,6 +268,8 @@ const queryParams = reactive<RoleQueryParams>({
|
||||
const roleList = ref<RoleItem[]>();
|
||||
// 菜单权限下拉
|
||||
const menuPermOptions = ref<OptionItem[]>([]);
|
||||
// 部门下拉选项
|
||||
const deptOptions = ref<OptionItem[]>([]);
|
||||
|
||||
// 弹窗
|
||||
const dialog = reactive({
|
||||
@@ -264,6 +289,7 @@ const rules = reactive({
|
||||
name: [{ required: true, message: "请输入角色名称", trigger: "blur" }],
|
||||
code: [{ required: true, message: "请输入角色编码", trigger: "blur" }],
|
||||
dataScope: [{ required: true, message: "请选择数据权限", trigger: "blur" }],
|
||||
deptIds: [{ required: true, message: "请选择部门", trigger: "blur" }],
|
||||
status: [{ required: true, message: "请选择状态", trigger: "blur" }],
|
||||
});
|
||||
|
||||
@@ -280,13 +306,32 @@ const isExpanded = ref(true);
|
||||
|
||||
const parentChildLinked = ref(true);
|
||||
|
||||
// 数据权限标签
|
||||
const dataScopeOptions = [
|
||||
{ value: 1, label: "全部数据", type: "danger" },
|
||||
{ value: 2, label: "部门及子部门数据", type: "warning" },
|
||||
{ value: 3, label: "本部门数据", type: "primary" },
|
||||
{ value: 4, label: "本人数据", type: "info" },
|
||||
{ value: 5, label: "自定义部门数据", type: "success" },
|
||||
];
|
||||
|
||||
function getDataScopeLabel(value: number): string {
|
||||
const option = dataScopeOptions.find((item) => item.value === value);
|
||||
return option ? option.label : "未知";
|
||||
}
|
||||
|
||||
function getDataScopeTagType(value: number): string {
|
||||
const option = dataScopeOptions.find((item) => item.value === value);
|
||||
return option ? option.type : "info";
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
function fetchData() {
|
||||
loading.value = true;
|
||||
RoleAPI.getPage(queryParams)
|
||||
.then((res) => {
|
||||
roleList.value = res.data;
|
||||
total.value = res.page?.total ?? 0;
|
||||
.then((data) => {
|
||||
roleList.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
@@ -312,8 +357,13 @@ function handleSelectionChange(selection: any) {
|
||||
}
|
||||
|
||||
// 打开角色弹窗
|
||||
function handleOpenDialog(roleId?: string) {
|
||||
async function handleOpenDialog(roleId?: string) {
|
||||
dialog.visible = true;
|
||||
// 获取部门下拉选项
|
||||
if (deptOptions.value.length === 0) {
|
||||
deptOptions.value = await DeptAPI.getOptions();
|
||||
}
|
||||
|
||||
if (roleId) {
|
||||
dialog.title = "修改角色";
|
||||
RoleAPI.getFormData(roleId).then((data) => {
|
||||
@@ -328,10 +378,16 @@ function handleOpenDialog(roleId?: string) {
|
||||
function handleSubmit() {
|
||||
roleFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
// 如果不是自定义数据权限,清空部门ID列表
|
||||
const submitData = { ...formData };
|
||||
if (submitData.dataScope !== 5) {
|
||||
submitData.deptIds = undefined;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
const roleId = formData.id;
|
||||
if (roleId) {
|
||||
RoleAPI.update(roleId, formData)
|
||||
RoleAPI.update(roleId, submitData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
handleCloseDialog();
|
||||
@@ -339,7 +395,7 @@ function handleSubmit() {
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
RoleAPI.create(formData)
|
||||
RoleAPI.create(submitData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
handleCloseDialog();
|
||||
@@ -361,6 +417,8 @@ function handleCloseDialog() {
|
||||
formData.id = undefined;
|
||||
formData.sort = 1;
|
||||
formData.status = 1;
|
||||
formData.dataScope = undefined;
|
||||
formData.deptIds = undefined;
|
||||
}
|
||||
|
||||
// 删除角色
|
||||
|
||||
@@ -491,12 +491,12 @@ function resolvePlanLabel(planId?: number) {
|
||||
function fetchData() {
|
||||
loading.value = true;
|
||||
TenantAPI.getPage(queryParams)
|
||||
.then((res) => {
|
||||
pageData.value = res.data.map((item) => ({
|
||||
.then((data) => {
|
||||
pageData.value = data.list.map((item) => ({
|
||||
...item,
|
||||
planId: item.planId != null ? Number(item.planId) : undefined,
|
||||
}));
|
||||
total.value = res.page?.total ?? 0;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
@@ -881,7 +881,10 @@ function handleCloseDialog() {
|
||||
|
||||
// 提交租户表单(新增/编辑)
|
||||
const handleSubmit = useDebounceFn(async () => {
|
||||
const valid = await dataFormRef.value?.validate().catch(() => false);
|
||||
const valid = await dataFormRef.value?.validate().then(
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
if (!valid) return;
|
||||
|
||||
loading.value = true;
|
||||
@@ -922,8 +925,6 @@ const handleSubmit = useDebounceFn(async () => {
|
||||
|
||||
handleCloseDialog();
|
||||
handleResetQuery();
|
||||
} catch {
|
||||
ElMessage.error(formData.id != null && String(formData.id) !== "" ? "修改失败" : "新增失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -941,8 +942,8 @@ function handleDelete(tenantId?: string) {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(async () => {
|
||||
}).then(
|
||||
async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
await TenantAPI.deleteByIds(tenantIds);
|
||||
@@ -951,10 +952,11 @@ function handleDelete(tenantId?: string) {
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
},
|
||||
() => {
|
||||
// 用户取消
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 页面初始化
|
||||
@@ -965,15 +967,11 @@ onMounted(() => {
|
||||
|
||||
// 拉取租户套餐选项
|
||||
async function fetchPlanOptions() {
|
||||
try {
|
||||
const options = await TenantPlanAPI.getOptions();
|
||||
planOptions.value = options.map((item) => ({
|
||||
...item,
|
||||
value: item.value != null ? Number(item.value) : item.value,
|
||||
}));
|
||||
} catch {
|
||||
planOptions.value = [];
|
||||
}
|
||||
const options = await TenantPlanAPI.getOptions();
|
||||
planOptions.value = options.map((item) => ({
|
||||
...item,
|
||||
value: item.value != null ? Number(item.value) : item.value,
|
||||
}));
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -278,9 +278,9 @@ const menuParentChildLinked = ref(true);
|
||||
function fetchData() {
|
||||
loading.value = true;
|
||||
TenantPlanAPI.getPage(queryParams)
|
||||
.then((res) => {
|
||||
pageData.value = res.data;
|
||||
total.value = res.page?.total ?? 0;
|
||||
.then((data) => {
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
@@ -343,7 +343,10 @@ function handleCloseDialog() {
|
||||
|
||||
// 提交新增/编辑
|
||||
const handleSubmit = useDebounceFn(async () => {
|
||||
const valid = await dataFormRef.value?.validate().catch(() => false);
|
||||
const valid = await dataFormRef.value?.validate().then(
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
if (!valid) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
@@ -161,22 +161,17 @@ const handleUpload = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await UserAPI.import("1", importFormData.files[0].raw as File);
|
||||
if (result.code === ApiCodeEnum.SUCCESS && result.invalidCount === 0) {
|
||||
ElMessage.success("导入成功,导入数据:" + result.validCount + "条");
|
||||
emit("import-success");
|
||||
handleClose();
|
||||
} else {
|
||||
ElMessage.error("上传失败");
|
||||
resultVisible.value = true;
|
||||
resultData.value = result.messageList;
|
||||
invalidCount.value = result.invalidCount;
|
||||
validCount.value = result.validCount;
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
ElMessage.error("上传失败:" + error);
|
||||
const result = await UserAPI.import("1", importFormData.files[0].raw as File);
|
||||
if (result.code === ApiCodeEnum.SUCCESS && result.invalidCount === 0) {
|
||||
ElMessage.success("导入成功,导入数据:" + result.validCount + "条");
|
||||
emit("import-success");
|
||||
handleClose();
|
||||
} else {
|
||||
ElMessage.error("上传失败");
|
||||
resultVisible.value = true;
|
||||
resultData.value = result.messageList;
|
||||
invalidCount.value = result.invalidCount;
|
||||
validCount.value = result.validCount;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -351,12 +351,9 @@ const rules = reactive({
|
||||
async function fetchUserList(): Promise<void> {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await UserAPI.getPage(queryParams);
|
||||
userList.value = res.data;
|
||||
total.value = res.page?.total ?? 0;
|
||||
} catch (error) {
|
||||
ElMessage.error("获取用户列表失败");
|
||||
console.error("获取用户列表失败:", error);
|
||||
const data = await UserAPI.getPage(queryParams);
|
||||
userList.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -400,14 +397,14 @@ function handleResetPassword(row: UserItem): void {
|
||||
.then(({ value }) => {
|
||||
return UserAPI.resetPassword(row.id, value);
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success("密码重置成功");
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error !== "cancel") {
|
||||
ElMessage.error("密码重置失败");
|
||||
.then(
|
||||
() => {
|
||||
ElMessage.success("密码重置成功");
|
||||
},
|
||||
() => {
|
||||
// 用户取消
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 弹窗操作 ====================
|
||||
@@ -420,27 +417,17 @@ async function handleOpenDialog(id?: string): Promise<void> {
|
||||
dialogState.visible = true;
|
||||
|
||||
// 并行加载下拉选项数据
|
||||
try {
|
||||
[roleOptions.value, deptOptions.value] = await Promise.all([
|
||||
RoleAPI.getOptions(),
|
||||
DeptAPI.getOptions(),
|
||||
]);
|
||||
} catch (error) {
|
||||
ElMessage.error("加载选项数据失败");
|
||||
console.error("加载选项数据失败:", error);
|
||||
}
|
||||
[roleOptions.value, deptOptions.value] = await Promise.all([
|
||||
RoleAPI.getOptions(),
|
||||
DeptAPI.getOptions(),
|
||||
]);
|
||||
|
||||
// 编辑:加载用户数据
|
||||
if (id) {
|
||||
dialogState.title = "修改用户";
|
||||
dialogState.mode = DialogMode.EDIT;
|
||||
try {
|
||||
const data = await UserAPI.getFormData(id);
|
||||
Object.assign(formData, data);
|
||||
} catch (error) {
|
||||
ElMessage.error("加载用户数据失败");
|
||||
console.error("加载用户数据失败:", error);
|
||||
}
|
||||
const data = await UserAPI.getFormData(id);
|
||||
Object.assign(formData, data);
|
||||
} else {
|
||||
// 新增:设置默认值
|
||||
dialogState.title = "新增用户";
|
||||
@@ -466,7 +453,10 @@ function handleCloseDialog(): void {
|
||||
* 提交用户表单(防抖)
|
||||
*/
|
||||
const handleSubmit = useDebounceFn(async () => {
|
||||
const valid = await userFormRef.value?.validate().catch(() => false);
|
||||
const valid = await userFormRef.value?.validate().then(
|
||||
() => true,
|
||||
() => false
|
||||
);
|
||||
if (!valid) return;
|
||||
|
||||
const userId = formData.id;
|
||||
@@ -482,9 +472,6 @@ const handleSubmit = useDebounceFn(async () => {
|
||||
}
|
||||
handleCloseDialog();
|
||||
handleResetQuery();
|
||||
} catch (error) {
|
||||
ElMessage.error(userId ? "修改用户失败" : "新增用户失败");
|
||||
console.error("提交用户表单失败:", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -519,23 +506,21 @@ function handleDelete(id?: string): void {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(async () => {
|
||||
}).then(
|
||||
async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
await UserAPI.deleteByIds(userIds);
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
} catch (error) {
|
||||
ElMessage.error("删除失败");
|
||||
console.error("删除用户失败:", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
},
|
||||
() => {
|
||||
// 用户取消操作,无需处理
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 导入导出 ====================
|
||||
@@ -551,14 +536,9 @@ function handleOpenImportDialog(): void {
|
||||
* 导出用户列表
|
||||
*/
|
||||
async function handleExport(): Promise<void> {
|
||||
try {
|
||||
const response = await UserAPI.export(queryParams);
|
||||
downloadFile(response);
|
||||
ElMessage.success("导出成功");
|
||||
} catch (error) {
|
||||
ElMessage.error("导出失败");
|
||||
console.error("导出用户列表失败:", error);
|
||||
}
|
||||
const response = await UserAPI.export(queryParams);
|
||||
downloadFile(response);
|
||||
ElMessage.success("导出成功");
|
||||
}
|
||||
|
||||
// ==================== 生命周期 ====================
|
||||
|
||||
1
types/env.d.ts
vendored
1
types/env.d.ts
vendored
@@ -10,7 +10,6 @@ interface ImportMetaEnv {
|
||||
readonly VITE_APP_API_URL: string;
|
||||
readonly VITE_APP_TITLE?: string;
|
||||
readonly VITE_APP_TENANT_ENABLED?: string;
|
||||
readonly VITE_ENABLE_AI_ASSISTANT?: string;
|
||||
readonly VITE_MOCK_DEV_SERVER: boolean;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user