smart-admin-web 10.20.1 → 10.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.d.ts +19 -1
- package/dist/api/index.js +85 -66
- package/dist/api/index.js.map +1 -1
- package/dist/api/schema.d.ts +448 -1
- package/dist/assets/icons/ph-subset.json.js +1 -1
- package/dist/components/FormContainer/index.vue.js +87 -75
- package/dist/components/FormContainer/index.vue.js.map +1 -1
- package/dist/components/UserPicker/UserAvatar.vue.d.ts +8 -0
- package/dist/components/UserPicker/UserAvatar.vue.js +8 -0
- package/dist/components/UserPicker/UserAvatar.vue.js.map +1 -0
- package/dist/components/UserPicker/UserAvatar2.vue.js +38 -0
- package/dist/components/UserPicker/UserAvatar2.vue.js.map +1 -0
- package/dist/components/UserPicker/index.vue.js +1 -1
- package/dist/components/UserPicker/index2.vue.js +328 -186
- package/dist/components/UserPicker/index2.vue.js.map +1 -1
- package/dist/index.js +248 -246
- package/dist/lib/runtime.js +1 -1
- package/dist/lib/watermark.d.ts +1 -1
- package/dist/locales/en-US.d.ts +62 -3
- package/dist/locales/en-US.js +65 -6
- package/dist/locales/en-US.js.map +1 -1
- package/dist/locales/kernelMenuTitles.js +1 -0
- package/dist/locales/kernelMenuTitles.js.map +1 -1
- package/dist/locales/zh-CN.d.ts +62 -3
- package/dist/locales/zh-CN.js +65 -6
- package/dist/locales/zh-CN.js.map +1 -1
- package/dist/router/viewRegistry.js +6 -6
- package/dist/router/viewRegistry.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/theme/naive-theme.js +80 -77
- package/dist/theme/naive-theme.js.map +1 -1
- package/dist/types/api.d.ts +91 -0
- package/dist/types/api.js +17 -15
- package/dist/types/api.js.map +1 -1
- package/dist/utils/shake.d.ts +1 -0
- package/dist/utils/shake.js +30 -0
- package/dist/utils/shake.js.map +1 -0
- package/dist/views/login/LoginForm.vue.js +2 -2
- package/dist/views/login/LoginForm2.vue.js +135 -135
- package/dist/views/login/LoginForm2.vue.js.map +1 -1
- package/dist/views/login/components/LoginDesk.vue.js +1 -1
- package/dist/views/login/components/LoginDesk2.vue.js +59 -47
- package/dist/views/login/components/LoginDesk2.vue.js.map +1 -1
- package/dist/views/personal/notice2.vue.js +1 -1
- package/dist/views/personal/notice2.vue.js.map +1 -1
- package/dist/views/personal/sessions.vue.js +2 -2
- package/dist/views/personal/sessions2.vue.js +41 -40
- package/dist/views/personal/sessions2.vue.js.map +1 -1
- package/dist/views/system/ai-usage/index.vue.js +3 -3
- package/dist/views/system/ai-usage/index2.vue.js +13 -12
- package/dist/views/system/ai-usage/index2.vue.js.map +1 -1
- package/dist/views/system/config/components/AdvancedSection.vue.js +3 -3
- package/dist/views/system/config/components/AdvancedSection2.vue.js +2 -1
- package/dist/views/system/config/components/AdvancedSection2.vue.js.map +1 -1
- package/dist/views/system/dict/index.vue.js +2 -2
- package/dist/views/system/dict/index2.vue.js +12 -1
- package/dist/views/system/dict/index2.vue.js.map +1 -1
- package/dist/views/system/file/index2.vue.js +1 -0
- package/dist/views/system/file/index2.vue.js.map +1 -1
- package/dist/views/system/job/index.vue.js +3 -3
- package/dist/views/system/job/index2.vue.js +182 -181
- package/dist/views/system/job/index2.vue.js.map +1 -1
- package/dist/views/system/job-log/components/AttemptTable2.vue.js +8 -3
- package/dist/views/system/job-log/components/AttemptTable2.vue.js.map +1 -1
- package/dist/views/system/job-log/index.vue.js +1 -1
- package/dist/views/system/job-log/index2.vue.js +5 -5
- package/dist/views/system/job-log/index2.vue.js.map +1 -1
- package/dist/views/system/log/exception/index.vue.js +2 -2
- package/dist/views/system/log/exception/index2.vue.js +8 -5
- package/dist/views/system/log/exception/index2.vue.js.map +1 -1
- package/dist/views/system/log/login/index2.vue.js +21 -12
- package/dist/views/system/log/login/index2.vue.js.map +1 -1
- package/dist/views/system/log/op/index.vue.js +2 -2
- package/dist/views/system/log/op/index2.vue.js +50 -40
- package/dist/views/system/log/op/index2.vue.js.map +1 -1
- package/dist/views/system/menu/components/ButtonManager.vue.js +2 -2
- package/dist/views/system/menu/components/ButtonManager2.vue.js +45 -36
- package/dist/views/system/menu/components/ButtonManager2.vue.js.map +1 -1
- package/dist/views/system/menu/index.vue.js +2 -2
- package/dist/views/system/menu/index2.vue.js +11 -11
- package/dist/views/system/menu/index2.vue.js.map +1 -1
- package/dist/views/system/module/index.vue.js +1 -1
- package/dist/views/system/module/index2.vue.js +171 -117
- package/dist/views/system/module/index2.vue.js.map +1 -1
- package/dist/views/system/notice/index.vue.js +3 -3
- package/dist/views/system/notice/index2.vue.js +1 -0
- package/dist/views/system/notice/index2.vue.js.map +1 -1
- package/dist/views/system/org/index.vue.js +2 -2
- package/dist/views/system/org/index2.vue.js +70 -67
- package/dist/views/system/org/index2.vue.js.map +1 -1
- package/dist/views/system/position/index.vue.js +2 -2
- package/dist/views/system/position/index2.vue.js +57 -48
- package/dist/views/system/position/index2.vue.js.map +1 -1
- package/dist/views/system/recycle/index.vue.js +1 -1
- package/dist/views/system/recycle/index2.vue.js +3 -1
- package/dist/views/system/recycle/index2.vue.js.map +1 -1
- package/dist/views/system/role/components/GrantMenuSheet.vue.d.ts +2 -0
- package/dist/views/system/role/components/GrantMenuSheet.vue.js +2 -2
- package/dist/views/system/role/components/GrantMenuSheet2.vue.js +54 -49
- package/dist/views/system/role/components/GrantMenuSheet2.vue.js.map +1 -1
- package/dist/views/system/role/components/GrantMenuTable.vue.js +1 -1
- package/dist/views/system/role/components/GrantMenuTable2.vue.js +159 -195
- package/dist/views/system/role/components/GrantMenuTable2.vue.js.map +1 -1
- package/dist/views/system/role/components/grantMenuGroups.d.ts +54 -0
- package/dist/views/system/role/components/grantMenuGroups.js +116 -76
- package/dist/views/system/role/components/grantMenuGroups.js.map +1 -1
- package/dist/views/system/role/index.vue.js +2 -2
- package/dist/views/system/role/index2.vue.js +152 -145
- package/dist/views/system/role/index2.vue.js.map +1 -1
- package/dist/views/system/session/index2.vue.js +39 -35
- package/dist/views/system/session/index2.vue.js.map +1 -1
- package/dist/views/system/user/components/UserGrantEffectiveTable.vue.d.ts +10 -0
- package/dist/views/system/user/components/UserGrantEffectiveTable.vue.js +5 -0
- package/dist/views/system/user/components/UserGrantEffectiveTable.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantEffectiveTable2.vue.js +96 -0
- package/dist/views/system/user/components/UserGrantEffectiveTable2.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantMenuSheet.vue.d.ts +13 -0
- package/dist/views/system/user/components/UserGrantMenuSheet.vue.js +8 -0
- package/dist/views/system/user/components/UserGrantMenuSheet.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantMenuSheet2.vue.js +216 -0
- package/dist/views/system/user/components/UserGrantMenuSheet2.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantMenuTable.vue.d.ts +13 -0
- package/dist/views/system/user/components/UserGrantMenuTable.vue.js +8 -0
- package/dist/views/system/user/components/UserGrantMenuTable.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantMenuTable2.vue.js +186 -0
- package/dist/views/system/user/components/UserGrantMenuTable2.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantNodeRow.vue.d.ts +31 -0
- package/dist/views/system/user/components/UserGrantNodeRow.vue.js +8 -0
- package/dist/views/system/user/components/UserGrantNodeRow.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantNodeRow2.vue.js +171 -0
- package/dist/views/system/user/components/UserGrantNodeRow2.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.d.ts +21 -0
- package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.js +5 -0
- package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantOverviewDrawer2.vue.js +178 -0
- package/dist/views/system/user/components/UserGrantOverviewDrawer2.vue.js.map +1 -0
- package/dist/views/system/user/components/userGrantState.d.ts +64 -0
- package/dist/views/system/user/components/userGrantState.js +117 -0
- package/dist/views/system/user/components/userGrantState.js.map +1 -0
- package/dist/views/system/user/index.vue.js +2 -2
- package/dist/views/system/user/index2.vue.js +250 -207
- package/dist/views/system/user/index2.vue.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { defineComponent as A, computed as k, openBlock as n, createElementBlock as o, normalizeClass as B, createElementVNode as u, toDisplayString as a, createCommentVNode as f, Fragment as U, renderList as V, createBlock as h, unref as e, withCtx as d, createTextVNode as c, createVNode as i } from "vue";
|
|
2
|
+
import { NTag as G, NTooltip as F, NRadioGroup as I, NRadioButton as D, NDatePicker as L, NInput as $ } from "naive-ui";
|
|
3
|
+
import { useI18n as P } from "vue-i18n";
|
|
4
|
+
import S from "../../../../components/AppIcon.vue.js";
|
|
5
|
+
import { UserMenuEffect as j } from "../../../../types/api.js";
|
|
6
|
+
import { formatDate as C, isExpiryDateDisabled as q } from "./userGrantState.js";
|
|
7
|
+
const H = { class: "ugr-main" }, J = { class: "ugr-name" }, K = { class: "ugr-title" }, O = {
|
|
8
|
+
key: 0,
|
|
9
|
+
class: "ugr-path"
|
|
10
|
+
}, Q = { class: "ugr-src" }, W = {
|
|
11
|
+
key: 0,
|
|
12
|
+
class: "faint"
|
|
13
|
+
}, X = { class: "ugr-tri" }, Y = {
|
|
14
|
+
key: 0,
|
|
15
|
+
class: "ugr-edit"
|
|
16
|
+
}, Z = {
|
|
17
|
+
key: 1,
|
|
18
|
+
class: "ugr-leak"
|
|
19
|
+
}, ne = /* @__PURE__ */ A({
|
|
20
|
+
__name: "UserGrantNodeRow",
|
|
21
|
+
props: {
|
|
22
|
+
title: {},
|
|
23
|
+
path: {},
|
|
24
|
+
level: {},
|
|
25
|
+
node: {},
|
|
26
|
+
state: {},
|
|
27
|
+
entry: {},
|
|
28
|
+
editable: { type: Boolean },
|
|
29
|
+
notGrantable: { type: Boolean },
|
|
30
|
+
maxDate: {},
|
|
31
|
+
leaked: {}
|
|
32
|
+
},
|
|
33
|
+
emits: ["update:state", "update:expireDate", "update:remark"],
|
|
34
|
+
setup(t, { emit: E }) {
|
|
35
|
+
const v = t, y = E, { t: l } = P(), b = k(() => {
|
|
36
|
+
var s;
|
|
37
|
+
return ((s = v.node) == null ? void 0 : s.roles) ?? [];
|
|
38
|
+
}), g = k(() => b.value.slice(0, 2)), R = C(/* @__PURE__ */ new Date()), T = (s) => q(C(new Date(s)), R, v.maxDate), M = k(
|
|
39
|
+
() => {
|
|
40
|
+
var s;
|
|
41
|
+
return v.maxDate == null || ((s = v.entry) == null ? void 0 : s.effect) === j.Deny;
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
return (s, m) => {
|
|
45
|
+
var p, x, w, N, z;
|
|
46
|
+
return n(), o("div", {
|
|
47
|
+
class: B(["ugr", [`is-${t.level}`, { "is-off": !((p = t.node) != null && p.effective) }]])
|
|
48
|
+
}, [
|
|
49
|
+
u("div", H, [
|
|
50
|
+
u("div", J, [
|
|
51
|
+
u("span", K, a(t.title), 1),
|
|
52
|
+
t.path ? (n(), o("span", O, a(t.path), 1)) : f("", !0)
|
|
53
|
+
]),
|
|
54
|
+
u("div", Q, [
|
|
55
|
+
(n(!0), o(U, null, V(g.value, (r) => (n(), h(e(G), {
|
|
56
|
+
key: r,
|
|
57
|
+
size: "small",
|
|
58
|
+
bordered: !1
|
|
59
|
+
}, {
|
|
60
|
+
default: d(() => [
|
|
61
|
+
c(a(r), 1)
|
|
62
|
+
]),
|
|
63
|
+
_: 2
|
|
64
|
+
}, 1024))), 128)),
|
|
65
|
+
b.value.length > g.value.length ? (n(), o("span", W, " +" + a(b.value.length - g.value.length), 1)) : f("", !0),
|
|
66
|
+
(x = t.node) != null && x.expired ? (n(), h(e(G), {
|
|
67
|
+
key: 1,
|
|
68
|
+
size: "small",
|
|
69
|
+
type: "warning",
|
|
70
|
+
bordered: !1
|
|
71
|
+
}, {
|
|
72
|
+
default: d(() => [
|
|
73
|
+
c(a(e(l)("userGrant.expired")), 1)
|
|
74
|
+
]),
|
|
75
|
+
_: 1
|
|
76
|
+
})) : f("", !0),
|
|
77
|
+
(w = t.node) != null && w.deniedByAncestor ? (n(), h(e(G), {
|
|
78
|
+
key: 2,
|
|
79
|
+
size: "small",
|
|
80
|
+
type: "error",
|
|
81
|
+
bordered: !1
|
|
82
|
+
}, {
|
|
83
|
+
default: d(() => [
|
|
84
|
+
c(a(e(l)("userGrant.deniedByAncestor")), 1)
|
|
85
|
+
]),
|
|
86
|
+
_: 1
|
|
87
|
+
})) : f("", !0),
|
|
88
|
+
u("span", {
|
|
89
|
+
class: B(["ugr-dot", { on: (N = t.node) == null ? void 0 : N.effective }])
|
|
90
|
+
}, a((z = t.node) != null && z.effective ? e(l)("userGrant.effective") : e(l)("userGrant.ineffective")), 3)
|
|
91
|
+
]),
|
|
92
|
+
i(e(F), {
|
|
93
|
+
disabled: !t.notGrantable
|
|
94
|
+
}, {
|
|
95
|
+
trigger: d(() => [
|
|
96
|
+
u("span", X, [
|
|
97
|
+
i(e(I), {
|
|
98
|
+
value: t.state,
|
|
99
|
+
size: "small",
|
|
100
|
+
disabled: !t.editable,
|
|
101
|
+
"onUpdate:value": m[0] || (m[0] = (r) => y("update:state", r))
|
|
102
|
+
}, {
|
|
103
|
+
default: d(() => [
|
|
104
|
+
i(e(D), { value: "follow" }, {
|
|
105
|
+
default: d(() => [
|
|
106
|
+
c(a(e(l)("userGrant.follow")), 1)
|
|
107
|
+
]),
|
|
108
|
+
_: 1
|
|
109
|
+
}),
|
|
110
|
+
i(e(D), { value: "allow" }, {
|
|
111
|
+
default: d(() => [
|
|
112
|
+
c(a(e(l)("userGrant.allow")), 1)
|
|
113
|
+
]),
|
|
114
|
+
_: 1
|
|
115
|
+
}),
|
|
116
|
+
i(e(D), { value: "deny" }, {
|
|
117
|
+
default: d(() => [
|
|
118
|
+
c(a(e(l)("userGrant.deny")), 1)
|
|
119
|
+
]),
|
|
120
|
+
_: 1
|
|
121
|
+
})
|
|
122
|
+
]),
|
|
123
|
+
_: 1
|
|
124
|
+
}, 8, ["value", "disabled"])
|
|
125
|
+
])
|
|
126
|
+
]),
|
|
127
|
+
default: d(() => [
|
|
128
|
+
c(" " + a(e(l)("userGrant.notGrantable")), 1)
|
|
129
|
+
]),
|
|
130
|
+
_: 1
|
|
131
|
+
}, 8, ["disabled"])
|
|
132
|
+
]),
|
|
133
|
+
t.entry ? (n(), o("div", Y, [
|
|
134
|
+
i(e(L), {
|
|
135
|
+
type: "date",
|
|
136
|
+
size: "small",
|
|
137
|
+
"value-format": "yyyy-MM-dd",
|
|
138
|
+
"formatted-value": t.entry.expireDate,
|
|
139
|
+
clearable: M.value,
|
|
140
|
+
"is-date-disabled": T,
|
|
141
|
+
disabled: !t.editable,
|
|
142
|
+
placeholder: e(l)("userGrant.expireLongTerm"),
|
|
143
|
+
"onUpdate:formattedValue": m[1] || (m[1] = (r) => y("update:expireDate", r))
|
|
144
|
+
}, null, 8, ["formatted-value", "clearable", "disabled", "placeholder"]),
|
|
145
|
+
i(e($), {
|
|
146
|
+
size: "small",
|
|
147
|
+
value: t.entry.remark ?? "",
|
|
148
|
+
maxlength: 200,
|
|
149
|
+
disabled: !t.editable,
|
|
150
|
+
placeholder: e(l)("userGrant.remark"),
|
|
151
|
+
"onUpdate:value": m[2] || (m[2] = (r) => y("update:remark", r))
|
|
152
|
+
}, null, 8, ["value", "disabled", "placeholder"])
|
|
153
|
+
])) : f("", !0),
|
|
154
|
+
t.state === "deny" && t.leaked.length ? (n(), o("div", Z, [
|
|
155
|
+
i(S, {
|
|
156
|
+
icon: "ph:warning",
|
|
157
|
+
size: 14
|
|
158
|
+
}),
|
|
159
|
+
u("span", null, a(e(l)("userGrant.leaked")), 1),
|
|
160
|
+
(n(!0), o(U, null, V(t.leaked, (r) => (n(), o("code", {
|
|
161
|
+
key: r.code
|
|
162
|
+
}, a(e(l)("userGrant.leakedFrom", { code: r.code, from: r.from })), 1))), 128))
|
|
163
|
+
])) : f("", !0)
|
|
164
|
+
], 2);
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
export {
|
|
169
|
+
ne as default
|
|
170
|
+
};
|
|
171
|
+
//# sourceMappingURL=UserGrantNodeRow2.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserGrantNodeRow2.vue.js","sources":["../../../../../src/views/system/user/components/UserGrantNodeRow.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 用户授权弹窗里一个节点一行:名称 / 来源与是否有效 / 三态;选了允许或拒绝再展开到期日与理由。\n// 选「拒绝」时,这个节点的接口若还由别的有效节点携带,就地列出来:按节点拒绝不收回共用的接口。\nimport { computed } from 'vue'\nimport { NDatePicker, NInput, NRadioButton, NRadioGroup, NTag, NTooltip } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport AppIcon from '#/components/AppIcon.vue'\nimport { UserMenuEffect, type UserMenuEffectiveNode } from '#/types/api'\nimport { formatDate, isExpiryDateDisabled, type DraftEntry, type TriState } from './userGrantState'\n\nconst props = defineProps<{\n title: string\n path?: string\n level: 'catalog' | 'page' | 'button'\n node?: UserMenuEffectiveNode\n state: TriState\n entry?: DraftEntry\n /** 目标用户可编辑且这个节点所属模块可授 */\n editable: boolean\n /** 只因模块不可转授而不能改(悬浮说明) */\n notGrantable: boolean\n /** 到期日上限 yyyy-MM-dd;null = 不限 */\n maxDate: string | null\n leaked: { code: string; from: string }[]\n}>()\n\nconst emit = defineEmits<{\n (e: 'update:state', v: TriState): void\n (e: 'update:expireDate', v: string | null): void\n (e: 'update:remark', v: string): void\n}>()\n\nconst { t } = useI18n()\n/** 角色标签最多露两个,其余折成 +N。 */\nconst roles = computed(() => props.node?.roles ?? [])\nconst shownRoles = computed(() => roles.value.slice(0, 2))\nconst today = formatDate(new Date())\n/** 今天以前、上限以后的日期不可选。 */\nconst isDateDisabled = (ts: number) =>\n isExpiryDateDisabled(formatDate(new Date(ts)), today, props.maxDate)\n/** 受限时「允许」的到期日必填(不给清空);拒绝与不受限时可留空 = 长期。 */\nconst clearable = computed(\n () => props.maxDate == null || props.entry?.effect === UserMenuEffect.Deny,\n)\n</script>\n\n<template>\n <div class=\"ugr\" :class=\"[`is-${level}`, { 'is-off': !node?.effective }]\">\n <div class=\"ugr-main\">\n <div class=\"ugr-name\">\n <span class=\"ugr-title\">{{ title }}</span>\n <span v-if=\"path\" class=\"ugr-path\">{{ path }}</span>\n </div>\n <div class=\"ugr-src\">\n <n-tag v-for=\"r in shownRoles\" :key=\"r\" size=\"small\" :bordered=\"false\">{{ r }}</n-tag>\n <span v-if=\"roles.length > shownRoles.length\" class=\"faint\">\n +{{ roles.length - shownRoles.length }}\n </span>\n <n-tag v-if=\"node?.expired\" size=\"small\" type=\"warning\" :bordered=\"false\">\n {{ t('userGrant.expired') }}\n </n-tag>\n <n-tag v-if=\"node?.deniedByAncestor\" size=\"small\" type=\"error\" :bordered=\"false\">\n {{ t('userGrant.deniedByAncestor') }}\n </n-tag>\n <span class=\"ugr-dot\" :class=\"{ on: node?.effective }\">\n {{ node?.effective ? t('userGrant.effective') : t('userGrant.ineffective') }}\n </span>\n </div>\n <n-tooltip :disabled=\"!notGrantable\">\n <template #trigger>\n <!-- 禁用的单选组收不到鼠标事件,包一层让悬浮说明能出来 -->\n <span class=\"ugr-tri\">\n <n-radio-group\n :value=\"state\"\n size=\"small\"\n :disabled=\"!editable\"\n @update:value=\"(v: TriState) => emit('update:state', v)\"\n >\n <n-radio-button value=\"follow\">{{ t('userGrant.follow') }}</n-radio-button>\n <n-radio-button value=\"allow\">{{ t('userGrant.allow') }}</n-radio-button>\n <n-radio-button value=\"deny\">{{ t('userGrant.deny') }}</n-radio-button>\n </n-radio-group>\n </span>\n </template>\n {{ t('userGrant.notGrantable') }}\n </n-tooltip>\n </div>\n\n <div v-if=\"entry\" class=\"ugr-edit\">\n <n-date-picker\n type=\"date\"\n size=\"small\"\n value-format=\"yyyy-MM-dd\"\n :formatted-value=\"entry.expireDate\"\n :clearable=\"clearable\"\n :is-date-disabled=\"isDateDisabled\"\n :disabled=\"!editable\"\n :placeholder=\"t('userGrant.expireLongTerm')\"\n @update:formatted-value=\"(v: string | null) => emit('update:expireDate', v)\"\n />\n <n-input\n size=\"small\"\n :value=\"entry.remark ?? ''\"\n :maxlength=\"200\"\n :disabled=\"!editable\"\n :placeholder=\"t('userGrant.remark')\"\n @update:value=\"(v: string) => emit('update:remark', v)\"\n />\n </div>\n\n <div v-if=\"state === 'deny' && leaked.length\" class=\"ugr-leak\">\n <AppIcon icon=\"ph:warning\" :size=\"14\" />\n <span>{{ t('userGrant.leaked') }}</span>\n <code v-for=\"l in leaked\" :key=\"l.code\">\n {{ t('userGrant.leakedFrom', { code: l.code, from: l.from }) }}\n </code>\n </div>\n </div>\n</template>\n\n<style scoped>\n.ugr {\n padding: 8px 16px;\n border-top: 1px solid var(--hairline);\n}\n.ugr.is-button {\n padding-left: 40px;\n}\n.ugr.is-catalog {\n flex: 1;\n min-width: 0;\n padding: 0;\n border-top: 0;\n}\n.ugr-main {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;\n gap: 6px 16px;\n align-items: center;\n}\n.ugr-name {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n.ugr-title {\n overflow-wrap: anywhere;\n}\n.ugr.is-catalog .ugr-title {\n font-size: 15px;\n font-weight: 600;\n}\n.ugr.is-off .ugr-title {\n color: var(--text-2);\n}\n.ugr-path {\n font-family: var(--font-mono);\n font-size: 12px;\n color: var(--text-3);\n}\n.ugr-src {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n align-items: center;\n font-size: 13px;\n}\n.ugr-dot {\n color: var(--text-3);\n}\n.ugr-dot.on {\n color: var(--sel-fg);\n}\n.ugr-tri {\n display: inline-flex;\n}\n.ugr-edit {\n display: grid;\n grid-template-columns: 170px minmax(0, 1fr);\n gap: 8px;\n margin-top: 8px;\n}\n.ugr-leak {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 8px;\n align-items: center;\n margin-top: 6px;\n font-size: 12px;\n color: var(--warn);\n}\n.ugr-leak code {\n font-family: var(--font-mono);\n}\n@media (max-width: 640px) {\n .ugr-main,\n .ugr-edit {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n</style>\n"],"names":["props","__props","emit","__emit","t","useI18n","roles","computed","_a","shownRoles","today","formatDate","isDateDisabled","ts","isExpiryDateDisabled","clearable","UserMenuEffect","_createElementBlock","_createElementVNode","_hoisted_1","_hoisted_2","_hoisted_3","_toDisplayString","_hoisted_4","_hoisted_5","_Fragment","_renderList","_createBlock","_unref","NTag","_hoisted_6","_b","_c","_normalizeClass","_d","_e","_createVNode","NTooltip","_hoisted_7","NRadioGroup","_cache","v","NRadioButton","_createTextVNode","_openBlock","_hoisted_8","NDatePicker","NInput","_hoisted_9","AppIcon","l"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type __VLS_ModelProps = {
|
|
2
|
+
'show'?: boolean;
|
|
3
|
+
};
|
|
4
|
+
declare const __VLS_export: import('vue').DefineComponent<__VLS_ModelProps, {
|
|
5
|
+
refresh: () => Promise<void> | undefined;
|
|
6
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
7
|
+
"update:show": (value: boolean) => any;
|
|
8
|
+
} & {
|
|
9
|
+
adjust: (user: {
|
|
10
|
+
id: number;
|
|
11
|
+
name: string;
|
|
12
|
+
}) => any;
|
|
13
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
|
|
14
|
+
onAdjust?: ((user: {
|
|
15
|
+
id: number;
|
|
16
|
+
name: string;
|
|
17
|
+
}) => any) | undefined;
|
|
18
|
+
"onUpdate:show"?: ((value: boolean) => any) | undefined;
|
|
19
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
declare const _default: typeof __VLS_export;
|
|
21
|
+
export default _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserGrantOverviewDrawer.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { defineComponent as N, useModel as _, ref as M, computed as w, h as l, openBlock as B, createBlock as I, unref as r, withCtx as u, createVNode as c, mergeModels as S } from "vue";
|
|
2
|
+
import { useMessage as z, NTag as y, NButton as O, NDrawer as U, NDrawerContent as C } from "naive-ui";
|
|
3
|
+
import { useI18n as R } from "vue-i18n";
|
|
4
|
+
import { SmartTable as D } from "smart-naive-table";
|
|
5
|
+
import L from "../../../../components/TableTotal/index.vue.js";
|
|
6
|
+
import { userApi as j } from "../../../../api/index.js";
|
|
7
|
+
import { translateMenuTitle as G } from "../../../../locales/menuTitle.js";
|
|
8
|
+
import { translateError as H } from "../../../../utils/error.js";
|
|
9
|
+
import { SEARCH_ACTIONS as i, deriveHeaderFilters as $ } from "../../../../utils/tableFilter.js";
|
|
10
|
+
import { TABLE_MIN_ROW_HEIGHT as q, TABLE_TOOLBAR as F } from "../../../../utils/tableToolbar.js";
|
|
11
|
+
import { UserMenuEffect as n, UserMenuGrantStatus as s } from "../../../../types/api.js";
|
|
12
|
+
const re = /* @__PURE__ */ N({
|
|
13
|
+
__name: "UserGrantOverviewDrawer",
|
|
14
|
+
props: {
|
|
15
|
+
show: { type: Boolean, default: !1 },
|
|
16
|
+
showModifiers: {}
|
|
17
|
+
},
|
|
18
|
+
emits: /* @__PURE__ */ S(["adjust"], ["update:show"]),
|
|
19
|
+
setup(T, { expose: g, emit: x }) {
|
|
20
|
+
const m = _(T, "show"), b = x, { t } = R(), k = z(), p = M();
|
|
21
|
+
g({ refresh: () => {
|
|
22
|
+
var e;
|
|
23
|
+
return (e = p.value) == null ? void 0 : e.refresh();
|
|
24
|
+
} });
|
|
25
|
+
const v = w(() => [
|
|
26
|
+
{ label: t("userGrant.allow"), value: n.Allow },
|
|
27
|
+
{ label: t("userGrant.deny"), value: n.Deny }
|
|
28
|
+
]), d = w(() => [
|
|
29
|
+
{ label: t("userGrant.statusActive"), value: s.Active },
|
|
30
|
+
{ label: t("userGrant.statusExpiring"), value: s.Expiring },
|
|
31
|
+
{ label: t("userGrant.statusExpired"), value: s.Expired }
|
|
32
|
+
]), A = {
|
|
33
|
+
[s.Active]: "success",
|
|
34
|
+
[s.Expiring]: "warning",
|
|
35
|
+
[s.Expired]: "default"
|
|
36
|
+
}, E = (e) => {
|
|
37
|
+
var a;
|
|
38
|
+
return ((a = d.value.find((o) => o.value === e)) == null ? void 0 : a.label) ?? "";
|
|
39
|
+
}, f = () => l("span", { class: "faint" }, "—"), h = [
|
|
40
|
+
{ type: "index", title: () => t("common.rowNo"), width: 64, align: "center" },
|
|
41
|
+
// 只作搜索项:目标用户账号或姓名
|
|
42
|
+
{
|
|
43
|
+
key: "user",
|
|
44
|
+
title: () => t("userGrant.colUser"),
|
|
45
|
+
hideInTable: !0,
|
|
46
|
+
search: { actions: i.fuzzy }
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
key: "userName",
|
|
50
|
+
title: () => t("userGrant.colUser"),
|
|
51
|
+
width: 140,
|
|
52
|
+
ellipsis: { tooltip: !0 },
|
|
53
|
+
card: "title",
|
|
54
|
+
render: (e) => `${e.userName}(${e.userAccount})`
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
key: "menuTitle",
|
|
58
|
+
title: () => t("userGrant.colMenu"),
|
|
59
|
+
width: 140,
|
|
60
|
+
ellipsis: { tooltip: !0 },
|
|
61
|
+
render: (e) => [e.moduleTitle ? G(e.moduleTitle) : null, G(e.menuTitle)].filter(Boolean).join(" · ")
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: "effect",
|
|
65
|
+
title: () => t("userGrant.colEffect"),
|
|
66
|
+
width: 102,
|
|
67
|
+
options: v,
|
|
68
|
+
search: { actions: i.exact, props: { clearable: !0 } },
|
|
69
|
+
render: (e) => l(
|
|
70
|
+
y,
|
|
71
|
+
{
|
|
72
|
+
size: "small",
|
|
73
|
+
bordered: !1,
|
|
74
|
+
type: e.effect === n.Allow ? "success" : "error"
|
|
75
|
+
},
|
|
76
|
+
() => t(e.effect === n.Allow ? "userGrant.allow" : "userGrant.deny")
|
|
77
|
+
)
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
key: "status",
|
|
81
|
+
title: () => t("userGrant.colStatus"),
|
|
82
|
+
width: 108,
|
|
83
|
+
options: d,
|
|
84
|
+
search: { actions: i.exact, props: { clearable: !0 } },
|
|
85
|
+
render: (e) => l(
|
|
86
|
+
y,
|
|
87
|
+
{ size: "small", bordered: !1, type: A[e.status] },
|
|
88
|
+
() => E(e.status)
|
|
89
|
+
)
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
key: "expireTime",
|
|
93
|
+
title: () => t("userGrant.colExpire"),
|
|
94
|
+
width: 160,
|
|
95
|
+
render: (e) => e.expireTime ? e.expireTime.replace("T", " ") : t("userGrant.expireLongTerm")
|
|
96
|
+
},
|
|
97
|
+
// 只作搜索项:授权人账号或姓名
|
|
98
|
+
{
|
|
99
|
+
key: "grantor",
|
|
100
|
+
title: () => t("userGrant.colGrantor"),
|
|
101
|
+
hideInTable: !0,
|
|
102
|
+
search: { actions: i.fuzzy }
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
key: "grantorName",
|
|
106
|
+
title: () => t("userGrant.colGrantor"),
|
|
107
|
+
width: 100,
|
|
108
|
+
ellipsis: { tooltip: !0 },
|
|
109
|
+
render: (e) => e.grantorName || f()
|
|
110
|
+
},
|
|
111
|
+
{ key: "grantTime", title: () => t("userGrant.colGrantTime"), width: 160, format: "datetime" },
|
|
112
|
+
{
|
|
113
|
+
key: "remark",
|
|
114
|
+
title: () => t("userGrant.colRemark"),
|
|
115
|
+
ellipsis: { tooltip: !0 },
|
|
116
|
+
render: (e) => e.remark || f()
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
key: "op",
|
|
120
|
+
title: () => t("common.operation"),
|
|
121
|
+
width: 84,
|
|
122
|
+
fixed: "right",
|
|
123
|
+
hideInSetting: !0,
|
|
124
|
+
render: (e) => l(
|
|
125
|
+
O,
|
|
126
|
+
{
|
|
127
|
+
size: "small",
|
|
128
|
+
quaternary: !0,
|
|
129
|
+
type: "primary",
|
|
130
|
+
onClick: () => b("adjust", { id: e.userId, name: e.userName })
|
|
131
|
+
},
|
|
132
|
+
() => t("userGrant.adjust")
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
];
|
|
136
|
+
return $(h), (e, a) => (B(), I(r(U), {
|
|
137
|
+
show: m.value,
|
|
138
|
+
"onUpdate:show": a[1] || (a[1] = (o) => m.value = o),
|
|
139
|
+
placement: "right",
|
|
140
|
+
width: "min(1440px, 100vw)"
|
|
141
|
+
}, {
|
|
142
|
+
default: u(() => [
|
|
143
|
+
c(r(C), {
|
|
144
|
+
title: r(t)("userGrant.overview"),
|
|
145
|
+
closable: "",
|
|
146
|
+
"body-content-style": { height: "100%", display: "flex", flexDirection: "column" }
|
|
147
|
+
}, {
|
|
148
|
+
default: u(() => [
|
|
149
|
+
c(r(D), {
|
|
150
|
+
ref_key: "tableRef",
|
|
151
|
+
ref: p,
|
|
152
|
+
columns: h,
|
|
153
|
+
fetcher: r(j).menuGrantPage,
|
|
154
|
+
search: { container: "table" },
|
|
155
|
+
toolbar: r(F),
|
|
156
|
+
"fill-height": "",
|
|
157
|
+
"min-row-height": r(q),
|
|
158
|
+
"card-on-narrow": "",
|
|
159
|
+
"storage-key": "sys-user-menu-grants",
|
|
160
|
+
onError: a[0] || (a[0] = (o) => r(k).error(r(H)(o)))
|
|
161
|
+
}, {
|
|
162
|
+
"pagination-prefix": u(({ itemCount: o }) => [
|
|
163
|
+
c(L, { count: o }, null, 8, ["count"])
|
|
164
|
+
]),
|
|
165
|
+
_: 1
|
|
166
|
+
}, 8, ["fetcher", "toolbar", "min-row-height"])
|
|
167
|
+
]),
|
|
168
|
+
_: 1
|
|
169
|
+
}, 8, ["title"])
|
|
170
|
+
]),
|
|
171
|
+
_: 1
|
|
172
|
+
}, 8, ["show"]));
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
export {
|
|
176
|
+
re as default
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=UserGrantOverviewDrawer2.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserGrantOverviewDrawer2.vue.js","sources":["../../../../../src/views/system/user/components/UserGrantOverviewDrawer.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 单独授权一览:全系统的例外放在一张表里给超管复核;普通管理员只看得到自己数据范围内用户的记录(后端收口)。\n// 行操作「去调整」把用户交给父页,打开授权菜单弹窗。\n// 取数全交给 SmartTable:翻页、改筛选时它自己丢弃慢的旧响应;抽屉关闭即卸载,下次打开重新取最新数据。\nimport { computed, h, ref } from 'vue'\nimport { NButton, NDrawer, NDrawerContent, NTag, useMessage } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { SmartTable, type SmartTableColumn, type SmartTableInst } from 'smart-naive-table'\nimport TableTotal from '#/components/TableTotal/index.vue'\nimport { userApi } from '#/api'\nimport { translateMenuTitle } from '#/locales/menuTitle'\nimport { translateError } from '#/utils/error'\nimport { SEARCH_ACTIONS, deriveHeaderFilters } from '#/utils/tableFilter'\nimport { TABLE_MIN_ROW_HEIGHT, TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport { UserMenuEffect, UserMenuGrantStatus, type UserMenuGrantPageItem } from '#/types/api'\n\nconst show = defineModel<boolean>('show', { default: false })\nconst emit = defineEmits<{ (e: 'adjust', user: { id: number; name: string }): void }>()\nconst { t } = useI18n()\nconst message = useMessage()\n\n// 父页在授权弹窗保存后调 refresh,让「去调整」改过的记录马上反映在列表里。\n// 抽屉没打开时表格未挂载,tableRef 为空,refresh 什么都不做。\nconst tableRef = ref<SmartTableInst<UserMenuGrantPageItem>>()\ndefineExpose({ refresh: () => tableRef.value?.refresh() })\n\nconst effectOptions = computed(() => [\n { label: t('userGrant.allow'), value: UserMenuEffect.Allow },\n { label: t('userGrant.deny'), value: UserMenuEffect.Deny },\n])\nconst statusOptions = computed(() => [\n { label: t('userGrant.statusActive'), value: UserMenuGrantStatus.Active },\n { label: t('userGrant.statusExpiring'), value: UserMenuGrantStatus.Expiring },\n { label: t('userGrant.statusExpired'), value: UserMenuGrantStatus.Expired },\n])\nconst STATUS_TAG: Record<UserMenuGrantStatus, 'success' | 'warning' | 'default'> = {\n [UserMenuGrantStatus.Active]: 'success',\n [UserMenuGrantStatus.Expiring]: 'warning',\n [UserMenuGrantStatus.Expired]: 'default',\n}\nconst statusLabel = (s: UserMenuGrantStatus) =>\n statusOptions.value.find(o => o.value === s)?.label ?? ''\nconst dash = () => h('span', { class: 'faint' }, '—')\n\n// 这张表有 10 列,比普通列表宽得多。列宽:「授权理由」以外都写死,合计约 1060,理由列拿剩下的(最窄 120)。\n// 抽屉跟着视口铺满、最宽 1440(抽屉内边距与卡片占约 84),所以视口 ≥ 1270 左右表格不出横向滚动条;\n// 更窄时由表格自己横向滚动。「效果」列带筛选漏斗,窄于 102 会被表头撑开,宽度照实写。\nconst columns: SmartTableColumn<UserMenuGrantPageItem>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n // 只作搜索项:目标用户账号或姓名\n {\n key: 'user',\n title: () => t('userGrant.colUser'),\n hideInTable: true,\n search: { actions: SEARCH_ACTIONS.fuzzy },\n },\n {\n key: 'userName',\n title: () => t('userGrant.colUser'),\n width: 140,\n ellipsis: { tooltip: true },\n card: 'title',\n render: r => `${r.userName}(${r.userAccount})`,\n },\n {\n key: 'menuTitle',\n title: () => t('userGrant.colMenu'),\n width: 140,\n ellipsis: { tooltip: true },\n render: r =>\n [r.moduleTitle ? translateMenuTitle(r.moduleTitle) : null, translateMenuTitle(r.menuTitle)]\n .filter(Boolean)\n .join(' · '),\n },\n {\n key: 'effect',\n title: () => t('userGrant.colEffect'),\n width: 102,\n options: effectOptions,\n search: { actions: SEARCH_ACTIONS.exact, props: { clearable: true } },\n render: r =>\n h(\n NTag,\n {\n size: 'small',\n bordered: false,\n type: r.effect === UserMenuEffect.Allow ? 'success' : 'error',\n },\n () => t(r.effect === UserMenuEffect.Allow ? 'userGrant.allow' : 'userGrant.deny'),\n ),\n },\n {\n key: 'status',\n title: () => t('userGrant.colStatus'),\n width: 108,\n options: statusOptions,\n search: { actions: SEARCH_ACTIONS.exact, props: { clearable: true } },\n render: r =>\n h(NTag, { size: 'small', bordered: false, type: STATUS_TAG[r.status] }, () =>\n statusLabel(r.status),\n ),\n },\n {\n key: 'expireTime',\n title: () => t('userGrant.colExpire'),\n width: 160,\n render: r => (r.expireTime ? r.expireTime.replace('T', ' ') : t('userGrant.expireLongTerm')),\n },\n // 只作搜索项:授权人账号或姓名\n {\n key: 'grantor',\n title: () => t('userGrant.colGrantor'),\n hideInTable: true,\n search: { actions: SEARCH_ACTIONS.fuzzy },\n },\n {\n key: 'grantorName',\n title: () => t('userGrant.colGrantor'),\n width: 100,\n ellipsis: { tooltip: true },\n render: r => r.grantorName || dash(),\n },\n { key: 'grantTime', title: () => t('userGrant.colGrantTime'), width: 160, format: 'datetime' },\n {\n key: 'remark',\n title: () => t('userGrant.colRemark'),\n ellipsis: { tooltip: true },\n render: r => r.remark || dash(),\n },\n {\n key: 'op',\n title: () => t('common.operation'),\n width: 84,\n fixed: 'right',\n hideInSetting: true,\n render: r =>\n h(\n NButton,\n {\n size: 'small',\n quaternary: true,\n type: 'primary',\n onClick: () => emit('adjust', { id: r.userId, name: r.userName }),\n },\n () => t('userGrant.adjust'),\n ),\n },\n]\nderiveHeaderFilters(columns)\n</script>\n\n<template>\n <n-drawer v-model:show=\"show\" placement=\"right\" width=\"min(1440px, 100vw)\">\n <n-drawer-content\n :title=\"t('userGrant.overview')\"\n closable\n :body-content-style=\"{ height: '100%', display: 'flex', flexDirection: 'column' }\"\n >\n <SmartTable\n ref=\"tableRef\"\n :columns=\"columns\"\n :fetcher=\"userApi.menuGrantPage\"\n :search=\"{ container: 'table' }\"\n :toolbar=\"TABLE_TOOLBAR\"\n fill-height\n :min-row-height=\"TABLE_MIN_ROW_HEIGHT\"\n card-on-narrow\n storage-key=\"sys-user-menu-grants\"\n @error=\"e => message.error(translateError(e))\"\n >\n <template #pagination-prefix=\"{ itemCount }\">\n <TableTotal :count=\"itemCount\" />\n </template>\n </SmartTable>\n </n-drawer-content>\n </n-drawer>\n</template>\n"],"names":["show","_useModel","__props","emit","__emit","useI18n","message","useMessage","tableRef","ref","__expose","_a","effectOptions","computed","UserMenuEffect","statusOptions","UserMenuGrantStatus","STATUS_TAG","statusLabel","s","dash","h","columns","SEARCH_ACTIONS","r","translateMenuTitle","NTag","NButton","deriveHeaderFilters","_createBlock","_unref","NDrawer","$event","_createVNode","NDrawerContent","SmartTable","userApi","TABLE_TOOLBAR","TABLE_MIN_ROW_HEIGHT","_cache","e","translateError","_withCtx","itemCount","TableTotal"],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { MenuTreeNode } from '../../../../types/menu';
|
|
2
|
+
import { UserMenuEffect, UserMenuGrantItem, UserMenuGrantUpsert } from '../../../../types/api';
|
|
3
|
+
export type TriState = 'follow' | 'allow' | 'deny';
|
|
4
|
+
export interface DraftEntry {
|
|
5
|
+
effect: UserMenuEffect;
|
|
6
|
+
/** 到期日 yyyy-MM-dd;null = 长期 */
|
|
7
|
+
expireDate: string | null;
|
|
8
|
+
/** 基线记录的原始到期时间串,只在日期没被改动时原样带回;新建的记录没有 */
|
|
9
|
+
expireTime?: string | null;
|
|
10
|
+
remark: string | null;
|
|
11
|
+
}
|
|
12
|
+
export type Draft = Map<number, DraftEntry>;
|
|
13
|
+
/** 弹窗只读的原因码 → 文案 key(直接复用后端错误码的 msgKey 文案)。 */
|
|
14
|
+
export declare const READONLY_REASON_KEYS: Record<number, string>;
|
|
15
|
+
/** 本地日期 yyyy-MM-dd。 */
|
|
16
|
+
export declare const formatDate: (d: Date) => string;
|
|
17
|
+
/** 后端到期时间(本地时间串)→ 日期部分。 */
|
|
18
|
+
export declare const toExpireDate: (expireTime?: string | null) => string | null;
|
|
19
|
+
/** 日期 → 后端到期时间:当天最后一秒失效。 */
|
|
20
|
+
export declare const toExpireTime: (date: string | null) => string | null;
|
|
21
|
+
export declare function draftFromGrants(grants: UserMenuGrantItem[]): Draft;
|
|
22
|
+
export declare function triStateOf(draft: Draft, menuId: number): TriState;
|
|
23
|
+
/**
|
|
24
|
+
* 切换三态。从「跟随」进入允许时带上默认到期日(普通管理员授允许必须限时,默认给到上限);
|
|
25
|
+
* 进入拒绝不带(拒绝只会收紧,不要求限时);已有记录切换效果时保留已填的到期日与备注。
|
|
26
|
+
*/
|
|
27
|
+
export declare function setTriState(draft: Draft, menuId: number, next: TriState, defaultExpireDate: string | null): void;
|
|
28
|
+
/** 与打开时的快照比出变更集。按菜单 Id 升序,提交内容稳定。 */
|
|
29
|
+
export declare function diffDraft(baseline: Draft, draft: Draft): {
|
|
30
|
+
upserts: UserMenuGrantUpsert[];
|
|
31
|
+
removes: number[];
|
|
32
|
+
};
|
|
33
|
+
export declare function countDraft(draft: Draft): {
|
|
34
|
+
allow: number;
|
|
35
|
+
deny: number;
|
|
36
|
+
};
|
|
37
|
+
/** 浏览器的今天 + 最长天数;不限时为 null。只在服务端没给出最晚到期日时作后备。 */
|
|
38
|
+
export declare function maxExpireDate(maxDays: number | null | undefined, today: Date): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* 普通管理员授允许的到期日上限(也是默认值);不限时为 null。
|
|
41
|
+
* 服务端给了 delegatedMaxDate 就直接用它:那是服务器本地日期 + 最长天数,与后端校验是同一份计算。
|
|
42
|
+
* 浏览器与服务器不在同一时区时(服务器 UTC、用户东八区),浏览器自己加天数每天凌晨会领先一天,选出被后端拒收的上限。
|
|
43
|
+
* 日期串只做字符串比较,不转成 Date(new Date('yyyy-MM-dd') 按 UTC 解析,在 UTC 以西的时区会差一天)。
|
|
44
|
+
* 没给(自定义的策略实现没提供最晚到期日)才退回浏览器的今天 + delegatedMaxDays。
|
|
45
|
+
*/
|
|
46
|
+
export declare function expiryLimit(effective: {
|
|
47
|
+
delegatedMaxDate?: string | null;
|
|
48
|
+
delegatedMaxDays?: number | null;
|
|
49
|
+
} | null | undefined, today: Date): string | null;
|
|
50
|
+
/** 日期选择器里不可选的日子:今天以前,以及(受限时)最晚到期日以后。三个参数都是 yyyy-MM-dd,按字符串比较。 */
|
|
51
|
+
export declare const isExpiryDateDisabled: (date: string, today: string, max: string | null) => boolean;
|
|
52
|
+
/**
|
|
53
|
+
* 要提交的记录里到期日不合规的菜单 Id:谁都不能选今天以前的日期;受限(max 非空,即 expiryLimit 的结果)时,
|
|
54
|
+
* 「允许」还必须有到期日且不晚于上限。只看变更集,没动的老记录不校验(后端同样只校验变更集)。
|
|
55
|
+
*/
|
|
56
|
+
export declare function invalidExpiry(baseline: Draft, draft: Draft, max: string | null, today: Date): number[];
|
|
57
|
+
/**
|
|
58
|
+
* 拒掉 menuId(连同子孙)后,仍由子树外的有效节点携带的权限码 → 携带它们的节点 Id。
|
|
59
|
+
* 规则同后端 UserMenuGrantRules.LeakedCodes;这里给还没保存的「拒绝」就地提示用,effective 取打开时的有效节点。
|
|
60
|
+
*/
|
|
61
|
+
export declare function leakedCodes(tree: MenuTreeNode[], effective: Set<number>, menuId: number): {
|
|
62
|
+
code: string;
|
|
63
|
+
carriers: number[];
|
|
64
|
+
}[];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { splitPermission as D } from "../../../../types/menu.js";
|
|
2
|
+
import { UserMenuEffect as u } from "../../../../types/api.js";
|
|
3
|
+
const y = {
|
|
4
|
+
42029: "error.user.cannotOperateSelf",
|
|
5
|
+
42007: "error.user.superAdminProtected",
|
|
6
|
+
41005: "error.user.outOfDataScope",
|
|
7
|
+
41007: "error.perm.targetIsDelegatedAdmin"
|
|
8
|
+
}, g = (e) => String(e).padStart(2, "0"), w = (e) => `${e.getFullYear()}-${g(e.getMonth() + 1)}-${g(e.getDate())}`, m = (e) => e ? e.slice(0, 10) : null, S = (e) => e ? `${e}T23:59:59` : null;
|
|
9
|
+
function O(e) {
|
|
10
|
+
return new Map(
|
|
11
|
+
e.map((t) => [
|
|
12
|
+
t.menuId,
|
|
13
|
+
{
|
|
14
|
+
effect: t.effect,
|
|
15
|
+
expireDate: m(t.expireTime),
|
|
16
|
+
expireTime: t.expireTime ?? null,
|
|
17
|
+
remark: t.remark ?? null
|
|
18
|
+
}
|
|
19
|
+
])
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
function I(e, t) {
|
|
23
|
+
const n = e.get(t);
|
|
24
|
+
return n ? n.effect === u.Allow ? "allow" : "deny" : "follow";
|
|
25
|
+
}
|
|
26
|
+
function Y(e, t, n, l) {
|
|
27
|
+
if (n === "follow") {
|
|
28
|
+
e.delete(t);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const o = n === "allow" ? u.Allow : u.Deny, r = e.get(t), i = (r == null ? void 0 : r.expireDate) ?? (o === u.Allow ? l : null);
|
|
32
|
+
e.set(t, {
|
|
33
|
+
effect: o,
|
|
34
|
+
expireDate: i,
|
|
35
|
+
expireTime: r == null ? void 0 : r.expireTime,
|
|
36
|
+
remark: (r == null ? void 0 : r.remark) ?? null
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const p = (e) => (e == null ? void 0 : e.trim()) || null, T = (e, t) => e.effect === t.effect && e.expireDate === t.expireDate && p(e.remark) === p(t.remark), E = (e) => e.expireTime && m(e.expireTime) === e.expireDate ? e.expireTime : S(e.expireDate);
|
|
40
|
+
function M(e, t) {
|
|
41
|
+
const n = [];
|
|
42
|
+
for (const [o, r] of t) {
|
|
43
|
+
const i = e.get(o);
|
|
44
|
+
i && T(i, r) || n.push({
|
|
45
|
+
menuId: o,
|
|
46
|
+
effect: r.effect,
|
|
47
|
+
expireTime: E(r),
|
|
48
|
+
remark: p(r.remark)
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const l = [...e.keys()].filter((o) => !t.has(o));
|
|
52
|
+
return {
|
|
53
|
+
upserts: n.toSorted((o, r) => o.menuId - r.menuId),
|
|
54
|
+
removes: l.toSorted((o, r) => o - r)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function $(e) {
|
|
58
|
+
let t = 0, n = 0;
|
|
59
|
+
for (const l of e.values())
|
|
60
|
+
l.effect === u.Allow ? t++ : n++;
|
|
61
|
+
return { allow: t, deny: n };
|
|
62
|
+
}
|
|
63
|
+
function h(e, t) {
|
|
64
|
+
return e == null ? null : w(new Date(t.getFullYear(), t.getMonth(), t.getDate() + e));
|
|
65
|
+
}
|
|
66
|
+
function F(e, t) {
|
|
67
|
+
return e != null && e.delegatedMaxDate ? e.delegatedMaxDate : h(e == null ? void 0 : e.delegatedMaxDays, t);
|
|
68
|
+
}
|
|
69
|
+
const R = (e, t, n) => e < t || n != null && e > n;
|
|
70
|
+
function L(e, t, n, l) {
|
|
71
|
+
const o = w(l);
|
|
72
|
+
return M(e, t).upserts.filter((r) => {
|
|
73
|
+
const i = m(r.expireTime);
|
|
74
|
+
return i && i < o ? !0 : r.effect !== u.Allow || n == null ? !1 : !i || i > n;
|
|
75
|
+
}).map((r) => r.menuId);
|
|
76
|
+
}
|
|
77
|
+
function N(e, t, n) {
|
|
78
|
+
const l = /* @__PURE__ */ new Map(), o = (s) => {
|
|
79
|
+
for (const a of s)
|
|
80
|
+
l.set(a.id, a), o(a.children);
|
|
81
|
+
};
|
|
82
|
+
o(e);
|
|
83
|
+
const r = l.get(n);
|
|
84
|
+
if (!r) return [];
|
|
85
|
+
const i = /* @__PURE__ */ new Set(), d = (s) => {
|
|
86
|
+
i.add(s.id), s.children.forEach(d);
|
|
87
|
+
};
|
|
88
|
+
d(r);
|
|
89
|
+
const x = new Set([...i].flatMap((s) => D(l.get(s).permission)));
|
|
90
|
+
if (!x.size) return [];
|
|
91
|
+
const f = /* @__PURE__ */ new Map();
|
|
92
|
+
for (const s of t) {
|
|
93
|
+
if (i.has(s)) continue;
|
|
94
|
+
const a = l.get(s);
|
|
95
|
+
if (a)
|
|
96
|
+
for (const c of D(a.permission))
|
|
97
|
+
x.has(c) && f.set(c, [...f.get(c) ?? [], s]);
|
|
98
|
+
}
|
|
99
|
+
return [...f.keys()].toSorted().map((s) => ({ code: s, carriers: f.get(s).toSorted((a, c) => a - c) }));
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
y as READONLY_REASON_KEYS,
|
|
103
|
+
$ as countDraft,
|
|
104
|
+
M as diffDraft,
|
|
105
|
+
O as draftFromGrants,
|
|
106
|
+
F as expiryLimit,
|
|
107
|
+
w as formatDate,
|
|
108
|
+
L as invalidExpiry,
|
|
109
|
+
R as isExpiryDateDisabled,
|
|
110
|
+
N as leakedCodes,
|
|
111
|
+
h as maxExpireDate,
|
|
112
|
+
Y as setTriState,
|
|
113
|
+
m as toExpireDate,
|
|
114
|
+
S as toExpireTime,
|
|
115
|
+
I as triStateOf
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=userGrantState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userGrantState.js","sources":["../../../../../src/views/system/user/components/userGrantState.ts"],"sourcesContent":["// 用户授权弹窗的草稿状态(纯函数,组件只管渲染)。草稿 = 菜单 Id → 单独授权记录;没有记录就是「跟随角色」。\n// 保存只提交变更集:与打开时的快照比,算出新增 / 修改(upserts)与移除(removes),没动的记录不提交。\n// 到期按日期:前端只选日期,新选的日期提交当天 23:59:59;日期没动的老记录原样带回它的到期时间串\n//(后端存的不一定是 23:59:59,只改备注不能把它悄悄改写,更不能让已过期的记录重新生效)。\n// 普通管理员授允许的上限是服务端给出的最晚到期日(服务器本地日期 + 最长天数),校验与展示共用这一天。\nimport { splitPermission, type MenuTreeNode } from '#/types/menu'\nimport { UserMenuEffect, type UserMenuGrantItem, type UserMenuGrantUpsert } from '#/types/api'\n\nexport type TriState = 'follow' | 'allow' | 'deny'\n\nexport interface DraftEntry {\n effect: UserMenuEffect\n /** 到期日 yyyy-MM-dd;null = 长期 */\n expireDate: string | null\n /** 基线记录的原始到期时间串,只在日期没被改动时原样带回;新建的记录没有 */\n expireTime?: string | null\n remark: string | null\n}\nexport type Draft = Map<number, DraftEntry>\n\n/** 弹窗只读的原因码 → 文案 key(直接复用后端错误码的 msgKey 文案)。 */\nexport const READONLY_REASON_KEYS: Record<number, string> = {\n 42029: 'error.user.cannotOperateSelf',\n 42007: 'error.user.superAdminProtected',\n 41005: 'error.user.outOfDataScope',\n 41007: 'error.perm.targetIsDelegatedAdmin',\n}\n\nconst pad = (n: number) => String(n).padStart(2, '0')\n\n/** 本地日期 yyyy-MM-dd。 */\nexport const formatDate = (d: Date) =>\n `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`\n\n/** 后端到期时间(本地时间串)→ 日期部分。 */\nexport const toExpireDate = (expireTime?: string | null) =>\n expireTime ? expireTime.slice(0, 10) : null\n\n/** 日期 → 后端到期时间:当天最后一秒失效。 */\nexport const toExpireTime = (date: string | null) => (date ? `${date}T23:59:59` : null)\n\nexport function draftFromGrants(grants: UserMenuGrantItem[]): Draft {\n return new Map(\n grants.map(g => [\n g.menuId,\n {\n effect: g.effect,\n expireDate: toExpireDate(g.expireTime),\n expireTime: g.expireTime ?? null,\n remark: g.remark ?? null,\n },\n ]),\n )\n}\n\nexport function triStateOf(draft: Draft, menuId: number): TriState {\n const entry = draft.get(menuId)\n if (!entry) return 'follow'\n return entry.effect === UserMenuEffect.Allow ? 'allow' : 'deny'\n}\n\n/**\n * 切换三态。从「跟随」进入允许时带上默认到期日(普通管理员授允许必须限时,默认给到上限);\n * 进入拒绝不带(拒绝只会收紧,不要求限时);已有记录切换效果时保留已填的到期日与备注。\n */\nexport function setTriState(\n draft: Draft,\n menuId: number,\n next: TriState,\n defaultExpireDate: string | null,\n): void {\n if (next === 'follow') {\n draft.delete(menuId)\n return\n }\n const effect = next === 'allow' ? UserMenuEffect.Allow : UserMenuEffect.Deny\n const cur = draft.get(menuId)\n const expireDate = cur?.expireDate ?? (effect === UserMenuEffect.Allow ? defaultExpireDate : null)\n draft.set(menuId, {\n effect,\n expireDate,\n expireTime: cur?.expireTime,\n remark: cur?.remark ?? null,\n })\n}\n\nconst normRemark = (remark: string | null) => remark?.trim() || null\n\nconst sameEntry = (a: DraftEntry, b: DraftEntry) =>\n a.effect === b.effect &&\n a.expireDate === b.expireDate &&\n normRemark(a.remark) === normRemark(b.remark)\n\n/** 提交用的到期时间:日期没动就沿用原始时间串,日期被改动(或新选)才取当天最后一秒。 */\nconst expireTimeOf = (entry: DraftEntry) =>\n entry.expireTime && toExpireDate(entry.expireTime) === entry.expireDate\n ? entry.expireTime\n : toExpireTime(entry.expireDate)\n\n/** 与打开时的快照比出变更集。按菜单 Id 升序,提交内容稳定。 */\nexport function diffDraft(\n baseline: Draft,\n draft: Draft,\n): { upserts: UserMenuGrantUpsert[]; removes: number[] } {\n const upserts: UserMenuGrantUpsert[] = []\n for (const [menuId, entry] of draft) {\n const before = baseline.get(menuId)\n if (before && sameEntry(before, entry)) continue\n upserts.push({\n menuId,\n effect: entry.effect,\n expireTime: expireTimeOf(entry),\n remark: normRemark(entry.remark),\n })\n }\n const removes = [...baseline.keys()].filter(id => !draft.has(id))\n return {\n upserts: upserts.toSorted((a, b) => a.menuId - b.menuId),\n removes: removes.toSorted((a, b) => a - b),\n }\n}\n\nexport function countDraft(draft: Draft): { allow: number; deny: number } {\n let allow = 0\n let deny = 0\n for (const entry of draft.values()) {\n if (entry.effect === UserMenuEffect.Allow) allow++\n else deny++\n }\n return { allow, deny }\n}\n\n/** 浏览器的今天 + 最长天数;不限时为 null。只在服务端没给出最晚到期日时作后备。 */\nexport function maxExpireDate(maxDays: number | null | undefined, today: Date): string | null {\n if (maxDays == null) return null\n return formatDate(new Date(today.getFullYear(), today.getMonth(), today.getDate() + maxDays))\n}\n\n/**\n * 普通管理员授允许的到期日上限(也是默认值);不限时为 null。\n * 服务端给了 delegatedMaxDate 就直接用它:那是服务器本地日期 + 最长天数,与后端校验是同一份计算。\n * 浏览器与服务器不在同一时区时(服务器 UTC、用户东八区),浏览器自己加天数每天凌晨会领先一天,选出被后端拒收的上限。\n * 日期串只做字符串比较,不转成 Date(new Date('yyyy-MM-dd') 按 UTC 解析,在 UTC 以西的时区会差一天)。\n * 没给(自定义的策略实现没提供最晚到期日)才退回浏览器的今天 + delegatedMaxDays。\n */\nexport function expiryLimit(\n effective:\n { delegatedMaxDate?: string | null; delegatedMaxDays?: number | null } | null | undefined,\n today: Date,\n): string | null {\n if (effective?.delegatedMaxDate) return effective.delegatedMaxDate\n return maxExpireDate(effective?.delegatedMaxDays, today)\n}\n\n/** 日期选择器里不可选的日子:今天以前,以及(受限时)最晚到期日以后。三个参数都是 yyyy-MM-dd,按字符串比较。 */\nexport const isExpiryDateDisabled = (date: string, today: string, max: string | null): boolean =>\n date < today || (max != null && date > max)\n\n/**\n * 要提交的记录里到期日不合规的菜单 Id:谁都不能选今天以前的日期;受限(max 非空,即 expiryLimit 的结果)时,\n * 「允许」还必须有到期日且不晚于上限。只看变更集,没动的老记录不校验(后端同样只校验变更集)。\n */\nexport function invalidExpiry(\n baseline: Draft,\n draft: Draft,\n max: string | null,\n today: Date,\n): number[] {\n const todayStr = formatDate(today)\n return diffDraft(baseline, draft)\n .upserts.filter(u => {\n const date = toExpireDate(u.expireTime)\n if (date && date < todayStr) return true\n if (u.effect !== UserMenuEffect.Allow || max == null) return false\n return !date || date > max\n })\n .map(u => u.menuId)\n}\n\n/**\n * 拒掉 menuId(连同子孙)后,仍由子树外的有效节点携带的权限码 → 携带它们的节点 Id。\n * 规则同后端 UserMenuGrantRules.LeakedCodes;这里给还没保存的「拒绝」就地提示用,effective 取打开时的有效节点。\n */\nexport function leakedCodes(\n tree: MenuTreeNode[],\n effective: Set<number>,\n menuId: number,\n): { code: string; carriers: number[] }[] {\n const byId = new Map<number, MenuTreeNode>()\n const index = (nodes: MenuTreeNode[]) => {\n for (const n of nodes) {\n byId.set(n.id, n)\n index(n.children)\n }\n }\n index(tree)\n const root = byId.get(menuId)\n if (!root) return []\n\n const subtree = new Set<number>()\n const collect = (n: MenuTreeNode) => {\n subtree.add(n.id)\n n.children.forEach(collect)\n }\n collect(root)\n const denied = new Set([...subtree].flatMap(id => splitPermission(byId.get(id)!.permission)))\n if (!denied.size) return []\n\n const carriers = new Map<string, number[]>()\n for (const id of effective) {\n if (subtree.has(id)) continue\n const n = byId.get(id)\n if (!n) continue\n for (const code of splitPermission(n.permission))\n if (denied.has(code)) carriers.set(code, [...(carriers.get(code) ?? []), id])\n }\n return [...carriers.keys()]\n .toSorted()\n .map(code => ({ code, carriers: carriers.get(code)!.toSorted((a, b) => a - b) }))\n}\n"],"names":["READONLY_REASON_KEYS","pad","n","formatDate","d","toExpireDate","expireTime","toExpireTime","date","draftFromGrants","grants","g","triStateOf","draft","menuId","entry","UserMenuEffect","setTriState","next","defaultExpireDate","effect","cur","expireDate","normRemark","remark","sameEntry","a","b","expireTimeOf","diffDraft","baseline","upserts","before","removes","id","countDraft","allow","deny","maxExpireDate","maxDays","today","expiryLimit","effective","isExpiryDateDisabled","max","invalidExpiry","todayStr","u","leakedCodes","tree","byId","index","nodes","root","subtree","collect","denied","splitPermission","carriers","code"],"mappings":"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"}
|