mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-06-07 00:47:19 +08:00
Merge branch 'main' into gitee
This commit is contained in:
commit
07dc4942ba
@ -1,4 +1,5 @@
|
|||||||
buttons:
|
buttons:
|
||||||
|
pureAccountSettings: Account Settings
|
||||||
pureLoginOut: LoginOut
|
pureLoginOut: LoginOut
|
||||||
pureLogin: Login
|
pureLogin: Login
|
||||||
pureSystemSet: Open ProjectConfig
|
pureSystemSet: Open ProjectConfig
|
||||||
@ -175,4 +176,4 @@ login:
|
|||||||
purePassWordRuleReg: The password format should be any combination of 8-18 digits
|
purePassWordRuleReg: The password format should be any combination of 8-18 digits
|
||||||
purePassWordSureReg: Please enter confirm password
|
purePassWordSureReg: Please enter confirm password
|
||||||
purePassWordDifferentReg: The two passwords do not match!
|
purePassWordDifferentReg: The two passwords do not match!
|
||||||
purePassWordUpdateReg: Password has been updated
|
purePassWordUpdateReg: Password has been updated
|
@ -1,4 +1,5 @@
|
|||||||
buttons:
|
buttons:
|
||||||
|
pureAccountSettings: 账户设置
|
||||||
pureLoginOut: 退出系统
|
pureLoginOut: 退出系统
|
||||||
pureLogin: 登录
|
pureLogin: 登录
|
||||||
pureSystemSet: 打开项目配置
|
pureSystemSet: 打开项目配置
|
||||||
@ -175,4 +176,4 @@ login:
|
|||||||
purePassWordRuleReg: 密码格式应为8-18位数字、字母、符号的任意两种组合
|
purePassWordRuleReg: 密码格式应为8-18位数字、字母、符号的任意两种组合
|
||||||
purePassWordSureReg: 请输入确认密码
|
purePassWordSureReg: 请输入确认密码
|
||||||
purePassWordDifferentReg: 两次密码不一致!
|
purePassWordDifferentReg: 两次密码不一致!
|
||||||
purePassWordUpdateReg: 修改密码成功
|
purePassWordUpdateReg: 修改密码成功
|
@ -10,7 +10,9 @@ export default defineFakeRoute([
|
|||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
|
avatar: "https://avatars.githubusercontent.com/u/44761321",
|
||||||
username: "admin",
|
username: "admin",
|
||||||
|
nickname: "小铭",
|
||||||
// 一个用户可能有多个角色
|
// 一个用户可能有多个角色
|
||||||
roles: ["admin"],
|
roles: ["admin"],
|
||||||
accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
|
accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
|
||||||
@ -22,8 +24,9 @@ export default defineFakeRoute([
|
|||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
|
avatar: "https://avatars.githubusercontent.com/u/52823142",
|
||||||
username: "common",
|
username: "common",
|
||||||
// 一个用户可能有多个角色
|
nickname: "小林",
|
||||||
roles: ["common"],
|
roles: ["common"],
|
||||||
accessToken: "eyJhbGciOiJIUzUxMiJ9.common",
|
accessToken: "eyJhbGciOiJIUzUxMiJ9.common",
|
||||||
refreshToken: "eyJhbGciOiJIUzUxMiJ9.commonRefresh",
|
refreshToken: "eyJhbGciOiJIUzUxMiJ9.commonRefresh",
|
||||||
|
59
mock/mine.ts
Normal file
59
mock/mine.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { defineFakeRoute } from "vite-plugin-fake-server/client";
|
||||||
|
import { faker } from "@faker-js/faker/locale/zh_CN";
|
||||||
|
|
||||||
|
export default defineFakeRoute([
|
||||||
|
// 账户设置-个人信息
|
||||||
|
{
|
||||||
|
url: "/mine",
|
||||||
|
method: "get",
|
||||||
|
response: () => {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
avatar: "https://avatars.githubusercontent.com/u/44761321",
|
||||||
|
username: "admin",
|
||||||
|
nickname: "小铭",
|
||||||
|
email: "pureadmin@163.com",
|
||||||
|
phone: "15888886789",
|
||||||
|
description: "一个热爱开源的前端工程师"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 账户设置-个人安全日志
|
||||||
|
{
|
||||||
|
url: "/mine-logs",
|
||||||
|
method: "get",
|
||||||
|
response: () => {
|
||||||
|
let list = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
ip: faker.internet.ipv4(),
|
||||||
|
address: "中国河南省信阳市",
|
||||||
|
system: "macOS",
|
||||||
|
browser: "Chrome",
|
||||||
|
summary: "账户登录", // 详情
|
||||||
|
operatingTime: new Date() // 时间
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
ip: faker.internet.ipv4(),
|
||||||
|
address: "中国广东省深圳市",
|
||||||
|
system: "Windows",
|
||||||
|
browser: "Firefox",
|
||||||
|
summary: "绑定了手机号码",
|
||||||
|
operatingTime: new Date().setDate(new Date().getDate() - 1)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
list,
|
||||||
|
total: list.length, // 总条目数
|
||||||
|
pageSize: 10, // 每页显示条目个数
|
||||||
|
currentPage: 1 // 当前页数
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
@ -9,9 +9,9 @@ export default defineFakeRoute([
|
|||||||
response: ({ body }) => {
|
response: ({ body }) => {
|
||||||
let list = [
|
let list = [
|
||||||
{
|
{
|
||||||
username: "admin",
|
|
||||||
nickname: "admin",
|
|
||||||
avatar: "https://avatars.githubusercontent.com/u/44761321",
|
avatar: "https://avatars.githubusercontent.com/u/44761321",
|
||||||
|
username: "admin",
|
||||||
|
nickname: "小铭",
|
||||||
phone: "15888886789",
|
phone: "15888886789",
|
||||||
email: faker.internet.email(),
|
email: faker.internet.email(),
|
||||||
sex: 0,
|
sex: 0,
|
||||||
@ -27,9 +27,9 @@ export default defineFakeRoute([
|
|||||||
createTime: 1605456000000
|
createTime: 1605456000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
username: "common",
|
|
||||||
nickname: "common",
|
|
||||||
avatar: "https://avatars.githubusercontent.com/u/52823142",
|
avatar: "https://avatars.githubusercontent.com/u/52823142",
|
||||||
|
username: "common",
|
||||||
|
nickname: "小林",
|
||||||
phone: "18288882345",
|
phone: "18288882345",
|
||||||
email: faker.internet.email(),
|
email: faker.internet.email(),
|
||||||
sex: 1,
|
sex: 1,
|
||||||
@ -397,6 +397,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -420,6 +421,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -443,6 +445,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -466,6 +469,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -489,6 +493,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -512,6 +517,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -535,6 +541,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -558,6 +565,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -581,6 +589,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -604,6 +613,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -627,6 +637,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -651,6 +662,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -674,6 +686,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -697,6 +710,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -720,6 +734,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -743,6 +758,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -766,6 +782,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -790,6 +807,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -813,6 +831,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -836,6 +855,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -859,6 +879,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -882,6 +903,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -906,6 +928,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -929,6 +952,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -952,6 +976,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -975,6 +1000,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -998,6 +1024,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -1022,6 +1049,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -1045,6 +1073,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -1068,6 +1097,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: false,
|
showLink: false,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -1091,6 +1121,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: false,
|
showLink: false,
|
||||||
showParent: false
|
showParent: false
|
||||||
}
|
}
|
||||||
@ -1472,6 +1503,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -1495,6 +1527,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -1518,6 +1551,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -1541,6 +1575,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
},
|
},
|
||||||
@ -1564,6 +1599,7 @@ export default defineFakeRoute([
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
}
|
}
|
||||||
|
28
package.json
28
package.json
@ -51,15 +51,15 @@
|
|||||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||||
"@howdyjs/mouse-menu": "^2.1.3",
|
"@howdyjs/mouse-menu": "^2.1.3",
|
||||||
"@infectoone/vue-ganttastic": "^2.3.2",
|
"@infectoone/vue-ganttastic": "^2.3.2",
|
||||||
"@logicflow/core": "^1.2.23",
|
"@logicflow/core": "^1.2.25",
|
||||||
"@logicflow/extension": "^1.2.23",
|
"@logicflow/extension": "^1.2.25",
|
||||||
"@pureadmin/descriptions": "^1.2.1",
|
"@pureadmin/descriptions": "^1.2.1",
|
||||||
"@pureadmin/table": "^3.1.2",
|
"@pureadmin/table": "^3.1.2",
|
||||||
"@pureadmin/utils": "^2.4.7",
|
"@pureadmin/utils": "^2.4.7",
|
||||||
"@vue-flow/background": "^1.3.0",
|
"@vue-flow/background": "^1.3.0",
|
||||||
"@vue-flow/core": "^1.33.5",
|
"@vue-flow/core": "^1.33.5",
|
||||||
"@vue-office/docx": "^1.6.0",
|
"@vue-office/docx": "^1.6.0",
|
||||||
"@vue-office/excel": "^1.7.3",
|
"@vue-office/excel": "^1.7.6",
|
||||||
"@vueuse/core": "^10.9.0",
|
"@vueuse/core": "^10.9.0",
|
||||||
"@vueuse/motion": "^2.1.0",
|
"@vueuse/motion": "^2.1.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
@ -95,7 +95,7 @@
|
|||||||
"v3-infinite-loading": "^1.3.1",
|
"v3-infinite-loading": "^1.3.1",
|
||||||
"version-rocket": "^1.7.1",
|
"version-rocket": "^1.7.1",
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
"vue-i18n": "^9.10.2",
|
"vue-i18n": "^9.11.0",
|
||||||
"vue-json-pretty": "^2.4.0",
|
"vue-json-pretty": "^2.4.0",
|
||||||
"vue-pdf-embed": "^2.0.3",
|
"vue-pdf-embed": "^2.0.3",
|
||||||
"vue-router": "^4.3.0",
|
"vue-router": "^4.3.0",
|
||||||
@ -107,8 +107,8 @@
|
|||||||
"vue3-puzzle-vcode": "^1.1.7",
|
"vue3-puzzle-vcode": "^1.1.7",
|
||||||
"vuedraggable": "^4.1.0",
|
"vuedraggable": "^4.1.0",
|
||||||
"vxe-table": "^4.5.21",
|
"vxe-table": "^4.5.21",
|
||||||
"wavesurfer.js": "^7.7.5",
|
"wavesurfer.js": "^7.7.8",
|
||||||
"xgplayer": "^3.0.15",
|
"xgplayer": "^3.0.16",
|
||||||
"xlsx": "^0.18.5"
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -123,16 +123,16 @@
|
|||||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||||
"@pureadmin/theme": "^3.2.0",
|
"@pureadmin/theme": "^3.2.0",
|
||||||
"@types/dagre": "^0.7.52",
|
"@types/dagre": "^0.7.52",
|
||||||
"@types/gradient-string": "^1.1.5",
|
"@types/gradient-string": "^1.1.6",
|
||||||
"@types/intro.js": "^5.1.5",
|
"@types/intro.js": "^5.1.5",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/node": "^20.12.2",
|
"@types/node": "^20.12.5",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@types/qrcode": "^1.5.5",
|
"@types/qrcode": "^1.5.5",
|
||||||
"@types/qs": "^6.9.14",
|
"@types/qs": "^6.9.14",
|
||||||
"@types/sortablejs": "^1.15.8",
|
"@types/sortablejs": "^1.15.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
||||||
"@typescript-eslint/parser": "^7.4.0",
|
"@typescript-eslint/parser": "^7.5.0",
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
@ -155,16 +155,16 @@
|
|||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"sass": "^1.72.0",
|
"sass": "^1.74.1",
|
||||||
"stylelint": "^16.3.1",
|
"stylelint": "^16.3.1",
|
||||||
"stylelint-config-recess-order": "^5.0.0",
|
"stylelint-config-recess-order": "^5.0.0",
|
||||||
"stylelint-config-recommended-vue": "^1.5.0",
|
"stylelint-config-recommended-vue": "^1.5.0",
|
||||||
"stylelint-config-standard-scss": "^13.0.0",
|
"stylelint-config-standard-scss": "^13.1.0",
|
||||||
"stylelint-prettier": "^5.0.0",
|
"stylelint-prettier": "^5.0.0",
|
||||||
"svgo": "^3.2.0",
|
"svgo": "^3.2.0",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"typescript": "^5.4.3",
|
"typescript": "^5.4.4",
|
||||||
"vite": "^5.2.7",
|
"vite": "^5.2.8",
|
||||||
"vite-plugin-cdn-import": "^0.3.5",
|
"vite-plugin-cdn-import": "^0.3.5",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-fake-server": "^2.1.1",
|
"vite-plugin-fake-server": "^2.1.1",
|
||||||
|
873
pnpm-lock.yaml
generated
873
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -28,12 +28,56 @@ export type RefreshTokenResult = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UserInfo = {
|
||||||
|
/** 头像 */
|
||||||
|
avatar: string;
|
||||||
|
/** 用户名 */
|
||||||
|
username: string;
|
||||||
|
/** 昵称 */
|
||||||
|
nickname: string;
|
||||||
|
/** 邮箱 */
|
||||||
|
email: string;
|
||||||
|
/** 联系电话 */
|
||||||
|
phone: string;
|
||||||
|
/** 简介 */
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserInfoResult = {
|
||||||
|
success: boolean;
|
||||||
|
data: UserInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ResultTable = {
|
||||||
|
success: boolean;
|
||||||
|
data?: {
|
||||||
|
/** 列表数据 */
|
||||||
|
list: Array<any>;
|
||||||
|
/** 总条目数 */
|
||||||
|
total?: number;
|
||||||
|
/** 每页显示条目个数 */
|
||||||
|
pageSize?: number;
|
||||||
|
/** 当前页数 */
|
||||||
|
currentPage?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/** 登录 */
|
/** 登录 */
|
||||||
export const getLogin = (data?: object) => {
|
export const getLogin = (data?: object) => {
|
||||||
return http.request<UserResult>("post", "/login", { data });
|
return http.request<UserResult>("post", "/login", { data });
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 刷新token */
|
/** 刷新`token` */
|
||||||
export const refreshTokenApi = (data?: object) => {
|
export const refreshTokenApi = (data?: object) => {
|
||||||
return http.request<RefreshTokenResult>("post", "/refresh-token", { data });
|
return http.request<RefreshTokenResult>("post", "/refresh-token", { data });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 账户设置-个人信息 */
|
||||||
|
export const getMine = (data?: object) => {
|
||||||
|
return http.request<UserInfoResult>("get", "/mine", { data });
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 账户设置-个人安全日志 */
|
||||||
|
export const getMineLogs = (data?: object) => {
|
||||||
|
return http.request<ResultTable>("get", "/mine-logs", { data });
|
||||||
|
};
|
||||||
|
7
src/components/ReCropperPreview/index.ts
Normal file
7
src/components/ReCropperPreview/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import reCropperPreview from "./src/index.vue";
|
||||||
|
import { withInstall } from "@pureadmin/utils";
|
||||||
|
|
||||||
|
/** 图片裁剪预览组件 */
|
||||||
|
export const ReCropperPreview = withInstall(reCropperPreview);
|
||||||
|
|
||||||
|
export default ReCropperPreview;
|
@ -3,6 +3,10 @@ import { ref } from "vue";
|
|||||||
import ReCropper from "@/components/ReCropper";
|
import ReCropper from "@/components/ReCropper";
|
||||||
import { formatBytes } from "@pureadmin/utils";
|
import { formatBytes } from "@pureadmin/utils";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "ReCropperPreview"
|
||||||
|
});
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
imgSrc: String
|
imgSrc: String
|
||||||
});
|
});
|
@ -11,6 +11,10 @@ import { isFunction } from "@pureadmin/utils";
|
|||||||
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
|
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
|
||||||
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
|
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "ReDialog"
|
||||||
|
});
|
||||||
|
|
||||||
const fullscreen = ref(false);
|
const fullscreen = ref(false);
|
||||||
|
|
||||||
const footerButtons = computed(() => {
|
const footerButtons = computed(() => {
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
import flippers from "./filpper";
|
import flippers from "./filpper";
|
||||||
import { ref, unref, nextTick, onUnmounted } from "vue";
|
import { ref, unref, nextTick, onUnmounted } from "vue";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "ReFlop"
|
||||||
|
});
|
||||||
|
|
||||||
const timer = ref(null);
|
const timer = ref(null);
|
||||||
const flipObjs = ref([]);
|
const flipObjs = ref([]);
|
||||||
|
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
import { h, onMounted, ref, useSlots } from "vue";
|
import { h, onMounted, ref, useSlots } from "vue";
|
||||||
import { type TippyOptions, useTippy } from "vue-tippy";
|
import { type TippyOptions, useTippy } from "vue-tippy";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "ReText"
|
||||||
|
});
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// 行数
|
// 行数
|
||||||
lineClamp: {
|
lineClamp: {
|
||||||
|
@ -8,9 +8,11 @@ import Breadcrumb from "./sidebar/breadCrumb.vue";
|
|||||||
import topCollapse from "./sidebar/topCollapse.vue";
|
import topCollapse from "./sidebar/topCollapse.vue";
|
||||||
import { useTranslationLang } from "../hooks/useTranslationLang";
|
import { useTranslationLang } from "../hooks/useTranslationLang";
|
||||||
import globalization from "@/assets/svg/globalization.svg?component";
|
import globalization from "@/assets/svg/globalization.svg?component";
|
||||||
|
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
|
||||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
||||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
import Setting from "@iconify-icons/ri/settings-3-line";
|
||||||
import Check from "@iconify-icons/ep/check";
|
import Check from "@iconify-icons/ep/check";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
layout,
|
layout,
|
||||||
device,
|
device,
|
||||||
@ -21,6 +23,7 @@ const {
|
|||||||
userAvatar,
|
userAvatar,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
toggleSideBar,
|
toggleSideBar,
|
||||||
|
toAccountSettings,
|
||||||
getDropdownItemStyle,
|
getDropdownItemStyle,
|
||||||
getDropdownItemClass
|
getDropdownItemClass
|
||||||
} = useNav();
|
} = useNav();
|
||||||
@ -91,6 +94,13 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
|
|||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu class="logout">
|
<el-dropdown-menu class="logout">
|
||||||
|
<el-dropdown-item @click="toAccountSettings">
|
||||||
|
<IconifyIconOffline
|
||||||
|
:icon="AccountSettingsIcon"
|
||||||
|
style="margin: 5px"
|
||||||
|
/>
|
||||||
|
{{ t("buttons.pureAccountSettings") }}
|
||||||
|
</el-dropdown-item>
|
||||||
<el-dropdown-item @click="logout">
|
<el-dropdown-item @click="logout">
|
||||||
<IconifyIconOffline
|
<IconifyIconOffline
|
||||||
:icon="LogoutCircleRLine"
|
:icon="LogoutCircleRLine"
|
||||||
@ -177,7 +187,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logout {
|
.logout {
|
||||||
max-width: 120px;
|
width: 120px;
|
||||||
|
|
||||||
::v-deep(.el-dropdown-menu__item) {
|
::v-deep(.el-dropdown-menu__item) {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
@ -9,6 +9,7 @@ import { useNav } from "@/layout/hooks/useNav";
|
|||||||
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||||
import globalization from "@/assets/svg/globalization.svg?component";
|
import globalization from "@/assets/svg/globalization.svg?component";
|
||||||
|
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
|
||||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
||||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
import Setting from "@iconify-icons/ri/settings-3-line";
|
||||||
import Check from "@iconify-icons/ep/check";
|
import Check from "@iconify-icons/ep/check";
|
||||||
@ -26,6 +27,7 @@ const {
|
|||||||
username,
|
username,
|
||||||
userAvatar,
|
userAvatar,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
|
toAccountSettings,
|
||||||
getDropdownItemStyle,
|
getDropdownItemStyle,
|
||||||
getDropdownItemClass
|
getDropdownItemClass
|
||||||
} = useNav();
|
} = useNav();
|
||||||
@ -107,6 +109,13 @@ nextTick(() => {
|
|||||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
|
<el-dropdown-item @click="toAccountSettings">
|
||||||
|
<IconifyIconOffline
|
||||||
|
:icon="AccountSettingsIcon"
|
||||||
|
style="margin: 5px"
|
||||||
|
/>
|
||||||
|
{{ t("buttons.pureAccountSettings") }}
|
||||||
|
</el-dropdown-item>
|
||||||
<el-dropdown-menu class="logout">
|
<el-dropdown-menu class="logout">
|
||||||
<el-dropdown-item @click="logout">
|
<el-dropdown-item @click="logout">
|
||||||
<IconifyIconOffline
|
<IconifyIconOffline
|
||||||
@ -151,7 +160,7 @@ nextTick(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logout {
|
.logout {
|
||||||
max-width: 120px;
|
width: 120px;
|
||||||
|
|
||||||
::v-deep(.el-dropdown-menu__item) {
|
::v-deep(.el-dropdown-menu__item) {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
@ -12,6 +12,7 @@ import { getParentPaths, findRouteByPath } from "@/router/utils";
|
|||||||
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
import { useTranslationLang } from "../../hooks/useTranslationLang";
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||||
import globalization from "@/assets/svg/globalization.svg?component";
|
import globalization from "@/assets/svg/globalization.svg?component";
|
||||||
|
import AccountSettingsIcon from "@iconify-icons/ri/user-settings-line";
|
||||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
||||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
import Setting from "@iconify-icons/ri/settings-3-line";
|
||||||
import Check from "@iconify-icons/ep/check";
|
import Check from "@iconify-icons/ep/check";
|
||||||
@ -30,6 +31,7 @@ const {
|
|||||||
userAvatar,
|
userAvatar,
|
||||||
getDivStyle,
|
getDivStyle,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
|
toAccountSettings,
|
||||||
getDropdownItemStyle,
|
getDropdownItemStyle,
|
||||||
getDropdownItemClass
|
getDropdownItemClass
|
||||||
} = useNav();
|
} = useNav();
|
||||||
@ -140,6 +142,13 @@ watch(
|
|||||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||||
</span>
|
</span>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
|
<el-dropdown-item @click="toAccountSettings">
|
||||||
|
<IconifyIconOffline
|
||||||
|
:icon="AccountSettingsIcon"
|
||||||
|
style="margin: 5px"
|
||||||
|
/>
|
||||||
|
{{ t("buttons.pureAccountSettings") }}
|
||||||
|
</el-dropdown-item>
|
||||||
<el-dropdown-menu class="logout">
|
<el-dropdown-menu class="logout">
|
||||||
<el-dropdown-item @click="logout">
|
<el-dropdown-item @click="logout">
|
||||||
<IconifyIconOffline
|
<IconifyIconOffline
|
||||||
@ -184,7 +193,7 @@ watch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logout {
|
.logout {
|
||||||
max-width: 120px;
|
width: 120px;
|
||||||
|
|
||||||
::v-deep(.el-dropdown-menu__item) {
|
::v-deep(.el-dropdown-menu__item) {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
@ -90,6 +90,10 @@
|
|||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fixed-tag {
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import { onClickOutside } from "@vueuse/core";
|
|||||||
import { handleAliveRoute, getTopMenu } from "@/router/utils";
|
import { handleAliveRoute, getTopMenu } from "@/router/utils";
|
||||||
import { useSettingStoreHook } from "@/store/modules/settings";
|
import { useSettingStoreHook } from "@/store/modules/settings";
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
||||||
|
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||||
import { ref, watch, unref, toRaw, nextTick, onBeforeUnmount } from "vue";
|
import { ref, watch, unref, toRaw, nextTick, onBeforeUnmount } from "vue";
|
||||||
import {
|
import {
|
||||||
delay,
|
delay,
|
||||||
@ -59,6 +60,10 @@ const contextmenuRef = ref();
|
|||||||
const isShowArrow = ref(false);
|
const isShowArrow = ref(false);
|
||||||
const topPath = getTopMenu()?.path;
|
const topPath = getTopMenu()?.path;
|
||||||
const { VITE_HIDE_HOME } = import.meta.env;
|
const { VITE_HIDE_HOME } = import.meta.env;
|
||||||
|
const fixedTags = [
|
||||||
|
...routerArrays,
|
||||||
|
...usePermissionStoreHook().flatteningRoutes.filter(v => v?.meta?.fixedTag)
|
||||||
|
];
|
||||||
|
|
||||||
const dynamicTagView = async () => {
|
const dynamicTagView = async () => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
@ -228,10 +233,13 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
|||||||
other?: boolean
|
other?: boolean
|
||||||
): void => {
|
): void => {
|
||||||
if (other) {
|
if (other) {
|
||||||
useMultiTagsStoreHook().handleTags("equal", [
|
useMultiTagsStoreHook().handleTags(
|
||||||
VITE_HIDE_HOME === "false" ? routerArrays[0] : toRaw(getTopMenu()),
|
"equal",
|
||||||
obj
|
[
|
||||||
]);
|
VITE_HIDE_HOME === "false" ? fixedTags : toRaw(getTopMenu()),
|
||||||
|
obj
|
||||||
|
].flat()
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
useMultiTagsStoreHook().handleTags("splice", "", {
|
useMultiTagsStoreHook().handleTags("splice", "", {
|
||||||
startIndex,
|
startIndex,
|
||||||
@ -244,7 +252,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
|
|||||||
if (tag === "other") {
|
if (tag === "other") {
|
||||||
spliceRoute(1, 1, true);
|
spliceRoute(1, 1, true);
|
||||||
} else if (tag === "left") {
|
} else if (tag === "left") {
|
||||||
spliceRoute(1, valueIndex - 1);
|
spliceRoute(fixedTags.length, valueIndex - 1, true);
|
||||||
} else if (tag === "right") {
|
} else if (tag === "right") {
|
||||||
spliceRoute(valueIndex + 1, multiTags.value.length);
|
spliceRoute(valueIndex + 1, multiTags.value.length);
|
||||||
} else {
|
} else {
|
||||||
@ -321,10 +329,11 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
|
|||||||
case 5:
|
case 5:
|
||||||
// 关闭全部标签页
|
// 关闭全部标签页
|
||||||
useMultiTagsStoreHook().handleTags("splice", "", {
|
useMultiTagsStoreHook().handleTags("splice", "", {
|
||||||
startIndex: 1,
|
startIndex: fixedTags.length,
|
||||||
length: multiTags.value.length
|
length: multiTags.value.length
|
||||||
});
|
});
|
||||||
router.push(topPath);
|
router.push(topPath);
|
||||||
|
// router.push(fixedTags[fixedTags.length - 1]?.path);
|
||||||
handleAliveRoute(route as ToRouteType);
|
handleAliveRoute(route as ToRouteType);
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
@ -363,10 +372,14 @@ function showMenus(value: boolean) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function disabledMenus(value: boolean) {
|
function disabledMenus(value: boolean, fixedTag = false) {
|
||||||
Array.of(1, 2, 3, 4, 5).forEach(v => {
|
Array.of(1, 2, 3, 4, 5).forEach(v => {
|
||||||
tagsViews[v].disabled = value;
|
tagsViews[v].disabled = value;
|
||||||
});
|
});
|
||||||
|
if (fixedTag) {
|
||||||
|
tagsViews[2].show = false;
|
||||||
|
tagsViews[2].disabled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是顶级菜单,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
|
/** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是顶级菜单,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
|
||||||
@ -383,6 +396,13 @@ function showMenuModel(
|
|||||||
} else {
|
} else {
|
||||||
currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
|
currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
|
||||||
}
|
}
|
||||||
|
function fixedTagDisabled() {
|
||||||
|
if (allRoute[currentIndex]?.meta?.fixedTag) {
|
||||||
|
Array.of(1, 2, 3, 4, 5).forEach(v => {
|
||||||
|
tagsViews[v].disabled = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
showMenus(true);
|
showMenus(true);
|
||||||
|
|
||||||
@ -401,6 +421,7 @@ function showMenuModel(
|
|||||||
tagsViews[v].disabled = false;
|
tagsViews[v].disabled = false;
|
||||||
});
|
});
|
||||||
tagsViews[2].disabled = true;
|
tagsViews[2].disabled = true;
|
||||||
|
fixedTagDisabled();
|
||||||
} else if (currentIndex === 1 && routeLength === 2) {
|
} else if (currentIndex === 1 && routeLength === 2) {
|
||||||
disabledMenus(false);
|
disabledMenus(false);
|
||||||
// 左侧的菜单是顶级菜单,右侧不存在别的菜单
|
// 左侧的菜单是顶级菜单,右侧不存在别的菜单
|
||||||
@ -408,6 +429,7 @@ function showMenuModel(
|
|||||||
tagsViews[v].show = false;
|
tagsViews[v].show = false;
|
||||||
tagsViews[v].disabled = true;
|
tagsViews[v].disabled = true;
|
||||||
});
|
});
|
||||||
|
fixedTagDisabled();
|
||||||
} else if (routeLength - 1 === currentIndex && currentIndex !== 0) {
|
} else if (routeLength - 1 === currentIndex && currentIndex !== 0) {
|
||||||
// 当前路由是所有路由中的最后一个
|
// 当前路由是所有路由中的最后一个
|
||||||
tagsViews[3].show = false;
|
tagsViews[3].show = false;
|
||||||
@ -415,18 +437,24 @@ function showMenuModel(
|
|||||||
tagsViews[v].disabled = false;
|
tagsViews[v].disabled = false;
|
||||||
});
|
});
|
||||||
tagsViews[3].disabled = true;
|
tagsViews[3].disabled = true;
|
||||||
|
if (allRoute[currentIndex - 1]?.meta?.fixedTag) {
|
||||||
|
tagsViews[2].show = false;
|
||||||
|
tagsViews[2].disabled = true;
|
||||||
|
}
|
||||||
|
fixedTagDisabled();
|
||||||
} else if (currentIndex === 0 || currentPath === `/redirect${topPath}`) {
|
} else if (currentIndex === 0 || currentPath === `/redirect${topPath}`) {
|
||||||
// 当前路由为顶级菜单
|
// 当前路由为顶级菜单
|
||||||
disabledMenus(true);
|
disabledMenus(true);
|
||||||
} else {
|
} else {
|
||||||
disabledMenus(false);
|
disabledMenus(false, allRoute[currentIndex - 1]?.meta?.fixedTag);
|
||||||
|
fixedTagDisabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openMenu(tag, e) {
|
function openMenu(tag, e) {
|
||||||
closeMenu();
|
closeMenu();
|
||||||
if (tag.path === topPath) {
|
if (tag.path === topPath || tag?.meta?.fixedTag) {
|
||||||
// 右键菜单为顶级菜单,只显示刷新
|
// 右键菜单为顶级菜单或拥有 fixedTag 属性,只显示刷新
|
||||||
showMenus(false);
|
showMenus(false);
|
||||||
tagsViews[0].show = true;
|
tagsViews[0].show = true;
|
||||||
} else if (route.path !== tag.path && route.name !== tag.name) {
|
} else if (route.path !== tag.path && route.name !== tag.name) {
|
||||||
@ -485,7 +513,6 @@ function tagOnClick(item) {
|
|||||||
} else {
|
} else {
|
||||||
router.push({ path });
|
router.push({ path });
|
||||||
}
|
}
|
||||||
// showMenuModel(item?.path, item?.query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickOutside(contextmenuRef, closeMenu, {
|
onClickOutside(contextmenuRef, closeMenu, {
|
||||||
@ -549,7 +576,11 @@ onBeforeUnmount(() => {
|
|||||||
v-for="(item, index) in multiTags"
|
v-for="(item, index) in multiTags"
|
||||||
:ref="'dynamic' + index"
|
:ref="'dynamic' + index"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="['scroll-item is-closable', linkIsActive(item)]"
|
:class="[
|
||||||
|
'scroll-item is-closable',
|
||||||
|
linkIsActive(item),
|
||||||
|
!isAllEmpty(item?.meta?.fixedTag) && 'fixed-tag'
|
||||||
|
]"
|
||||||
@contextmenu.prevent="openMenu(item, $event)"
|
@contextmenu.prevent="openMenu(item, $event)"
|
||||||
@mouseenter.prevent="onMouseenter(index)"
|
@mouseenter.prevent="onMouseenter(index)"
|
||||||
@mouseleave.prevent="onMouseleave(index)"
|
@mouseleave.prevent="onMouseleave(index)"
|
||||||
@ -562,8 +593,10 @@ onBeforeUnmount(() => {
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="
|
v-if="
|
||||||
iconIsActive(item, index) ||
|
isAllEmpty(item?.meta?.fixedTag)
|
||||||
(index === activeIndex && index !== 0)
|
? iconIsActive(item, index) ||
|
||||||
|
(index === activeIndex && index !== 0)
|
||||||
|
: false
|
||||||
"
|
"
|
||||||
class="el-icon-close"
|
class="el-icon-close"
|
||||||
@click.stop="deleteMenu(item)"
|
@click.stop="deleteMenu(item)"
|
||||||
|
@ -2,16 +2,16 @@ import { storeToRefs } from "pinia";
|
|||||||
import { getConfig } from "@/config";
|
import { getConfig } from "@/config";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { emitter } from "@/utils/mitt";
|
import { emitter } from "@/utils/mitt";
|
||||||
import userAvatar from "@/assets/user.jpg";
|
import Avatar from "@/assets/user.jpg";
|
||||||
import { getTopMenu } from "@/router/utils";
|
import { getTopMenu } from "@/router/utils";
|
||||||
import { useFullscreen } from "@vueuse/core";
|
import { useFullscreen } from "@vueuse/core";
|
||||||
import { useGlobal } from "@pureadmin/utils";
|
|
||||||
import type { routeMetaType } from "../types";
|
import type { routeMetaType } from "../types";
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
import { transformI18n } from "@/plugins/i18n";
|
||||||
import { router, remainingPaths } from "@/router";
|
import { router, remainingPaths } from "@/router";
|
||||||
import { computed, type CSSProperties } from "vue";
|
import { computed, type CSSProperties } from "vue";
|
||||||
import { useAppStoreHook } from "@/store/modules/app";
|
import { useAppStoreHook } from "@/store/modules/app";
|
||||||
import { useUserStoreHook } from "@/store/modules/user";
|
import { useUserStoreHook } from "@/store/modules/user";
|
||||||
|
import { useGlobal, isAllEmpty } from "@pureadmin/utils";
|
||||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||||
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
|
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
|
||||||
@ -37,9 +37,18 @@ export function useNav() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 用户名 */
|
/** 头像(如果头像为空则使用 src/assets/user.jpg ) */
|
||||||
|
const userAvatar = computed(() => {
|
||||||
|
return isAllEmpty(useUserStoreHook()?.avatar)
|
||||||
|
? Avatar
|
||||||
|
: useUserStoreHook()?.avatar;
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 昵称(如果昵称为空则显示用户名) */
|
||||||
const username = computed(() => {
|
const username = computed(() => {
|
||||||
return useUserStoreHook()?.username;
|
return isAllEmpty(useUserStoreHook()?.nickname)
|
||||||
|
? useUserStoreHook()?.username
|
||||||
|
: useUserStoreHook()?.nickname;
|
||||||
});
|
});
|
||||||
|
|
||||||
/** 设置国际化选中后的样式 */
|
/** 设置国际化选中后的样式 */
|
||||||
@ -99,6 +108,10 @@ export function useNav() {
|
|||||||
emitter.emit("openPanel");
|
emitter.emit("openPanel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toAccountSettings() {
|
||||||
|
router.push({ name: "AccountSettings" });
|
||||||
|
}
|
||||||
|
|
||||||
function toggleSideBar() {
|
function toggleSideBar() {
|
||||||
pureApp.toggleSideBar();
|
pureApp.toggleSideBar();
|
||||||
}
|
}
|
||||||
@ -159,6 +172,7 @@ export function useNav() {
|
|||||||
userAvatar,
|
userAvatar,
|
||||||
avatarsStyle,
|
avatarsStyle,
|
||||||
tooltipEffect,
|
tooltipEffect,
|
||||||
|
toAccountSettings,
|
||||||
getDropdownItemStyle,
|
getDropdownItemStyle,
|
||||||
getDropdownItemClass
|
getDropdownItemClass
|
||||||
};
|
};
|
||||||
|
@ -38,5 +38,15 @@ export default [
|
|||||||
showLink: false,
|
showLink: false,
|
||||||
rank: 103
|
rank: 103
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/account-settings",
|
||||||
|
name: "AccountSettings",
|
||||||
|
component: () => import("@/views/account-settings/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: $t("buttons.pureAccountSettings"),
|
||||||
|
showLink: false,
|
||||||
|
rank: 104
|
||||||
|
}
|
||||||
}
|
}
|
||||||
] satisfies Array<RouteConfigsTable>;
|
] satisfies Array<RouteConfigsTable>;
|
||||||
|
@ -17,9 +17,9 @@ import {
|
|||||||
isIncludeAllChildren
|
isIncludeAllChildren
|
||||||
} from "@pureadmin/utils";
|
} from "@pureadmin/utils";
|
||||||
import { getConfig } from "@/config";
|
import { getConfig } from "@/config";
|
||||||
import type { menuType } from "@/layout/types";
|
|
||||||
import { buildHierarchyTree } from "@/utils/tree";
|
import { buildHierarchyTree } from "@/utils/tree";
|
||||||
import { userKey, type DataInfo } from "@/utils/auth";
|
import { userKey, type DataInfo } from "@/utils/auth";
|
||||||
|
import { type menuType, routerArrays } from "@/layout/types";
|
||||||
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission";
|
import { usePermissionStoreHook } from "@/store/modules/permission";
|
||||||
const IFrame = () => import("@/layout/frameView.vue");
|
const IFrame = () => import("@/layout/frameView.vue");
|
||||||
@ -178,6 +178,14 @@ function handleAsyncRoutes(routeList) {
|
|||||||
);
|
);
|
||||||
usePermissionStoreHook().handleWholeMenus(routeList);
|
usePermissionStoreHook().handleWholeMenus(routeList);
|
||||||
}
|
}
|
||||||
|
if (!useMultiTagsStoreHook().getMultiTagsCache) {
|
||||||
|
useMultiTagsStoreHook().handleTags("equal", [
|
||||||
|
...routerArrays,
|
||||||
|
...usePermissionStoreHook().flatteningRoutes.filter(
|
||||||
|
v => v?.meta?.fixedTag
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
addPathMatch();
|
addPathMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { store } from "@/store";
|
import { store } from "@/store";
|
||||||
import { routerArrays } from "@/layout/types";
|
import { routerArrays } from "@/layout/types";
|
||||||
|
import { usePermissionStoreHook } from "./permission";
|
||||||
import { responsiveStorageNameSpace } from "@/config";
|
import { responsiveStorageNameSpace } from "@/config";
|
||||||
import type { multiType, positionType } from "./types";
|
import type { multiType, positionType } from "./types";
|
||||||
import { isEqual, isBoolean, isUrl, storageLocal } from "@pureadmin/utils";
|
import { isEqual, isBoolean, isUrl, storageLocal } from "@pureadmin/utils";
|
||||||
@ -15,7 +16,12 @@ export const useMultiTagsStore = defineStore({
|
|||||||
? storageLocal().getItem<StorageConfigs>(
|
? storageLocal().getItem<StorageConfigs>(
|
||||||
`${responsiveStorageNameSpace()}tags`
|
`${responsiveStorageNameSpace()}tags`
|
||||||
)
|
)
|
||||||
: [...routerArrays],
|
: [
|
||||||
|
...routerArrays,
|
||||||
|
...usePermissionStoreHook().flatteningRoutes.filter(
|
||||||
|
v => v?.meta?.fixedTag
|
||||||
|
)
|
||||||
|
],
|
||||||
multiTagsCache: storageLocal().getItem<StorageConfigs>(
|
multiTagsCache: storageLocal().getItem<StorageConfigs>(
|
||||||
`${responsiveStorageNameSpace()}configure`
|
`${responsiveStorageNameSpace()}configure`
|
||||||
)?.multiTagsCache
|
)?.multiTagsCache
|
||||||
|
@ -4,7 +4,12 @@ import type { cacheType } from "./types";
|
|||||||
import { constantMenus } from "@/router";
|
import { constantMenus } from "@/router";
|
||||||
import { useMultiTagsStoreHook } from "./multiTags";
|
import { useMultiTagsStoreHook } from "./multiTags";
|
||||||
import { debounce, getKeyList } from "@pureadmin/utils";
|
import { debounce, getKeyList } from "@pureadmin/utils";
|
||||||
import { ascending, filterTree, filterNoPermissionTree } from "@/router/utils";
|
import {
|
||||||
|
ascending,
|
||||||
|
filterTree,
|
||||||
|
filterNoPermissionTree,
|
||||||
|
formatFlatteningRoutes
|
||||||
|
} from "@/router/utils";
|
||||||
|
|
||||||
export const usePermissionStore = defineStore({
|
export const usePermissionStore = defineStore({
|
||||||
id: "pure-permission",
|
id: "pure-permission",
|
||||||
@ -13,6 +18,8 @@ export const usePermissionStore = defineStore({
|
|||||||
constantMenus,
|
constantMenus,
|
||||||
// 整体路由生成的菜单(静态、动态)
|
// 整体路由生成的菜单(静态、动态)
|
||||||
wholeMenus: [],
|
wholeMenus: [],
|
||||||
|
// 整体路由(一维数组格式)
|
||||||
|
flatteningRoutes: [],
|
||||||
// 缓存页面keepAlive
|
// 缓存页面keepAlive
|
||||||
cachePageList: []
|
cachePageList: []
|
||||||
}),
|
}),
|
||||||
@ -22,6 +29,9 @@ export const usePermissionStore = defineStore({
|
|||||||
this.wholeMenus = filterNoPermissionTree(
|
this.wholeMenus = filterNoPermissionTree(
|
||||||
filterTree(ascending(this.constantMenus.concat(routes)))
|
filterTree(ascending(this.constantMenus.concat(routes)))
|
||||||
);
|
);
|
||||||
|
this.flatteningRoutes = formatFlatteningRoutes(
|
||||||
|
this.constantMenus.concat(routes)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
cacheOperate({ mode, name }: cacheType) {
|
cacheOperate({ mode, name }: cacheType) {
|
||||||
const delIndex = this.cachePageList.findIndex(v => v === name);
|
const delIndex = this.cachePageList.findIndex(v => v === name);
|
||||||
|
@ -38,7 +38,9 @@ export type setType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type userType = {
|
export type userType = {
|
||||||
|
avatar?: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
|
nickname?: string;
|
||||||
roles?: Array<string>;
|
roles?: Array<string>;
|
||||||
verifyCode?: string;
|
verifyCode?: string;
|
||||||
currentPage?: number;
|
currentPage?: number;
|
||||||
|
@ -12,8 +12,12 @@ import { type DataInfo, setToken, removeToken, userKey } from "@/utils/auth";
|
|||||||
export const useUserStore = defineStore({
|
export const useUserStore = defineStore({
|
||||||
id: "pure-user",
|
id: "pure-user",
|
||||||
state: (): userType => ({
|
state: (): userType => ({
|
||||||
|
// 头像
|
||||||
|
avatar: storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "",
|
||||||
// 用户名
|
// 用户名
|
||||||
username: storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "",
|
username: storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "",
|
||||||
|
// 昵称
|
||||||
|
nickname: storageLocal().getItem<DataInfo<number>>(userKey)?.nickname ?? "",
|
||||||
// 页面级别权限
|
// 页面级别权限
|
||||||
roles: storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [],
|
roles: storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [],
|
||||||
// 前端生成的验证码(按实际需求替换)
|
// 前端生成的验证码(按实际需求替换)
|
||||||
@ -26,10 +30,18 @@ export const useUserStore = defineStore({
|
|||||||
loginDay: 7
|
loginDay: 7
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
|
/** 存储头像 */
|
||||||
|
SET_AVATAR(avatar: string) {
|
||||||
|
this.avatar = avatar;
|
||||||
|
},
|
||||||
/** 存储用户名 */
|
/** 存储用户名 */
|
||||||
SET_USERNAME(username: string) {
|
SET_USERNAME(username: string) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
},
|
},
|
||||||
|
/** 存储昵称 */
|
||||||
|
SET_NICKNAME(nickname: string) {
|
||||||
|
this.nickname = nickname;
|
||||||
|
},
|
||||||
/** 存储角色 */
|
/** 存储角色 */
|
||||||
SET_ROLES(roles: Array<string>) {
|
SET_ROLES(roles: Array<string>) {
|
||||||
this.roles = roles;
|
this.roles = roles;
|
||||||
|
@ -9,8 +9,12 @@ export interface DataInfo<T> {
|
|||||||
expires: T;
|
expires: T;
|
||||||
/** 用于调用刷新accessToken的接口时所需的token */
|
/** 用于调用刷新accessToken的接口时所需的token */
|
||||||
refreshToken: string;
|
refreshToken: string;
|
||||||
|
/** 头像 */
|
||||||
|
avatar?: string;
|
||||||
/** 用户名 */
|
/** 用户名 */
|
||||||
username?: string;
|
username?: string;
|
||||||
|
/** 昵称 */
|
||||||
|
nickname?: string;
|
||||||
/** 当前登陆用户的角色 */
|
/** 当前登陆用户的角色 */
|
||||||
roles?: Array<string>;
|
roles?: Array<string>;
|
||||||
}
|
}
|
||||||
@ -37,7 +41,7 @@ export function getToken(): DataInfo<number> {
|
|||||||
* @description 设置`token`以及一些必要信息并采用无感刷新`token`方案
|
* @description 设置`token`以及一些必要信息并采用无感刷新`token`方案
|
||||||
* 无感刷新:后端返回`accessToken`(访问接口使用的`token`)、`refreshToken`(用于调用刷新`accessToken`的接口时所需的`token`,`refreshToken`的过期时间(比如30天)应大于`accessToken`的过期时间(比如2小时))、`expires`(`accessToken`的过期时间)
|
* 无感刷新:后端返回`accessToken`(访问接口使用的`token`)、`refreshToken`(用于调用刷新`accessToken`的接口时所需的`token`,`refreshToken`的过期时间(比如30天)应大于`accessToken`的过期时间(比如2小时))、`expires`(`accessToken`的过期时间)
|
||||||
* 将`accessToken`、`expires`这两条信息放在key值为authorized-token的cookie里(过期自动销毁)
|
* 将`accessToken`、`expires`这两条信息放在key值为authorized-token的cookie里(过期自动销毁)
|
||||||
* 将`username`、`roles`、`refreshToken`、`expires`这四条信息放在key值为`user-info`的localStorage里(利用`multipleTabsKey`当浏览器完全关闭后自动销毁)
|
* 将`avatar`、`username`、`nickname`、`roles`、`refreshToken`、`expires`这六条信息放在key值为`user-info`的localStorage里(利用`multipleTabsKey`当浏览器完全关闭后自动销毁)
|
||||||
*/
|
*/
|
||||||
export function setToken(data: DataInfo<Date>) {
|
export function setToken(data: DataInfo<Date>) {
|
||||||
let expires = 0;
|
let expires = 0;
|
||||||
@ -62,26 +66,44 @@ export function setToken(data: DataInfo<Date>) {
|
|||||||
: {}
|
: {}
|
||||||
);
|
);
|
||||||
|
|
||||||
function setUserKey(username: string, roles: Array<string>) {
|
function setUserKey({ avatar, username, nickname, roles }) {
|
||||||
|
useUserStoreHook().SET_AVATAR(avatar);
|
||||||
useUserStoreHook().SET_USERNAME(username);
|
useUserStoreHook().SET_USERNAME(username);
|
||||||
|
useUserStoreHook().SET_NICKNAME(nickname);
|
||||||
useUserStoreHook().SET_ROLES(roles);
|
useUserStoreHook().SET_ROLES(roles);
|
||||||
storageLocal().setItem(userKey, {
|
storageLocal().setItem(userKey, {
|
||||||
refreshToken,
|
refreshToken,
|
||||||
expires,
|
expires,
|
||||||
|
avatar,
|
||||||
username,
|
username,
|
||||||
|
nickname,
|
||||||
roles
|
roles
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.username && data.roles) {
|
if (data.username && data.roles) {
|
||||||
const { username, roles } = data;
|
const { username, roles } = data;
|
||||||
setUserKey(username, roles);
|
setUserKey({
|
||||||
|
avatar: data?.avatar ?? "",
|
||||||
|
username,
|
||||||
|
nickname: data?.nickname ?? "",
|
||||||
|
roles
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
|
const avatar =
|
||||||
|
storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "";
|
||||||
const username =
|
const username =
|
||||||
storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "";
|
storageLocal().getItem<DataInfo<number>>(userKey)?.username ?? "";
|
||||||
|
const nickname =
|
||||||
|
storageLocal().getItem<DataInfo<number>>(userKey)?.nickname ?? "";
|
||||||
const roles =
|
const roles =
|
||||||
storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [];
|
storageLocal().getItem<DataInfo<number>>(userKey)?.roles ?? [];
|
||||||
setUserKey(username, roles);
|
setUserKey({
|
||||||
|
avatar,
|
||||||
|
username,
|
||||||
|
nickname,
|
||||||
|
roles
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
62
src/views/account-settings/components/accountManagement.vue
Normal file
62
src/views/account-settings/components/accountManagement.vue
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
import { deviceDetection } from "@pureadmin/utils";
|
||||||
|
|
||||||
|
const list = ref([
|
||||||
|
{
|
||||||
|
title: "账户密码",
|
||||||
|
illustrate: "当前密码强度:强",
|
||||||
|
button: "修改"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "密保手机",
|
||||||
|
illustrate: "已经绑定手机:158****6789",
|
||||||
|
button: "修改"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "密保问题",
|
||||||
|
illustrate: "未设置密保问题,密保问题可有效保护账户安全",
|
||||||
|
button: "修改"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "备用邮箱",
|
||||||
|
illustrate: "已绑定邮箱:pure***@163.com",
|
||||||
|
button: "修改"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
function onClick(item) {
|
||||||
|
console.log("onClick", item.title);
|
||||||
|
message("请根据具体业务自行实现", { type: "success" });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'min-w-[180px]',
|
||||||
|
deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<h3 class="my-8">账户管理</h3>
|
||||||
|
<div v-for="(item, index) in list" :key="index">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="flex-1">
|
||||||
|
<p>{{ item.title }}</p>
|
||||||
|
<el-text class="mx-1" type="info">{{ item.illustrate }}</el-text>
|
||||||
|
</div>
|
||||||
|
<el-button type="primary" text @click="onClick(item)">
|
||||||
|
{{ item.button }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<el-divider />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.el-divider--horizontal {
|
||||||
|
border-top: 0.1px var(--el-border-color) var(--el-border-style);
|
||||||
|
}
|
||||||
|
</style>
|
65
src/views/account-settings/components/preferences.vue
Normal file
65
src/views/account-settings/components/preferences.vue
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
import { deviceDetection } from "@pureadmin/utils";
|
||||||
|
|
||||||
|
const list = ref([
|
||||||
|
{
|
||||||
|
title: "账户密码",
|
||||||
|
illustrate: "其他用户的消息将以站内信的形式通知",
|
||||||
|
checked: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "系统消息",
|
||||||
|
illustrate: "系统消息将以站内信的形式通知",
|
||||||
|
checked: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "待办任务",
|
||||||
|
illustrate: "待办任务将以站内信的形式通知",
|
||||||
|
checked: true
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
function onChange(val, item) {
|
||||||
|
console.log("onChange", val);
|
||||||
|
message(`${item.title}设置成功`, { type: "success" });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'min-w-[180px]',
|
||||||
|
deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<h3 class="my-8">偏好设置</h3>
|
||||||
|
<div v-for="(item, index) in list" :key="index">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="flex-1">
|
||||||
|
<p>{{ item.title }}</p>
|
||||||
|
<p class="wp-4">
|
||||||
|
<el-text class="mx-1" type="info">
|
||||||
|
{{ item.illustrate }}
|
||||||
|
</el-text>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<el-switch
|
||||||
|
v-model="item.checked"
|
||||||
|
inline-prompt
|
||||||
|
active-text="是"
|
||||||
|
inactive-text="否"
|
||||||
|
@change="val => onChange(val, item)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<el-divider />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.el-divider--horizontal {
|
||||||
|
border-top: 0.1px var(--el-border-color) var(--el-border-style);
|
||||||
|
}
|
||||||
|
</style>
|
187
src/views/account-settings/components/profile.vue
Normal file
187
src/views/account-settings/components/profile.vue
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref } from "vue";
|
||||||
|
import { formUpload } from "@/api/mock";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
import { type UserInfo, getMine } from "@/api/user";
|
||||||
|
import type { FormInstance, FormRules } from "element-plus";
|
||||||
|
import ReCropperPreview from "@/components/ReCropperPreview";
|
||||||
|
import { createFormData, deviceDetection } from "@pureadmin/utils";
|
||||||
|
import uploadLine from "@iconify-icons/ri/upload-line";
|
||||||
|
|
||||||
|
const imgSrc = ref("");
|
||||||
|
const cropperInfo = ref();
|
||||||
|
const cropRef = ref();
|
||||||
|
const uploadRef = ref();
|
||||||
|
const isShow = ref(false);
|
||||||
|
const userInfoFormRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const userInfos = reactive({
|
||||||
|
avatar: "",
|
||||||
|
nickname: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
description: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules = reactive<FormRules<UserInfo>>({
|
||||||
|
nickname: [{ required: true, message: "昵称必填", trigger: "blur" }]
|
||||||
|
});
|
||||||
|
|
||||||
|
function queryEmail(queryString, callback) {
|
||||||
|
const emailList = [
|
||||||
|
{ value: "@qq.com" },
|
||||||
|
{ value: "@126.com" },
|
||||||
|
{ value: "@163.com" }
|
||||||
|
];
|
||||||
|
let results = [];
|
||||||
|
let queryList = [];
|
||||||
|
emailList.map(item =>
|
||||||
|
queryList.push({ value: queryString.split("@")[0] + item.value })
|
||||||
|
);
|
||||||
|
results = queryString
|
||||||
|
? queryList.filter(
|
||||||
|
item =>
|
||||||
|
item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
|
||||||
|
)
|
||||||
|
: queryList;
|
||||||
|
callback(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChange = uploadFile => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = e => {
|
||||||
|
imgSrc.value = e.target.result as string;
|
||||||
|
isShow.value = true;
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(uploadFile.raw);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
cropRef.value.hidePopover();
|
||||||
|
uploadRef.value.clearFiles();
|
||||||
|
isShow.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCropper = info => (cropperInfo.value = info);
|
||||||
|
|
||||||
|
const handleSubmitImage = () => {
|
||||||
|
const formData = createFormData({
|
||||||
|
files: new File([cropperInfo.value], "avatar")
|
||||||
|
});
|
||||||
|
formUpload(formData)
|
||||||
|
.then(({ success, data }) => {
|
||||||
|
if (success) {
|
||||||
|
message("更新头像成功", { type: "success" });
|
||||||
|
handleClose();
|
||||||
|
} else {
|
||||||
|
message("更新头像失败");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
message(`提交异常 ${error}`, { type: "error" });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新信息
|
||||||
|
const onSubmit = async (formEl: FormInstance) => {
|
||||||
|
await formEl.validate((valid, fields) => {
|
||||||
|
if (valid) {
|
||||||
|
console.log(userInfos);
|
||||||
|
message("更新信息成功", { type: "success" });
|
||||||
|
} else {
|
||||||
|
console.log("error submit!", fields);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getMine().then(res => {
|
||||||
|
Object.assign(userInfos, res.data);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'min-w-[180px]',
|
||||||
|
deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<h3 class="my-8">个人信息</h3>
|
||||||
|
<el-form
|
||||||
|
ref="userInfoFormRef"
|
||||||
|
label-position="top"
|
||||||
|
:rules="rules"
|
||||||
|
:model="userInfos"
|
||||||
|
>
|
||||||
|
<el-form-item label="头像">
|
||||||
|
<el-avatar :size="80" :src="userInfos.avatar" />
|
||||||
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
|
accept="image/*"
|
||||||
|
action="#"
|
||||||
|
:limit="1"
|
||||||
|
:auto-upload="false"
|
||||||
|
:show-file-list="false"
|
||||||
|
:on-change="onChange"
|
||||||
|
>
|
||||||
|
<el-button plain class="ml-4">
|
||||||
|
<IconifyIconOffline :icon="uploadLine" />
|
||||||
|
<span class="ml-2">更新头像</span>
|
||||||
|
</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="昵称" prop="nickname">
|
||||||
|
<el-input v-model="userInfos.nickname" placeholder="请输入昵称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="邮箱" prop="email">
|
||||||
|
<el-autocomplete
|
||||||
|
v-model="userInfos.email"
|
||||||
|
:fetch-suggestions="queryEmail"
|
||||||
|
:trigger-on-focus="false"
|
||||||
|
placeholder="请输入邮箱"
|
||||||
|
clearable
|
||||||
|
class="w-full"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="联系电话">
|
||||||
|
<el-input
|
||||||
|
v-model="userInfos.phone"
|
||||||
|
placeholder="请输入联系电话"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="简介">
|
||||||
|
<el-input
|
||||||
|
v-model="userInfos.description"
|
||||||
|
placeholder="请输入简介"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{ minRows: 6, maxRows: 8 }"
|
||||||
|
maxlength="56"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-button type="primary" @click="onSubmit(userInfoFormRef)">
|
||||||
|
更新信息
|
||||||
|
</el-button>
|
||||||
|
</el-form>
|
||||||
|
<el-dialog
|
||||||
|
v-model="isShow"
|
||||||
|
width="40%"
|
||||||
|
title="编辑头像"
|
||||||
|
destroy-on-close
|
||||||
|
:closeOnClickModal="false"
|
||||||
|
:before-close="handleClose"
|
||||||
|
:fullscreen="deviceDetection()"
|
||||||
|
>
|
||||||
|
<ReCropperPreview ref="cropRef" :imgSrc="imgSrc" @cropper="onCropper" />
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button bg text @click="handleClose">取消</el-button>
|
||||||
|
<el-button bg text type="primary" @click="handleSubmitImage">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
87
src/views/account-settings/components/securityLog.vue
Normal file
87
src/views/account-settings/components/securityLog.vue
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { getMineLogs } from "@/api/user";
|
||||||
|
import { reactive, ref, onMounted } from "vue";
|
||||||
|
import { deviceDetection } from "@pureadmin/utils";
|
||||||
|
import type { PaginationProps } from "@pureadmin/table";
|
||||||
|
|
||||||
|
const loading = ref(true);
|
||||||
|
const dataList = ref([]);
|
||||||
|
const pagination = reactive<PaginationProps>({
|
||||||
|
total: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
currentPage: 1,
|
||||||
|
background: true,
|
||||||
|
layout: "prev, pager, next"
|
||||||
|
});
|
||||||
|
const columns: TableColumnList = [
|
||||||
|
{
|
||||||
|
label: "详情",
|
||||||
|
prop: "summary",
|
||||||
|
minWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "IP 地址",
|
||||||
|
prop: "ip",
|
||||||
|
minWidth: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "地点",
|
||||||
|
prop: "address",
|
||||||
|
minWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "操作系统",
|
||||||
|
prop: "system",
|
||||||
|
minWidth: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "浏览器类型",
|
||||||
|
prop: "browser",
|
||||||
|
minWidth: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "时间",
|
||||||
|
prop: "operatingTime",
|
||||||
|
minWidth: 180,
|
||||||
|
formatter: ({ operatingTime }) =>
|
||||||
|
dayjs(operatingTime).format("YYYY-MM-DD HH:mm:ss")
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
async function onSearch() {
|
||||||
|
loading.value = true;
|
||||||
|
const { data } = await getMineLogs();
|
||||||
|
dataList.value = data.list;
|
||||||
|
pagination.total = data.total;
|
||||||
|
pagination.pageSize = data.pageSize;
|
||||||
|
pagination.currentPage = data.currentPage;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
onSearch();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'min-w-[180px]',
|
||||||
|
deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<h3 class="my-8">安全日志</h3>
|
||||||
|
<pure-table
|
||||||
|
row-key="id"
|
||||||
|
table-layout="auto"
|
||||||
|
:loading="loading"
|
||||||
|
:data="dataList"
|
||||||
|
:columns="columns"
|
||||||
|
:pagination="pagination"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
183
src/views/account-settings/index.vue
Normal file
183
src/views/account-settings/index.vue
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { getMine } from "@/api/user";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { ref, onBeforeMount } from "vue";
|
||||||
|
import { ReText } from "@/components/ReText";
|
||||||
|
import Profile from "./components/profile.vue";
|
||||||
|
import Preferences from "./components/preferences.vue";
|
||||||
|
import SecurityLog from "./components/securityLog.vue";
|
||||||
|
import { useGlobal, deviceDetection } from "@pureadmin/utils";
|
||||||
|
import AccountManagement from "./components/accountManagement.vue";
|
||||||
|
import TopCollapse from "@/layout/components/sidebar/topCollapse.vue";
|
||||||
|
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
|
||||||
|
|
||||||
|
import leftLine from "@iconify-icons/ri/arrow-left-s-line";
|
||||||
|
import ProfileIcon from "@iconify-icons/ri/user-3-line";
|
||||||
|
import PreferencesIcon from "@iconify-icons/ri/settings-3-line";
|
||||||
|
import SecurityLogIcon from "@iconify-icons/ri/window-line";
|
||||||
|
import AccountManagementIcon from "@iconify-icons/ri/profile-line";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "AccountSettings"
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const isOpen = ref(deviceDetection() ? false : true);
|
||||||
|
const { $storage } = useGlobal<GlobalPropertiesApi>();
|
||||||
|
onBeforeMount(() => {
|
||||||
|
useDataThemeChange().dataThemeChange($storage.layout?.overallStyle);
|
||||||
|
});
|
||||||
|
|
||||||
|
const userInfo = ref({
|
||||||
|
avatar: "",
|
||||||
|
username: "",
|
||||||
|
nickname: ""
|
||||||
|
});
|
||||||
|
const panes = [
|
||||||
|
{
|
||||||
|
key: "profile",
|
||||||
|
label: "个人信息",
|
||||||
|
icon: ProfileIcon,
|
||||||
|
component: Profile
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "preferences",
|
||||||
|
label: "偏好设置",
|
||||||
|
icon: PreferencesIcon,
|
||||||
|
component: Preferences
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "securityLog",
|
||||||
|
label: "安全日志",
|
||||||
|
icon: SecurityLogIcon,
|
||||||
|
component: SecurityLog
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "accountManagement",
|
||||||
|
label: "账户管理",
|
||||||
|
icon: AccountManagementIcon,
|
||||||
|
component: AccountManagement
|
||||||
|
}
|
||||||
|
];
|
||||||
|
const witchPane = ref("profile");
|
||||||
|
|
||||||
|
getMine().then(res => {
|
||||||
|
userInfo.value = res.data;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-container class="h-full">
|
||||||
|
<el-aside
|
||||||
|
v-if="isOpen"
|
||||||
|
class="settings-sidebar px-2 dark:!bg-[var(--el-bg-color)]"
|
||||||
|
:width="deviceDetection() ? '180px' : '240px'"
|
||||||
|
>
|
||||||
|
<el-menu :default-active="witchPane" class="settings-menu">
|
||||||
|
<el-menu-item
|
||||||
|
class="hover:!transition-all hover:!duration-200 hover:!text-base !h-[50px]"
|
||||||
|
@click="router.go(-1)"
|
||||||
|
>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<IconifyIconOffline :icon="leftLine" />
|
||||||
|
<span class="ml-2">返回</span>
|
||||||
|
</div>
|
||||||
|
</el-menu-item>
|
||||||
|
<div class="flex items-center ml-8 mt-4 mb-4">
|
||||||
|
<el-avatar :size="48" :src="userInfo.avatar" />
|
||||||
|
<div class="ml-4 flex flex-col max-w-[130px]">
|
||||||
|
<ReText class="font-bold !self-baseline">
|
||||||
|
{{ userInfo.nickname }}
|
||||||
|
</ReText>
|
||||||
|
<ReText class="!self-baseline" type="info">
|
||||||
|
{{ userInfo.username }}
|
||||||
|
</ReText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-menu-item
|
||||||
|
v-for="item in panes"
|
||||||
|
:key="item.key"
|
||||||
|
:index="item.key"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
witchPane = item.key;
|
||||||
|
if (deviceDetection()) {
|
||||||
|
isOpen = !isOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div class="flex items-center z-10">
|
||||||
|
<el-icon><IconifyIconOffline :icon="item.icon" /></el-icon>
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
</div>
|
||||||
|
</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</el-aside>
|
||||||
|
<el-main>
|
||||||
|
<TopCollapse
|
||||||
|
v-if="deviceDetection()"
|
||||||
|
class="px-0"
|
||||||
|
:is-active="isOpen"
|
||||||
|
@toggleClick="isOpen = !isOpen"
|
||||||
|
/>
|
||||||
|
<component
|
||||||
|
:is="panes.find(item => item.key === witchPane).component"
|
||||||
|
:class="[!deviceDetection() && 'ml-[120px]']"
|
||||||
|
/>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.settings-sidebar {
|
||||||
|
overflow: hidden;
|
||||||
|
background: $menuBg;
|
||||||
|
border-right: 1px solid var(--pure-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-menu {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
::v-deep(.el-menu-item) {
|
||||||
|
height: 48px !important;
|
||||||
|
color: $menuText !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
transition: color 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $menuTitleHover !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
color: #fff !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0 8px;
|
||||||
|
margin: 4px 0;
|
||||||
|
clear: both;
|
||||||
|
content: "";
|
||||||
|
background: var(--el-color-primary);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body[layout] {
|
||||||
|
.el-menu--vertical .is-active {
|
||||||
|
color: #fff !important;
|
||||||
|
transition: color 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -20,6 +20,7 @@
|
|||||||
| `frameLoading` | 加载动画(内嵌的`iframe`页面是否开启首次加载动画) |
|
| `frameLoading` | 加载动画(内嵌的`iframe`页面是否开启首次加载动画) |
|
||||||
| `keepAlive` | 缓存页面(是否缓存该路由页面,开启后会保存该页面的整体状态,刷新后会清空状态) |
|
| `keepAlive` | 缓存页面(是否缓存该路由页面,开启后会保存该页面的整体状态,刷新后会清空状态) |
|
||||||
| `hiddenTag` | 标签页(当前菜单名称或自定义信息禁止添加到标签页) |
|
| `hiddenTag` | 标签页(当前菜单名称或自定义信息禁止添加到标签页) |
|
||||||
|
| `fixedTag` | 固定标签页(当前菜单名称是否固定显示在标签页且不可关闭) |
|
||||||
| `showLink` | 菜单(是否显示该菜单) |
|
| `showLink` | 菜单(是否显示该菜单) |
|
||||||
| `showParent` | 父级菜单(是否显示父级菜单 [点击查看更多](https://yiming_chang.gitee.io/pure-admin-doc/pages/routerMenu/#%E7%AC%AC%E4%B8%80%E7%A7%8D-%E8%AF%A5%E6%A8%A1%E5%BC%8F%E9%92%88%E5%AF%B9%E7%88%B6%E7%BA%A7%E8%8F%9C%E5%8D%95%E4%B8%8B%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84%E6%83%85%E5%86%B5-%E5%9C%A8%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84-meta-%E5%B1%9E%E6%80%A7%E4%B8%AD%E5%8A%A0%E4%B8%8A-showparent-true-%E5%8D%B3%E5%8F%AF)) |
|
| `showParent` | 父级菜单(是否显示父级菜单 [点击查看更多](https://yiming_chang.gitee.io/pure-admin-doc/pages/routerMenu/#%E7%AC%AC%E4%B8%80%E7%A7%8D-%E8%AF%A5%E6%A8%A1%E5%BC%8F%E9%92%88%E5%AF%B9%E7%88%B6%E7%BA%A7%E8%8F%9C%E5%8D%95%E4%B8%8B%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84%E6%83%85%E5%86%B5-%E5%9C%A8%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84-meta-%E5%B1%9E%E6%80%A7%E4%B8%AD%E5%8A%A0%E4%B8%8A-showparent-true-%E5%8D%B3%E5%8F%AF)) |
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import ReAnimateSelector from "@/components/ReAnimateSelector";
|
|||||||
import {
|
import {
|
||||||
menuTypeOptions,
|
menuTypeOptions,
|
||||||
showLinkOptions,
|
showLinkOptions,
|
||||||
|
fixedTagOptions,
|
||||||
keepAliveOptions,
|
keepAliveOptions,
|
||||||
hiddenTagOptions,
|
hiddenTagOptions,
|
||||||
showParentOptions,
|
showParentOptions,
|
||||||
@ -26,7 +27,7 @@ const props = withDefaults(defineProps<FormProps>(), {
|
|||||||
path: "",
|
path: "",
|
||||||
component: "",
|
component: "",
|
||||||
rank: 99,
|
rank: 99,
|
||||||
redirect: " ",
|
redirect: "",
|
||||||
icon: "",
|
icon: "",
|
||||||
extraIcon: "",
|
extraIcon: "",
|
||||||
enterTransition: "",
|
enterTransition: "",
|
||||||
@ -37,6 +38,7 @@ const props = withDefaults(defineProps<FormProps>(), {
|
|||||||
frameLoading: true,
|
frameLoading: true,
|
||||||
keepAlive: false,
|
keepAlive: false,
|
||||||
hiddenTag: false,
|
hiddenTag: false,
|
||||||
|
fixedTag: false,
|
||||||
showLink: true,
|
showLink: true,
|
||||||
showParent: false
|
showParent: false
|
||||||
})
|
})
|
||||||
@ -258,33 +260,6 @@ defineExpose({ getRef });
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</re-col>
|
</re-col>
|
||||||
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="缓存页面">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.keepAlive ? 0 : 1"
|
|
||||||
:options="keepAliveOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.keepAlive = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="标签页">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.hiddenTag ? 1 : 0"
|
|
||||||
:options="hiddenTagOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.hiddenTag = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col
|
<re-col
|
||||||
v-show="newFormInline.menuType !== 3"
|
v-show="newFormInline.menuType !== 3"
|
||||||
:value="12"
|
:value="12"
|
||||||
@ -305,7 +280,7 @@ defineExpose({ getRef });
|
|||||||
</re-col>
|
</re-col>
|
||||||
<re-col
|
<re-col
|
||||||
v-show="newFormInline.menuType !== 3"
|
v-show="newFormInline.menuType !== 3"
|
||||||
:value="8"
|
:value="12"
|
||||||
:xs="24"
|
:xs="24"
|
||||||
:sm="24"
|
:sm="24"
|
||||||
>
|
>
|
||||||
@ -321,6 +296,47 @@ defineExpose({ getRef });
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</re-col>
|
</re-col>
|
||||||
|
|
||||||
|
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||||
|
<el-form-item label="缓存页面">
|
||||||
|
<Segmented
|
||||||
|
:modelValue="newFormInline.keepAlive ? 0 : 1"
|
||||||
|
:options="keepAliveOptions"
|
||||||
|
@change="
|
||||||
|
({ option: { value } }) => {
|
||||||
|
newFormInline.keepAlive = value;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||||
|
<el-form-item label="标签页">
|
||||||
|
<Segmented
|
||||||
|
:modelValue="newFormInline.hiddenTag ? 1 : 0"
|
||||||
|
:options="hiddenTagOptions"
|
||||||
|
@change="
|
||||||
|
({ option: { value } }) => {
|
||||||
|
newFormInline.hiddenTag = value;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||||
|
<el-form-item label="固定标签页">
|
||||||
|
<Segmented
|
||||||
|
:modelValue="newFormInline.fixedTag ? 0 : 1"
|
||||||
|
:options="fixedTagOptions"
|
||||||
|
@change="
|
||||||
|
({ option: { value } }) => {
|
||||||
|
newFormInline.fixedTag = value;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
|
@ -61,7 +61,7 @@ const {
|
|||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<PureTableBar
|
<PureTableBar
|
||||||
title="菜单管理(初版,持续完善中)"
|
title="菜单管理(仅演示,操作后不生效)"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:isExpandAll="false"
|
:isExpandAll="false"
|
||||||
:tableRef="tableRef?.getTableRef()"
|
:tableRef="tableRef?.getTableRef()"
|
||||||
|
@ -32,6 +32,19 @@ const showLinkOptions: Array<OptionsType> = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const fixedTagOptions: Array<OptionsType> = [
|
||||||
|
{
|
||||||
|
label: "固定",
|
||||||
|
tip: "当前菜单名称固定显示在标签页且不可关闭",
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "不固定",
|
||||||
|
tip: "当前菜单名称不固定显示在标签页且可关闭",
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const keepAliveOptions: Array<OptionsType> = [
|
const keepAliveOptions: Array<OptionsType> = [
|
||||||
{
|
{
|
||||||
label: "缓存",
|
label: "缓存",
|
||||||
@ -87,6 +100,7 @@ const frameLoadingOptions: Array<OptionsType> = [
|
|||||||
export {
|
export {
|
||||||
menuTypeOptions,
|
menuTypeOptions,
|
||||||
showLinkOptions,
|
showLinkOptions,
|
||||||
|
fixedTagOptions,
|
||||||
keepAliveOptions,
|
keepAliveOptions,
|
||||||
hiddenTagOptions,
|
hiddenTagOptions,
|
||||||
showParentOptions,
|
showParentOptions,
|
||||||
|
@ -155,6 +155,7 @@ export function useMenu() {
|
|||||||
frameLoading: row?.frameLoading ?? true,
|
frameLoading: row?.frameLoading ?? true,
|
||||||
keepAlive: row?.keepAlive ?? false,
|
keepAlive: row?.keepAlive ?? false,
|
||||||
hiddenTag: row?.hiddenTag ?? false,
|
hiddenTag: row?.hiddenTag ?? false,
|
||||||
|
fixedTag: row?.fixedTag ?? false,
|
||||||
showLink: row?.showLink ?? true,
|
showLink: row?.showLink ?? true,
|
||||||
showParent: row?.showParent ?? false
|
showParent: row?.showParent ?? false
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ interface FormItemProps {
|
|||||||
frameLoading: boolean;
|
frameLoading: boolean;
|
||||||
keepAlive: boolean;
|
keepAlive: boolean;
|
||||||
hiddenTag: boolean;
|
hiddenTag: boolean;
|
||||||
|
fixedTag: boolean;
|
||||||
showLink: boolean;
|
showLink: boolean;
|
||||||
showParent: boolean;
|
showParent: boolean;
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,11 @@ import editForm from "../form/index.vue";
|
|||||||
import { zxcvbn } from "@zxcvbn-ts/core";
|
import { zxcvbn } from "@zxcvbn-ts/core";
|
||||||
import { handleTree } from "@/utils/tree";
|
import { handleTree } from "@/utils/tree";
|
||||||
import { message } from "@/utils/message";
|
import { message } from "@/utils/message";
|
||||||
import croppingUpload from "../upload.vue";
|
|
||||||
import userAvatar from "@/assets/user.jpg";
|
import userAvatar from "@/assets/user.jpg";
|
||||||
import { usePublicHooks } from "../../hooks";
|
import { usePublicHooks } from "../../hooks";
|
||||||
import { addDialog } from "@/components/ReDialog";
|
import { addDialog } from "@/components/ReDialog";
|
||||||
import type { PaginationProps } from "@pureadmin/table";
|
import type { PaginationProps } from "@pureadmin/table";
|
||||||
|
import ReCropperPreview from "@/components/ReCropperPreview";
|
||||||
import type { FormItemProps, RoleFormItemProps } from "../utils/types";
|
import type { FormItemProps, RoleFormItemProps } from "../utils/types";
|
||||||
import {
|
import {
|
||||||
getKeyList,
|
getKeyList,
|
||||||
@ -365,11 +365,10 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
addDialog({
|
addDialog({
|
||||||
title: "裁剪、上传头像",
|
title: "裁剪、上传头像",
|
||||||
width: "40%",
|
width: "40%",
|
||||||
draggable: true,
|
|
||||||
closeOnClickModal: false,
|
closeOnClickModal: false,
|
||||||
fullscreen: deviceDetection(),
|
fullscreen: deviceDetection(),
|
||||||
contentRenderer: () =>
|
contentRenderer: () =>
|
||||||
h(croppingUpload, {
|
h(ReCropperPreview, {
|
||||||
ref: cropRef,
|
ref: cropRef,
|
||||||
imgSrc: row.avatar || userAvatar,
|
imgSrc: row.avatar || userAvatar,
|
||||||
onCropper: info => (avatarInfo.value = info)
|
onCropper: info => (avatarInfo.value = info)
|
||||||
|
@ -2,7 +2,6 @@ import { message } from "@/utils/message";
|
|||||||
import { tableData } from "../data";
|
import { tableData } from "../data";
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
// 如果您不习惯tsx写法,可以传slot,然后在template里写
|
|
||||||
// 需是hooks写法(函数中有return),避免失去响应性
|
// 需是hooks写法(函数中有return),避免失去响应性
|
||||||
export function useColumns() {
|
export function useColumns() {
|
||||||
const search = ref("");
|
const search = ref("");
|
||||||
@ -26,8 +25,9 @@ export function useColumns() {
|
|||||||
|
|
||||||
const columns: TableColumnList = [
|
const columns: TableColumnList = [
|
||||||
{
|
{
|
||||||
label: "日期",
|
prop: "date",
|
||||||
prop: "date"
|
// 自定义表头,slot用法 #nameHeader="{ column, $index }"
|
||||||
|
headerSlot: "nameHeader"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "姓名",
|
label: "姓名",
|
||||||
@ -39,7 +39,7 @@ export function useColumns() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: "right",
|
align: "right",
|
||||||
// 自定义表头
|
// 自定义表头,tsx用法
|
||||||
headerRenderer: () => (
|
headerRenderer: () => (
|
||||||
<el-input
|
<el-input
|
||||||
v-model={search.value}
|
v-model={search.value}
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useColumns } from "./columns";
|
import { useColumns } from "./columns";
|
||||||
|
import Calendar from "@iconify-icons/ri/calendar-2-line";
|
||||||
|
|
||||||
const { columns, filterTableData } = useColumns();
|
const { columns, filterTableData } = useColumns();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<pure-table :data="filterTableData" :columns="columns" />
|
<pure-table :data="filterTableData" :columns="columns">
|
||||||
|
<template #nameHeader>
|
||||||
|
<span class="flex items-center">
|
||||||
|
<IconifyIconOffline :icon="Calendar" />
|
||||||
|
日期
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</pure-table>
|
||||||
</template>
|
</template>
|
||||||
|
4
types/router.d.ts
vendored
4
types/router.d.ts
vendored
@ -45,8 +45,10 @@ declare global {
|
|||||||
/** 离场动画 */
|
/** 离场动画 */
|
||||||
leaveTransition?: string;
|
leaveTransition?: string;
|
||||||
};
|
};
|
||||||
// 是否不添加信息到标签页,(默认`false`)
|
/** 当前菜单名称或自定义信息禁止添加到标签页(默认`false`) */
|
||||||
hiddenTag?: boolean;
|
hiddenTag?: boolean;
|
||||||
|
/** 当前菜单名称是否固定显示在标签页且不可关闭(默认`false`) */
|
||||||
|
fixedTag?: boolean;
|
||||||
/** 动态路由可打开的最大数量 `可选` */
|
/** 动态路由可打开的最大数量 `可选` */
|
||||||
dynamicLevel?: number;
|
dynamicLevel?: number;
|
||||||
/** 将某个菜单激活
|
/** 将某个菜单激活
|
||||||
|
Loading…
x
Reference in New Issue
Block a user