smart-admin-web 10.13.1 → 10.15.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/README.md +5 -5
- package/dist/api/index.d.ts +37 -0
- package/dist/api/index.js +35 -14
- package/dist/api/index.js.map +1 -1
- package/dist/api/schema.d.ts +381 -3
- package/dist/assets/icons/ph-subset.json.js +3 -3
- package/dist/assets/smart-logo.svg.js +5 -0
- package/dist/assets/smart-logo.svg.js.map +1 -0
- package/dist/components/SmartLogo.vue.d.ts +3 -0
- package/dist/components/SmartLogo.vue.js +1 -1
- package/dist/components/SmartLogo2.vue.js +22 -64
- package/dist/components/SmartLogo2.vue.js.map +1 -1
- package/dist/composables/useLoginHero.d.ts +2 -0
- package/dist/composables/useLoginHero.js +22 -0
- package/dist/composables/useLoginHero.js.map +1 -0
- package/dist/composables/usePageTitle.js +16 -14
- package/dist/composables/usePageTitle.js.map +1 -1
- package/dist/composables/useSite.d.ts +5 -0
- package/dist/composables/useSite.js +16 -13
- package/dist/composables/useSite.js.map +1 -1
- package/dist/index.js +92 -91
- package/dist/lib/favicon.d.ts +1 -0
- package/dist/lib/favicon.js +12 -0
- package/dist/lib/favicon.js.map +1 -0
- package/dist/lib/loginHero.d.ts +16 -0
- package/dist/lib/loginHero.js +33 -0
- package/dist/lib/loginHero.js.map +1 -0
- package/dist/lib/logoCrop.d.ts +25 -0
- package/dist/lib/logoCrop.js +22 -0
- package/dist/lib/logoCrop.js.map +1 -0
- package/dist/lib/runtime.d.ts +2 -0
- package/dist/lib/runtime.js +1 -1
- package/dist/lib/runtime.js.map +1 -1
- package/dist/lib/securityRules.d.ts +26 -0
- package/dist/lib/securityRules.js +86 -0
- package/dist/lib/securityRules.js.map +1 -0
- package/dist/locales/en-US.d.ts +250 -28
- package/dist/locales/en-US.js +271 -58
- package/dist/locales/en-US.js.map +1 -1
- package/dist/locales/zh-CN.d.ts +250 -28
- package/dist/locales/zh-CN.js +266 -58
- package/dist/locales/zh-CN.js.map +1 -1
- package/dist/router/viewRegistry.js +10 -10
- package/dist/router/viewRegistry.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/utils/oauthBrand.d.ts +34 -9
- package/dist/utils/oauthBrand.js +70 -74
- package/dist/utils/oauthBrand.js.map +1 -1
- package/dist/views/login/components/LoginHeroPanel.vue.d.ts +18 -0
- package/dist/views/login/components/LoginHeroPanel.vue.js +8 -0
- package/dist/views/login/components/LoginHeroPanel.vue.js.map +1 -0
- package/dist/views/login/components/LoginHeroPanel2.vue.js +66 -0
- package/dist/views/login/components/LoginHeroPanel2.vue.js.map +1 -0
- package/dist/views/login/skins/SplitPanel.vue.js +1 -1
- package/dist/views/login/skins/SplitPanel2.vue.js +39 -59
- package/dist/views/login/skins/SplitPanel2.vue.js.map +1 -1
- package/dist/views/system/api-docs/index.vue.js +5 -0
- package/dist/views/system/api-docs/index.vue.js.map +1 -0
- package/dist/views/system/api-docs/index2.vue.js +64 -0
- package/dist/views/system/api-docs/index2.vue.js.map +1 -0
- package/dist/views/system/config/components/AdminMock.vue.js +8 -0
- package/dist/views/system/config/components/AdminMock.vue.js.map +1 -0
- package/dist/views/system/config/components/AdminMock2.vue.js +76 -0
- package/dist/views/system/config/components/AdminMock2.vue.js.map +1 -0
- package/dist/views/system/config/components/AdvancedSection.vue.js +5 -0
- package/dist/views/system/config/components/AdvancedSection.vue.js.map +1 -0
- package/dist/views/system/config/components/AdvancedSection2.vue.js +135 -0
- package/dist/views/system/config/components/AdvancedSection2.vue.js.map +1 -0
- package/dist/views/system/config/components/BrandSection.vue.d.ts +7 -0
- package/dist/views/system/config/components/BrandSection.vue.js +8 -0
- package/dist/views/system/config/components/BrandSection.vue.js.map +1 -0
- package/dist/views/system/config/components/BrandSection2.vue.js +264 -0
- package/dist/views/system/config/components/BrandSection2.vue.js.map +1 -0
- package/dist/views/system/config/components/CfgGroup.vue.d.ts +18 -0
- package/dist/views/system/config/components/CfgGroup.vue.js +8 -0
- package/dist/views/system/config/components/CfgGroup.vue.js.map +1 -0
- package/dist/views/system/config/components/CfgGroup2.vue.js +22 -0
- package/dist/views/system/config/components/CfgGroup2.vue.js.map +1 -0
- package/dist/views/system/config/components/CfgRow.vue.d.ts +26 -0
- package/dist/views/system/config/components/CfgRow.vue.js +9 -0
- package/dist/views/system/config/components/CfgRow.vue.js.map +1 -0
- package/dist/views/system/config/components/CfgRow2.vue.js +53 -0
- package/dist/views/system/config/components/CfgRow2.vue.js.map +1 -0
- package/dist/views/system/config/components/CfgSegment.vue.d.ts +28 -0
- package/dist/views/system/config/components/CfgSegment.vue.js +8 -0
- package/dist/views/system/config/components/CfgSegment.vue.js.map +1 -0
- package/dist/views/system/config/components/CfgSegment2.vue.js +44 -0
- package/dist/views/system/config/components/CfgSegment2.vue.js.map +1 -0
- package/dist/views/system/config/components/ConfigFormModal.vue.js +5 -2
- package/dist/views/system/config/components/ConfigFormModal.vue.js.map +1 -1
- package/dist/views/system/config/components/ConfigFormModal2.vue.js +66 -56
- package/dist/views/system/config/components/ConfigFormModal2.vue.js.map +1 -1
- package/dist/views/system/config/components/JobSection.vue.js +8 -0
- package/dist/views/system/config/components/JobSection.vue.js.map +1 -0
- package/dist/views/system/config/components/JobSection2.vue.js +138 -0
- package/dist/views/system/config/components/JobSection2.vue.js.map +1 -0
- package/dist/views/system/config/components/{GroupExtraConfig.vue.d.ts → LoginMock.vue.d.ts} +3 -4
- package/dist/views/system/config/components/LoginMock.vue.js +8 -0
- package/dist/views/system/config/components/LoginMock.vue.js.map +1 -0
- package/dist/views/system/config/components/LoginMock2.vue.js +121 -0
- package/dist/views/system/config/components/LoginMock2.vue.js.map +1 -0
- package/dist/views/system/config/components/LogoCropper.vue.d.ts +10 -0
- package/dist/views/system/config/components/LogoCropper.vue.js +8 -0
- package/dist/views/system/config/components/LogoCropper.vue.js.map +1 -0
- package/dist/views/system/config/components/LogoCropper2.vue.js +122 -0
- package/dist/views/system/config/components/LogoCropper2.vue.js.map +1 -0
- package/dist/views/system/config/components/LogoEditor.vue.js +8 -0
- package/dist/views/system/config/components/LogoEditor.vue.js.map +1 -0
- package/dist/views/system/config/components/LogoEditor2.vue.js +163 -0
- package/dist/views/system/config/components/LogoEditor2.vue.js.map +1 -0
- package/dist/views/system/config/components/ProviderSheet.vue.d.ts +17 -0
- package/dist/views/system/config/components/ProviderSheet.vue.js +8 -0
- package/dist/views/system/config/components/ProviderSheet.vue.js.map +1 -0
- package/dist/views/system/config/components/ProviderSheet2.vue.js +343 -0
- package/dist/views/system/config/components/ProviderSheet2.vue.js.map +1 -0
- package/dist/views/system/config/components/ScaleToFit.vue.d.ts +17 -0
- package/dist/views/system/config/components/ScaleToFit.vue.js +8 -0
- package/dist/views/system/config/components/ScaleToFit.vue.js.map +1 -0
- package/dist/views/system/config/components/ScaleToFit2.vue.js +41 -0
- package/dist/views/system/config/components/ScaleToFit2.vue.js.map +1 -0
- package/dist/views/system/config/components/SectionLayout.vue.d.ts +28 -0
- package/dist/views/system/config/components/SectionLayout.vue.js +8 -0
- package/dist/views/system/config/components/SectionLayout.vue.js.map +1 -0
- package/dist/views/system/config/components/SectionLayout2.vue.js +45 -0
- package/dist/views/system/config/components/SectionLayout2.vue.js.map +1 -0
- package/dist/views/system/config/components/SecuritySection.vue.js +8 -0
- package/dist/views/system/config/components/SecuritySection.vue.js.map +1 -0
- package/dist/views/system/config/components/SecuritySection2.vue.js +416 -0
- package/dist/views/system/config/components/SecuritySection2.vue.js.map +1 -0
- package/dist/views/system/config/components/SensitiveSection.vue.js +8 -0
- package/dist/views/system/config/components/SensitiveSection.vue.js.map +1 -0
- package/dist/views/system/config/components/SensitiveSection2.vue.js +209 -0
- package/dist/views/system/config/components/SensitiveSection2.vue.js.map +1 -0
- package/dist/views/system/config/components/SigninSection.vue.d.ts +3 -0
- package/dist/views/system/config/components/SigninSection.vue.js +8 -0
- package/dist/views/system/config/components/SigninSection.vue.js.map +1 -0
- package/dist/views/system/config/components/SigninSection2.vue.js +201 -0
- package/dist/views/system/config/components/SigninSection2.vue.js.map +1 -0
- package/dist/views/system/config/components/UploadSection.vue.d.ts +3 -0
- package/dist/views/system/config/components/UploadSection.vue.js +8 -0
- package/dist/views/system/config/components/UploadSection.vue.js.map +1 -0
- package/dist/views/system/config/components/UploadSection2.vue.js +167 -0
- package/dist/views/system/config/components/UploadSection2.vue.js.map +1 -0
- package/dist/views/system/config/draft.d.ts +148 -0
- package/dist/views/system/config/draft.js +170 -0
- package/dist/views/system/config/draft.js.map +1 -0
- package/dist/views/system/config/flash.d.ts +2 -0
- package/dist/views/system/config/flash.js +9 -0
- package/dist/views/system/config/flash.js.map +1 -0
- package/dist/views/system/config/groups.d.ts +35 -7
- package/dist/views/system/config/groups.js +99 -14
- package/dist/views/system/config/groups.js.map +1 -1
- package/dist/views/system/config/index.vue.js +4 -1
- package/dist/views/system/config/index.vue.js.map +1 -1
- package/dist/views/system/config/index2.vue.js +180 -115
- package/dist/views/system/config/index2.vue.js.map +1 -1
- package/dist/views/system/config/providerForm.d.ts +33 -0
- package/dist/views/system/config/providerForm.js +63 -0
- package/dist/views/system/config/providerForm.js.map +1 -0
- package/package.json +4 -4
- package/dist/views/system/config/components/ExternalAuthConfig.vue.js +0 -8
- package/dist/views/system/config/components/ExternalAuthConfig.vue.js.map +0 -1
- package/dist/views/system/config/components/ExternalAuthConfig2.vue.js +0 -218
- package/dist/views/system/config/components/ExternalAuthConfig2.vue.js.map +0 -1
- package/dist/views/system/config/components/GroupExtraConfig.vue.js +0 -8
- package/dist/views/system/config/components/GroupExtraConfig.vue.js.map +0 -1
- package/dist/views/system/config/components/GroupExtraConfig2.vue.js +0 -108
- package/dist/views/system/config/components/GroupExtraConfig2.vue.js.map +0 -1
- package/dist/views/system/config/components/HighSensConfig.vue.js +0 -5
- package/dist/views/system/config/components/HighSensConfig.vue.js.map +0 -1
- package/dist/views/system/config/components/HighSensConfig2.vue.js +0 -134
- package/dist/views/system/config/components/HighSensConfig2.vue.js.map +0 -1
- package/dist/views/system/config/components/JobConfig.vue.js +0 -5
- package/dist/views/system/config/components/JobConfig.vue.js.map +0 -1
- package/dist/views/system/config/components/JobConfig2.vue.js +0 -116
- package/dist/views/system/config/components/JobConfig2.vue.js.map +0 -1
- package/dist/views/system/config/components/OtherConfig.vue.js +0 -5
- package/dist/views/system/config/components/OtherConfig.vue.js.map +0 -1
- package/dist/views/system/config/components/OtherConfig2.vue.js +0 -121
- package/dist/views/system/config/components/OtherConfig2.vue.js.map +0 -1
- package/dist/views/system/config/components/SecurityConfig.vue.js +0 -8
- package/dist/views/system/config/components/SecurityConfig.vue.js.map +0 -1
- package/dist/views/system/config/components/SecurityConfig2.vue.js +0 -456
- package/dist/views/system/config/components/SecurityConfig2.vue.js.map +0 -1
- package/dist/views/system/config/components/SysBaseConfig.vue.js +0 -5
- package/dist/views/system/config/components/SysBaseConfig.vue.js.map +0 -1
- package/dist/views/system/config/components/SysBaseConfig2.vue.js +0 -107
- package/dist/views/system/config/components/SysBaseConfig2.vue.js.map +0 -1
- package/dist/views/system/config/components/UploadConfig.vue.js +0 -5
- package/dist/views/system/config/components/UploadConfig.vue.js.map +0 -1
- package/dist/views/system/config/components/UploadConfig2.vue.js +0 -135
- package/dist/views/system/config/components/UploadConfig2.vue.js.map +0 -1
- /package/dist/views/system/{config/components/ExternalAuthConfig.vue.d.ts → api-docs/index.vue.d.ts} +0 -0
- /package/dist/views/system/config/components/{HighSensConfig.vue.d.ts → AdminMock.vue.d.ts} +0 -0
- /package/dist/views/system/config/components/{JobConfig.vue.d.ts → AdvancedSection.vue.d.ts} +0 -0
- /package/dist/views/system/config/components/{OtherConfig.vue.d.ts → JobSection.vue.d.ts} +0 -0
- /package/dist/views/system/config/components/{SecurityConfig.vue.d.ts → LogoEditor.vue.d.ts} +0 -0
- /package/dist/views/system/config/components/{SysBaseConfig.vue.d.ts → SecuritySection.vue.d.ts} +0 -0
- /package/dist/views/system/config/components/{UploadConfig.vue.d.ts → SensitiveSection.vue.d.ts} +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { defineComponent as B, computed as m, openBlock as a, createBlock as C, unref as e, withCtx as c, createVNode as p, createElementVNode as o, createElementBlock as u, Fragment as _, renderList as D, toDisplayString as i, isRef as A, withDirectives as b, normalizeStyle as I, createCommentVNode as L, createTextVNode as f } from "vue";
|
|
2
|
+
import { NInputNumber as V, NDynamicTags as z } from "naive-ui";
|
|
3
|
+
import { useI18n as F } from "vue-i18n";
|
|
4
|
+
import { JOB_RETENTION_KEY as g, JOB_ALERT_KEY as y } from "../groups.js";
|
|
5
|
+
import { useConfigDraft as J, useDraftFields as K } from "../draft.js";
|
|
6
|
+
import { vFlash as h } from "../flash.js";
|
|
7
|
+
import O from "./SectionLayout.vue.js";
|
|
8
|
+
import j from "./CfgGroup.vue.js";
|
|
9
|
+
import w from "./CfgRow.vue.js";
|
|
10
|
+
const Y = { class: "presets" }, H = ["aria-pressed", "onClick"], M = { class: "unit" }, U = { class: "card tl" }, $ = { class: "tl-text" }, G = { class: "bar" }, P = { key: 0 }, W = { class: "axis" }, X = { class: "card mail" }, q = { class: "mh" }, Q = { class: "mb" }, Z = { class: "err" }, ee = {
|
|
11
|
+
key: 1,
|
|
12
|
+
class: "none"
|
|
13
|
+
}, k = 180, de = /* @__PURE__ */ B({
|
|
14
|
+
__name: "JobSection",
|
|
15
|
+
setup(te) {
|
|
16
|
+
const E = [7, 30, 90, 180], { t } = F(), { values: S } = J(), { num: T, str: x } = K(), s = T(g, 30), v = x(y), d = m({
|
|
17
|
+
get: () => v.value.split(",").map((l) => l.trim()).filter(Boolean),
|
|
18
|
+
set: (l) => {
|
|
19
|
+
v.value = [...new Set(l.map((r) => r.trim()).filter(Boolean))].join(",");
|
|
20
|
+
}
|
|
21
|
+
}), N = m(() => {
|
|
22
|
+
var l;
|
|
23
|
+
return ((l = S["sys.site.title"]) == null ? void 0 : l.trim()) || "SmartAdmin";
|
|
24
|
+
}), R = m(() => `${Math.min(100, Math.max(s.value, 1) / k * 100)}%`);
|
|
25
|
+
return (l, r) => (a(), C(O, {
|
|
26
|
+
title: e(t)("config.tab.job"),
|
|
27
|
+
desc: e(t)("config.job.desc"),
|
|
28
|
+
"preview-title": e(t)("config.job.preview"),
|
|
29
|
+
layout: "slim"
|
|
30
|
+
}, {
|
|
31
|
+
preview: c(() => [
|
|
32
|
+
b((a(), u("div", U, [
|
|
33
|
+
o("div", $, i(e(t)("config.job.previewRetention", { days: e(s) })), 1),
|
|
34
|
+
o("div", G, [
|
|
35
|
+
o("div", {
|
|
36
|
+
class: "keep",
|
|
37
|
+
style: I({ width: R.value })
|
|
38
|
+
}, [
|
|
39
|
+
e(s) >= 20 ? (a(), u("span", P, i(e(t)("config.job.previewKeep", { days: e(s) })), 1)) : L("", !0)
|
|
40
|
+
], 4)
|
|
41
|
+
]),
|
|
42
|
+
o("div", W, [
|
|
43
|
+
o("span", null, i(e(t)("config.job.previewAxisStart", { days: k })), 1),
|
|
44
|
+
o("span", null, i(e(t)("config.job.previewToday")), 1)
|
|
45
|
+
])
|
|
46
|
+
])), [
|
|
47
|
+
[e(h), e(s)]
|
|
48
|
+
]),
|
|
49
|
+
b((a(), u("div", X, [
|
|
50
|
+
d.value.length ? (a(), u(_, { key: 0 }, [
|
|
51
|
+
o("div", q, [
|
|
52
|
+
o("span", null, [
|
|
53
|
+
f(i(e(t)("config.job.previewTo")) + " ", 1),
|
|
54
|
+
o("b", null, i(d.value.join(e(t)("config.preview.sep"))), 1)
|
|
55
|
+
]),
|
|
56
|
+
o("span", null, [
|
|
57
|
+
f(i(e(t)("config.job.previewSubject")) + " ", 1),
|
|
58
|
+
o("b", null, i(e(t)("config.job.previewSubjectText", { site: N.value })), 1)
|
|
59
|
+
])
|
|
60
|
+
]),
|
|
61
|
+
o("div", Q, [
|
|
62
|
+
f(i(e(t)("config.job.previewBody")) + " ", 1),
|
|
63
|
+
o("div", Z, "TimeoutException: " + i(e(t)("config.job.previewError")), 1)
|
|
64
|
+
])
|
|
65
|
+
], 64)) : (a(), u("div", ee, i(e(t)("config.job.previewNoAlert")), 1))
|
|
66
|
+
])), [
|
|
67
|
+
[e(h), d.value.join(",")]
|
|
68
|
+
])
|
|
69
|
+
]),
|
|
70
|
+
default: c(() => [
|
|
71
|
+
p(j, {
|
|
72
|
+
title: e(t)("config.job.groupLog")
|
|
73
|
+
}, {
|
|
74
|
+
default: c(() => [
|
|
75
|
+
p(w, {
|
|
76
|
+
label: e(t)("config.job.logRetentionDays"),
|
|
77
|
+
hint: e(t)("config.job.logRetentionHint"),
|
|
78
|
+
keys: [e(g)]
|
|
79
|
+
}, {
|
|
80
|
+
default: c(() => [
|
|
81
|
+
o("span", Y, [
|
|
82
|
+
(a(), u(_, null, D(E, (n) => o("button", {
|
|
83
|
+
key: n,
|
|
84
|
+
type: "button",
|
|
85
|
+
class: "chip",
|
|
86
|
+
"aria-pressed": e(s) === n,
|
|
87
|
+
onClick: (oe) => s.value = n
|
|
88
|
+
}, i(n), 9, H)), 64))
|
|
89
|
+
]),
|
|
90
|
+
p(e(V), {
|
|
91
|
+
value: e(s),
|
|
92
|
+
"onUpdate:value": r[0] || (r[0] = (n) => A(s) ? s.value = n : null),
|
|
93
|
+
class: "num",
|
|
94
|
+
size: "small",
|
|
95
|
+
min: 1,
|
|
96
|
+
"show-button": !1,
|
|
97
|
+
"input-props": { "aria-label": e(t)("config.job.logRetentionDays") }
|
|
98
|
+
}, null, 8, ["value", "input-props"]),
|
|
99
|
+
o("span", M, i(e(t)("config.security.unit.days")), 1)
|
|
100
|
+
]),
|
|
101
|
+
_: 1
|
|
102
|
+
}, 8, ["label", "hint", "keys"])
|
|
103
|
+
]),
|
|
104
|
+
_: 1
|
|
105
|
+
}, 8, ["title"]),
|
|
106
|
+
p(j, {
|
|
107
|
+
title: e(t)("config.job.groupAlert")
|
|
108
|
+
}, {
|
|
109
|
+
default: c(() => [
|
|
110
|
+
p(w, {
|
|
111
|
+
label: e(t)("config.job.alertEmails"),
|
|
112
|
+
hint: e(t)("config.job.alertEmailsHint"),
|
|
113
|
+
keys: [e(y)],
|
|
114
|
+
tall: "",
|
|
115
|
+
class: "mails"
|
|
116
|
+
}, {
|
|
117
|
+
default: c(() => [
|
|
118
|
+
p(e(z), {
|
|
119
|
+
value: d.value,
|
|
120
|
+
"onUpdate:value": r[1] || (r[1] = (n) => d.value = n),
|
|
121
|
+
size: "small",
|
|
122
|
+
"data-testid": "alert-emails"
|
|
123
|
+
}, null, 8, ["value"])
|
|
124
|
+
]),
|
|
125
|
+
_: 1
|
|
126
|
+
}, 8, ["label", "hint", "keys"])
|
|
127
|
+
]),
|
|
128
|
+
_: 1
|
|
129
|
+
}, 8, ["title"])
|
|
130
|
+
]),
|
|
131
|
+
_: 1
|
|
132
|
+
}, 8, ["title", "desc", "preview-title"]));
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
export {
|
|
136
|
+
de as default
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=JobSection2.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JobSection2.vue.js","sources":["../../../../../src/views/system/config/components/JobSection.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 定时任务:执行日志保留天数 + 失败告警邮箱。\n// 预览:一条时间轴标出日志保留到哪天,再给一封失败告警邮件的样子(收件人读草稿);不填邮箱就不发。\nimport { computed } from 'vue'\nimport { NDynamicTags, NInputNumber } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { JOB_ALERT_KEY, JOB_RETENTION_KEY } from '../groups'\nimport { useConfigDraft, useDraftFields } from '../draft'\nimport { vFlash } from '../flash'\nimport SectionLayout from './SectionLayout.vue'\nimport CfgGroup from './CfgGroup.vue'\nimport CfgRow from './CfgRow.vue'\n\nconst PRESETS = [7, 30, 90, 180]\n/** 时间轴的总跨度(天);超过的按满格画 */\nconst AXIS_DAYS = 180\n\nconst { t } = useI18n()\nconst { values } = useConfigDraft()\nconst { num, str } = useDraftFields()\nconst days = num(JOB_RETENTION_KEY, 30)\nconst emails = str(JOB_ALERT_KEY)\nconst recipients = computed({\n get: () =>\n emails.value\n .split(',')\n .map(e => e.trim())\n .filter(Boolean),\n set: (list: string[]) => {\n emails.value = [...new Set(list.map(e => e.trim()).filter(Boolean))].join(',')\n },\n})\nconst siteTitle = computed(() => values['sys.site.title']?.trim() || 'SmartAdmin')\nconst keepWidth = computed(() => `${Math.min(100, (Math.max(days.value, 1) / AXIS_DAYS) * 100)}%`)\n</script>\n\n<template>\n <SectionLayout\n :title=\"t('config.tab.job')\"\n :desc=\"t('config.job.desc')\"\n :preview-title=\"t('config.job.preview')\"\n layout=\"slim\"\n >\n <CfgGroup :title=\"t('config.job.groupLog')\">\n <CfgRow\n :label=\"t('config.job.logRetentionDays')\"\n :hint=\"t('config.job.logRetentionHint')\"\n :keys=\"[JOB_RETENTION_KEY]\"\n >\n <span class=\"presets\">\n <button\n v-for=\"d in PRESETS\"\n :key=\"d\"\n type=\"button\"\n class=\"chip\"\n :aria-pressed=\"days === d\"\n @click=\"days = d\"\n >\n {{ d }}\n </button>\n </span>\n <n-input-number\n v-model:value=\"days\"\n class=\"num\"\n size=\"small\"\n :min=\"1\"\n :show-button=\"false\"\n :input-props=\"{ 'aria-label': t('config.job.logRetentionDays') }\"\n />\n <span class=\"unit\">{{ t('config.security.unit.days') }}</span>\n </CfgRow>\n </CfgGroup>\n <CfgGroup :title=\"t('config.job.groupAlert')\">\n <CfgRow\n :label=\"t('config.job.alertEmails')\"\n :hint=\"t('config.job.alertEmailsHint')\"\n :keys=\"[JOB_ALERT_KEY]\"\n tall\n class=\"mails\"\n >\n <n-dynamic-tags v-model:value=\"recipients\" size=\"small\" data-testid=\"alert-emails\" />\n </CfgRow>\n </CfgGroup>\n\n <template #preview>\n <div v-flash=\"days\" class=\"card tl\">\n <div class=\"tl-text\">{{ t('config.job.previewRetention', { days }) }}</div>\n <div class=\"bar\">\n <div class=\"keep\" :style=\"{ width: keepWidth }\">\n <span v-if=\"days >= 20\">{{ t('config.job.previewKeep', { days }) }}</span>\n </div>\n </div>\n <div class=\"axis\">\n <span>{{ t('config.job.previewAxisStart', { days: AXIS_DAYS }) }}</span>\n <span>{{ t('config.job.previewToday') }}</span>\n </div>\n </div>\n <div v-flash=\"recipients.join(',')\" class=\"card mail\">\n <template v-if=\"recipients.length\">\n <div class=\"mh\">\n <span>\n {{ t('config.job.previewTo') }}\n <b>{{ recipients.join(t('config.preview.sep')) }}</b>\n </span>\n <span>\n {{ t('config.job.previewSubject') }}\n <b>{{ t('config.job.previewSubjectText', { site: siteTitle }) }}</b>\n </span>\n </div>\n <div class=\"mb\">\n {{ t('config.job.previewBody') }}\n <div class=\"err\">TimeoutException: {{ t('config.job.previewError') }}</div>\n </div>\n </template>\n <div v-else class=\"none\">{{ t('config.job.previewNoAlert') }}</div>\n </div>\n </template>\n </SectionLayout>\n</template>\n\n<style scoped>\n.num {\n width: 72px;\n}\n.num :deep(input) {\n text-align: right;\n}\n.unit {\n font-size: var(--font-size-sm);\n color: var(--color-text-secondary);\n}\n.presets {\n display: inline-flex;\n gap: 4px;\n}\n.chip {\n min-width: 34px;\n padding: 2px 6px;\n border: 0;\n border-radius: 6px;\n background: var(--color-fill);\n font: inherit;\n font-size: var(--font-size-sm);\n line-height: 20px;\n color: var(--color-text-secondary);\n cursor: pointer;\n}\n.chip[aria-pressed='true'] {\n background: var(--color-primary);\n color: #fff;\n}\n.chip:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 1px;\n}\n.mails :deep(.ctl) {\n flex: 1.4;\n}\n.mails :deep(.n-dynamic-tags) {\n justify-content: flex-end;\n}\n.card {\n border-radius: 12px;\n background: var(--color-bg-container);\n}\n.tl {\n padding: 14px 16px 12px;\n}\n.tl-text {\n margin-bottom: 8px;\n font-size: var(--font-size-base);\n font-weight: 500;\n}\n.bar {\n position: relative;\n height: 26px;\n overflow: hidden;\n border-radius: 7px;\n background: repeating-linear-gradient(-45deg, var(--color-fill) 0 6px, transparent 6px 12px);\n}\n.keep {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n display: grid;\n place-items: center;\n border-radius: 7px;\n background: var(--color-primary);\n color: #fff;\n font-size: var(--font-size-sm);\n white-space: nowrap;\n transition: width 0.25s;\n}\n@media (prefers-reduced-motion: reduce) {\n .keep {\n transition: none;\n }\n}\n.axis {\n display: flex;\n justify-content: space-between;\n margin-top: 6px;\n font-size: var(--font-size-sm);\n color: var(--color-text-tertiary);\n}\n.mail {\n overflow: hidden;\n font-size: var(--font-size-base);\n}\n.mh {\n display: grid;\n gap: 3px;\n padding: 10px 14px;\n border-bottom: 0.5px solid var(--color-border);\n font-size: var(--font-size-sm);\n color: var(--color-text-secondary);\n}\n.mh b {\n font-weight: 500;\n color: var(--color-text-primary);\n}\n.mb {\n padding: 12px 14px;\n}\n.err {\n margin-top: 8px;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--color-danger-bg);\n color: var(--color-danger);\n font:\n var(--font-size-sm) ui-monospace,\n SFMono-Regular,\n Menlo,\n Consolas,\n monospace;\n}\n.none {\n padding: 28px 16px;\n text-align: center;\n color: var(--color-text-secondary);\n}\n</style>\n"],"names":["AXIS_DAYS","PRESETS","useI18n","values","useConfigDraft","num","str","useDraftFields","days","JOB_RETENTION_KEY","emails","JOB_ALERT_KEY","recipients","computed","e","list","siteTitle","_a","keepWidth","_createBlock","SectionLayout","_unref","_withDirectives","_openBlock","_createElementBlock","_hoisted_4","_createElementVNode","_hoisted_5","_toDisplayString","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_Fragment","_hoisted_10","_createTextVNode","_hoisted_11","_hoisted_12","_hoisted_13","vFlash","_createVNode","CfgGroup","CfgRow","_hoisted_1","_renderList","d","$event","_hoisted_2","NInputNumber","_hoisted_3","NDynamicTags"],"mappings":"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"}
|
package/dist/views/system/config/components/{GroupExtraConfig.vue.d.ts → LoginMock.vue.d.ts}
RENAMED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { LoginLocale } from '../groups';
|
|
1
2
|
type __VLS_Props = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/** 本 Tab 已渲染成字段的键 */
|
|
5
|
-
claimed: readonly string[];
|
|
3
|
+
locale: LoginLocale;
|
|
4
|
+
formOnly?: boolean;
|
|
6
5
|
};
|
|
7
6
|
declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
8
7
|
declare const _default: typeof __VLS_export;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import o from "./LoginMock2.vue.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
import t from "../../../../_virtual/_plugin-vue_export-helper.js";
|
|
4
|
+
const p = /* @__PURE__ */ t(o, [["__scopeId", "data-v-d1880ff3"]]);
|
|
5
|
+
export {
|
|
6
|
+
p as default
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=LoginMock.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoginMock.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { defineComponent as N, computed as i, openBlock as s, createElementBlock as l, normalizeClass as H, withDirectives as u, createVNode as O, unref as a, createCommentVNode as g, createElementVNode as n, toDisplayString as c, createTextVNode as k, Fragment as K, renderList as Y } from "vue";
|
|
2
|
+
import { useI18n as x } from "vue-i18n";
|
|
3
|
+
import B from "../../../../components/oauth/BrandIcon.vue.js";
|
|
4
|
+
import { resolveLoginHero as F, splitLoginHeroFeatures as I } from "../../../../lib/loginHero.js";
|
|
5
|
+
import V from "../../../login/components/LoginHeroPanel.vue.js";
|
|
6
|
+
import { heroKeys as D, SHOW_FEATURES_KEY as M, SUBTITLE_KEY as R, CAPTCHA_TYPE_KEY as $, SMS_LOGIN_KEY as z, LOGO_KEY as G } from "../groups.js";
|
|
7
|
+
import { useConfigDraft as U, enabledKey as j } from "../draft.js";
|
|
8
|
+
import { vFlash as d } from "../flash.js";
|
|
9
|
+
const J = {
|
|
10
|
+
key: 0,
|
|
11
|
+
class: "hero"
|
|
12
|
+
}, W = { class: "form" }, q = { class: "welcome" }, Q = { class: "sub" }, X = { class: "modes" }, Z = { class: "on" }, ee = {
|
|
13
|
+
key: 0,
|
|
14
|
+
"data-testid": "preview-sms"
|
|
15
|
+
}, te = {
|
|
16
|
+
key: 0,
|
|
17
|
+
class: "captcha"
|
|
18
|
+
}, oe = { class: "go" }, se = {
|
|
19
|
+
class: "oauth",
|
|
20
|
+
"data-testid": "preview-oauth"
|
|
21
|
+
}, le = { class: "divider" }, ie = { class: "btns" }, ne = ["title"], ae = { class: "copy" }, pe = /* @__PURE__ */ N({
|
|
22
|
+
__name: "LoginMock",
|
|
23
|
+
props: {
|
|
24
|
+
locale: {},
|
|
25
|
+
formOnly: { type: Boolean }
|
|
26
|
+
},
|
|
27
|
+
setup(v) {
|
|
28
|
+
const y = v, C = { char: "A7K2", path: "K9x4", math: "3 + 5 = ?" }, { t: A } = x(), E = U(), { values: t } = E, o = (e) => A(e, {}, { locale: y.locale }), p = i(() => D(y.locale)), h = i(
|
|
29
|
+
() => F(
|
|
30
|
+
{
|
|
31
|
+
headline: t[p.value.headline] ?? "",
|
|
32
|
+
highlight: t[p.value.highlight] ?? "",
|
|
33
|
+
features: I(t[p.value.features])
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
headline: `${o("login.headlinePre")}${o("login.headlineAccent")}${o("login.headlinePost")}`,
|
|
37
|
+
highlight: o("login.headlineAccent"),
|
|
38
|
+
features: [o("login.featRbac"), o("login.featScope"), o("login.featPortal")]
|
|
39
|
+
},
|
|
40
|
+
// 缺省视为开:库里没有这个键时登录页照常显示卖点
|
|
41
|
+
t[M] !== "false"
|
|
42
|
+
)
|
|
43
|
+
), f = i(() => {
|
|
44
|
+
var e;
|
|
45
|
+
return ((e = t[R]) == null ? void 0 : e.trim()) || o("login.subtitle");
|
|
46
|
+
}), S = i(() => {
|
|
47
|
+
var e;
|
|
48
|
+
return ((e = t["sys.site.title"]) == null ? void 0 : e.trim()) || "SmartAdmin";
|
|
49
|
+
}), b = i(() => {
|
|
50
|
+
var e;
|
|
51
|
+
return ((e = t["sys.site.copyright"]) == null ? void 0 : e.trim()) || S.value;
|
|
52
|
+
}), P = i(() => t["sys.security.captcha.enabled"] === "true"), T = i(() => C[t[$] || "char"] ?? "A7K2"), L = i(() => t[z] === "true"), _ = i(
|
|
53
|
+
() => E.providerRows.value.filter((e) => e.registered && t[j(e.code)] === "true")
|
|
54
|
+
), w = i(() => _.value.map((e) => e.code).join(","));
|
|
55
|
+
return (e, r) => (s(), l("div", {
|
|
56
|
+
class: H(["mock", { "form-only": v.formOnly }])
|
|
57
|
+
}, [
|
|
58
|
+
v.formOnly ? g("", !0) : u((s(), l("div", J, [
|
|
59
|
+
O(V, {
|
|
60
|
+
headline: h.value.headline,
|
|
61
|
+
highlight: h.value.highlight,
|
|
62
|
+
subtitle: f.value,
|
|
63
|
+
features: h.value.features,
|
|
64
|
+
title: S.value,
|
|
65
|
+
logo: a(t)[a(G)] ?? ""
|
|
66
|
+
}, null, 8, ["headline", "highlight", "subtitle", "features", "title", "logo"])
|
|
67
|
+
])), [
|
|
68
|
+
[a(d), JSON.stringify(h.value)]
|
|
69
|
+
]),
|
|
70
|
+
n("div", W, [
|
|
71
|
+
n("b", q, c(o("login.welcome")), 1),
|
|
72
|
+
u((s(), l("span", Q, [
|
|
73
|
+
k(c(f.value), 1)
|
|
74
|
+
])), [
|
|
75
|
+
[a(d), f.value]
|
|
76
|
+
]),
|
|
77
|
+
u((s(), l("div", X, [
|
|
78
|
+
n("span", Z, c(o("login.accountLogin")), 1),
|
|
79
|
+
L.value ? (s(), l("span", ee, c(o("login.smsLogin")), 1)) : g("", !0)
|
|
80
|
+
])), [
|
|
81
|
+
[a(d), L.value]
|
|
82
|
+
]),
|
|
83
|
+
r[1] || (r[1] = n("i", { class: "in" }, null, -1)),
|
|
84
|
+
r[2] || (r[2] = n("i", { class: "in" }, null, -1)),
|
|
85
|
+
P.value ? (s(), l("div", te, [
|
|
86
|
+
r[0] || (r[0] = n("i", { class: "in" }, null, -1)),
|
|
87
|
+
n("span", null, c(T.value), 1)
|
|
88
|
+
])) : g("", !0),
|
|
89
|
+
n("span", oe, c(o("login.submit")), 1),
|
|
90
|
+
u((s(), l("div", se, [
|
|
91
|
+
_.value.length ? (s(), l(K, { key: 0 }, [
|
|
92
|
+
n("div", le, c(a(A)("config.externalAuth.previewOther")), 1),
|
|
93
|
+
n("div", ie, [
|
|
94
|
+
(s(!0), l(K, null, Y(_.value, (m) => (s(), l("span", {
|
|
95
|
+
key: m.code,
|
|
96
|
+
title: m.displayName
|
|
97
|
+
}, [
|
|
98
|
+
O(B, {
|
|
99
|
+
code: m.code,
|
|
100
|
+
icon: m.icon,
|
|
101
|
+
size: 20
|
|
102
|
+
}, null, 8, ["code", "icon"])
|
|
103
|
+
], 8, ne))), 128))
|
|
104
|
+
])
|
|
105
|
+
], 64)) : g("", !0)
|
|
106
|
+
])), [
|
|
107
|
+
[a(d), w.value]
|
|
108
|
+
]),
|
|
109
|
+
u((s(), l("div", ae, [
|
|
110
|
+
k("© " + c(b.value), 1)
|
|
111
|
+
])), [
|
|
112
|
+
[a(d), b.value]
|
|
113
|
+
])
|
|
114
|
+
])
|
|
115
|
+
], 2));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
export {
|
|
119
|
+
pe as default
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=LoginMock2.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoginMock2.vue.js","sources":["../../../../../src/views/system/config/components/LoginMock.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 缩小版登录页(880×540 设计尺寸,外面 ScaleToFit 等比缩放)。form-only 只画右侧登录框(440×540),\n// 给「登录方式」页用。Hero 文案、验证码、短信登录入口、第三方按钮、页脚版权都读草稿——\n// 别的分类改了,这里一样跟着变。\nimport { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\nimport BrandIcon from '#/components/oauth/BrandIcon.vue'\nimport { resolveLoginHero, splitLoginHeroFeatures } from '#/lib/loginHero'\nimport LoginHeroPanel from '#/views/login/components/LoginHeroPanel.vue'\nimport {\n CAPTCHA_TYPE_KEY,\n heroKeys,\n LOGO_KEY,\n SHOW_FEATURES_KEY,\n SMS_LOGIN_KEY,\n SUBTITLE_KEY,\n type LoginLocale,\n} from '../groups'\nimport { enabledKey, useConfigDraft } from '../draft'\nimport { vFlash } from '../flash'\n\nconst props = defineProps<{ locale: LoginLocale; formOnly?: boolean }>()\n\nconst CAPTCHA_SAMPLE: Record<string, string> = { char: 'A7K2', path: 'K9x4', math: '3 + 5 = ?' }\n\nconst { t } = useI18n()\nconst draft = useConfigDraft()\nconst { values } = draft\nconst tr = (key: string) => t(key, {}, { locale: props.locale })\nconst keys = computed(() => heroKeys(props.locale))\n\nconst hero = computed(() =>\n resolveLoginHero(\n {\n headline: values[keys.value.headline] ?? '',\n highlight: values[keys.value.highlight] ?? '',\n features: splitLoginHeroFeatures(values[keys.value.features]),\n },\n {\n headline: `${tr('login.headlinePre')}${tr('login.headlineAccent')}${tr('login.headlinePost')}`,\n highlight: tr('login.headlineAccent'),\n features: [tr('login.featRbac'), tr('login.featScope'), tr('login.featPortal')],\n },\n // 缺省视为开:库里没有这个键时登录页照常显示卖点\n values[SHOW_FEATURES_KEY] !== 'false',\n ),\n)\nconst subtitle = computed(() => values[SUBTITLE_KEY]?.trim() || tr('login.subtitle'))\nconst siteTitle = computed(() => values['sys.site.title']?.trim() || 'SmartAdmin')\nconst copyright = computed(() => values['sys.site.copyright']?.trim() || siteTitle.value)\nconst captchaOn = computed(() => values['sys.security.captcha.enabled'] === 'true')\nconst captchaSample = computed(() => CAPTCHA_SAMPLE[values[CAPTCHA_TYPE_KEY] || 'char'] ?? 'A7K2')\nconst smsOn = computed(() => values[SMS_LOGIN_KEY] === 'true')\nconst oauth = computed(() =>\n draft.providerRows.value.filter(p => p.registered && values[enabledKey(p.code)] === 'true'),\n)\nconst oauthSig = computed(() => oauth.value.map(p => p.code).join(','))\n</script>\n\n<template>\n <div :class=\"['mock', { 'form-only': formOnly }]\">\n <div v-if=\"!formOnly\" v-flash=\"JSON.stringify(hero)\" class=\"hero\">\n <LoginHeroPanel\n :headline=\"hero.headline\"\n :highlight=\"hero.highlight\"\n :subtitle=\"subtitle\"\n :features=\"hero.features\"\n :title=\"siteTitle\"\n :logo=\"values[LOGO_KEY] ?? ''\"\n />\n </div>\n <div class=\"form\">\n <b class=\"welcome\">{{ tr('login.welcome') }}</b>\n <span v-flash=\"subtitle\" class=\"sub\">{{ subtitle }}</span>\n <div v-flash=\"smsOn\" class=\"modes\">\n <span class=\"on\">{{ tr('login.accountLogin') }}</span>\n <span v-if=\"smsOn\" data-testid=\"preview-sms\">{{ tr('login.smsLogin') }}</span>\n </div>\n <i class=\"in\" />\n <i class=\"in\" />\n <div v-if=\"captchaOn\" class=\"captcha\">\n <i class=\"in\" />\n <span>{{ captchaSample }}</span>\n </div>\n <span class=\"go\">{{ tr('login.submit') }}</span>\n <div v-flash=\"oauthSig\" class=\"oauth\" data-testid=\"preview-oauth\">\n <template v-if=\"oauth.length\">\n <div class=\"divider\">{{ t('config.externalAuth.previewOther') }}</div>\n <div class=\"btns\">\n <span v-for=\"p in oauth\" :key=\"p.code\" :title=\"p.displayName\">\n <BrandIcon :code=\"p.code\" :icon=\"p.icon\" :size=\"20\" />\n </span>\n </div>\n </template>\n </div>\n <div v-flash=\"copyright\" class=\"copy\">© {{ copyright }}</div>\n </div>\n </div>\n</template>\n\n<style scoped>\n.mock {\n display: grid;\n grid-template-columns: 1.15fr 1fr;\n width: 880px;\n height: 540px;\n overflow: hidden;\n border-radius: 10px;\n background: var(--color-bg-container);\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);\n}\n.mock.form-only {\n grid-template-columns: 1fr;\n width: 440px;\n}\n.hero {\n display: flex;\n align-items: center;\n padding: 32px 36px;\n overflow: hidden;\n background:\n radial-gradient(\n 60% 70% at 20% 15%,\n color-mix(in srgb, var(--color-primary) 18%, transparent),\n transparent 72%\n ),\n var(--color-bg-body);\n}\n.hero :deep(.headline) {\n font-size: 30px;\n line-height: 1.3;\n}\n.hero :deep(.bar) {\n margin: 22px 0 16px;\n}\n.hero :deep(.sub) {\n margin: 12px 0 18px;\n}\n.form {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 44px 40px 18px;\n font-size: 13px;\n}\n.welcome {\n font-size: 22px;\n}\n.sub {\n margin: -8px 0 4px;\n border-radius: 6px;\n color: var(--color-text-secondary);\n}\n.modes {\n display: flex;\n gap: 18px;\n border-radius: 6px;\n color: var(--color-text-secondary);\n}\n.modes .on {\n padding-bottom: 3px;\n border-bottom: 2px solid var(--color-primary);\n color: var(--color-text-primary);\n font-weight: 600;\n}\n.in {\n display: block;\n height: 40px;\n border-radius: 8px;\n background: var(--color-fill);\n}\n.captcha {\n display: flex;\n gap: 8px;\n}\n.captcha .in {\n flex: 1;\n}\n.captcha span {\n display: grid;\n place-items: center;\n width: 104px;\n border-radius: 8px;\n font-weight: 700;\n letter-spacing: 1px;\n color: var(--color-text-secondary);\n background: repeating-linear-gradient(\n 45deg,\n var(--color-fill) 0 4px,\n var(--color-bg-body) 4px 8px\n );\n}\n.go {\n display: grid;\n place-items: center;\n height: 42px;\n border-radius: 8px;\n color: #fff;\n background: var(--color-primary);\n font-weight: 600;\n}\n.oauth {\n min-height: 20px;\n border-radius: 8px;\n}\n.divider {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n color: var(--color-text-tertiary);\n}\n.divider::before,\n.divider::after {\n content: '';\n flex: 1;\n border-top: 1px solid var(--color-border);\n}\n.btns {\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n gap: 12px;\n margin-top: 12px;\n}\n.btns span {\n display: grid;\n place-items: center;\n width: 40px;\n height: 40px;\n border: 1px solid var(--color-border);\n border-radius: 50%;\n}\n.copy {\n margin-top: auto;\n border-radius: 6px;\n text-align: center;\n font-size: 12px;\n color: var(--color-text-tertiary);\n}\n</style>\n"],"names":["props","__props","CAPTCHA_SAMPLE","t","useI18n","draft","useConfigDraft","values","tr","key","keys","computed","heroKeys","hero","resolveLoginHero","splitLoginHeroFeatures","SHOW_FEATURES_KEY","subtitle","_a","SUBTITLE_KEY","siteTitle","copyright","captchaOn","captchaSample","CAPTCHA_TYPE_KEY","smsOn","SMS_LOGIN_KEY","oauth","p","enabledKey","oauthSig","_createElementBlock","_withDirectives","_openBlock","_hoisted_1","_createVNode","LoginHeroPanel","_unref","LOGO_KEY","_createElementVNode","_hoisted_2","_hoisted_3","_toDisplayString","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_Fragment","_hoisted_11","_hoisted_12","_renderList","BrandIcon","_hoisted_14","_createTextVNode"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
file: File;
|
|
3
|
+
};
|
|
4
|
+
/** 按当前取景导出 PNG。 */
|
|
5
|
+
declare function crop(): Promise<Blob>;
|
|
6
|
+
declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {
|
|
7
|
+
crop: typeof crop;
|
|
8
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
declare const _default: typeof __VLS_export;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import o from "./LogoCropper2.vue.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
import r from "../../../../_virtual/_plugin-vue_export-helper.js";
|
|
4
|
+
const m = /* @__PURE__ */ r(o, [["__scopeId", "data-v-a92c4a40"]]);
|
|
5
|
+
export {
|
|
6
|
+
m as default
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=LogoCropper.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LogoCropper.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { defineComponent as I, ref as c, computed as v, watch as k, onBeforeUnmount as W, openBlock as K, createElementBlock as N, createElementVNode as i, withModifiers as V, unref as p, normalizeStyle as g, toDisplayString as P, createVNode as Y } from "vue";
|
|
2
|
+
import { NSlider as X } from "naive-ui";
|
|
3
|
+
import { useI18n as F } from "vue-i18n";
|
|
4
|
+
import { coverScale as H, clampOffset as x, sourceRect as Q } from "../../../../lib/logoCrop.js";
|
|
5
|
+
const q = { class: "cropper" }, G = ["aria-label"], J = ["src"], Z = { class: "zoom" }, ee = { class: "tip" }, b = 280, f = 232, y = 256, le = /* @__PURE__ */ I({
|
|
6
|
+
__name: "LogoCropper",
|
|
7
|
+
props: {
|
|
8
|
+
file: {}
|
|
9
|
+
},
|
|
10
|
+
setup(L, { expose: R }) {
|
|
11
|
+
const $ = L, { t: m } = F(), h = c(null), u = c(""), n = c({ w: 1, h: 1 }), o = c(1), a = c({ x: 0, y: 0 }), E = v(() => H(n.value.w, n.value.h, f)), d = v(() => E.value * o.value), s = v(() => ({
|
|
12
|
+
width: n.value.w,
|
|
13
|
+
height: n.value.h,
|
|
14
|
+
frame: f,
|
|
15
|
+
scale: d.value,
|
|
16
|
+
...a.value
|
|
17
|
+
})), M = v(() => ({
|
|
18
|
+
width: `${n.value.w * d.value}px`,
|
|
19
|
+
height: `${n.value.h * d.value}px`,
|
|
20
|
+
transform: `translate(calc(-50% + ${a.value.x}px), calc(-50% + ${a.value.y}px))`
|
|
21
|
+
}));
|
|
22
|
+
k(
|
|
23
|
+
() => $.file,
|
|
24
|
+
(e) => {
|
|
25
|
+
u.value && URL.revokeObjectURL(u.value), u.value = URL.createObjectURL(e), o.value = 1, a.value = { x: 0, y: 0 };
|
|
26
|
+
},
|
|
27
|
+
{ immediate: !0 }
|
|
28
|
+
), W(() => URL.revokeObjectURL(u.value));
|
|
29
|
+
function D(e) {
|
|
30
|
+
const t = e.target;
|
|
31
|
+
n.value = { w: t.naturalWidth || 1, h: t.naturalHeight || 1 };
|
|
32
|
+
}
|
|
33
|
+
k(o, () => {
|
|
34
|
+
a.value = x(s.value);
|
|
35
|
+
});
|
|
36
|
+
let l = null;
|
|
37
|
+
function S(e) {
|
|
38
|
+
e.currentTarget.setPointerCapture(e.pointerId), l = { x: e.clientX, y: e.clientY, ox: a.value.x, oy: a.value.y };
|
|
39
|
+
}
|
|
40
|
+
function z(e) {
|
|
41
|
+
l && (a.value = x({
|
|
42
|
+
...s.value,
|
|
43
|
+
x: l.ox + e.clientX - l.x,
|
|
44
|
+
y: l.oy + e.clientY - l.y
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
function w() {
|
|
48
|
+
l = null;
|
|
49
|
+
}
|
|
50
|
+
function A(e) {
|
|
51
|
+
const t = e.shiftKey ? 20 : 5, r = {
|
|
52
|
+
ArrowLeft: [-t, 0],
|
|
53
|
+
ArrowRight: [t, 0],
|
|
54
|
+
ArrowUp: [0, -t],
|
|
55
|
+
ArrowDown: [0, t]
|
|
56
|
+
};
|
|
57
|
+
r[e.key] ? (e.preventDefault(), a.value = x({
|
|
58
|
+
...s.value,
|
|
59
|
+
x: a.value.x + r[e.key][0],
|
|
60
|
+
y: a.value.y + r[e.key][1]
|
|
61
|
+
})) : e.key === "+" || e.key === "=" ? (e.preventDefault(), o.value = Math.min(4, o.value + 0.1)) : e.key === "-" && (e.preventDefault(), o.value = Math.max(1, o.value - 0.1));
|
|
62
|
+
}
|
|
63
|
+
function B(e) {
|
|
64
|
+
o.value = Math.min(4, Math.max(1, o.value - e.deltaY * 2e-3));
|
|
65
|
+
}
|
|
66
|
+
function O() {
|
|
67
|
+
const e = document.createElement("canvas");
|
|
68
|
+
e.width = e.height = y;
|
|
69
|
+
const t = e.getContext("2d");
|
|
70
|
+
if (!t || !h.value) return Promise.reject(new Error("canvas unavailable"));
|
|
71
|
+
const { sx: r, sy: T, size: _ } = Q(s.value);
|
|
72
|
+
return t.imageSmoothingQuality = "high", t.drawImage(h.value, r, T, _, _, 0, 0, y, y), new Promise(
|
|
73
|
+
(j, C) => e.toBlob((U) => U ? j(U) : C(new Error("toBlob failed")), "image/png")
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return R({ crop: O }), (e, t) => (K(), N("div", q, [
|
|
77
|
+
i("div", {
|
|
78
|
+
class: "viewport",
|
|
79
|
+
style: g({ width: `${b}px`, height: `${b}px` }),
|
|
80
|
+
tabindex: "0",
|
|
81
|
+
role: "application",
|
|
82
|
+
"aria-label": p(m)("config.logo.cropTip"),
|
|
83
|
+
onKeydown: A,
|
|
84
|
+
onPointerdown: S,
|
|
85
|
+
onPointermove: z,
|
|
86
|
+
onPointerup: w,
|
|
87
|
+
onPointercancel: w,
|
|
88
|
+
onWheel: V(B, ["prevent"])
|
|
89
|
+
}, [
|
|
90
|
+
i("img", {
|
|
91
|
+
ref_key: "img",
|
|
92
|
+
ref: h,
|
|
93
|
+
src: u.value,
|
|
94
|
+
style: g(M.value),
|
|
95
|
+
alt: "",
|
|
96
|
+
draggable: "false",
|
|
97
|
+
onLoad: D
|
|
98
|
+
}, null, 44, J),
|
|
99
|
+
i("div", {
|
|
100
|
+
class: "frame",
|
|
101
|
+
style: g({ width: `${f}px`, height: `${f}px` })
|
|
102
|
+
}, null, 4)
|
|
103
|
+
], 44, G),
|
|
104
|
+
i("div", Z, [
|
|
105
|
+
i("span", null, P(p(m)("config.logo.zoom")), 1),
|
|
106
|
+
Y(p(X), {
|
|
107
|
+
value: o.value,
|
|
108
|
+
"onUpdate:value": t[0] || (t[0] = (r) => o.value = r),
|
|
109
|
+
min: 1,
|
|
110
|
+
max: 4,
|
|
111
|
+
step: 0.01,
|
|
112
|
+
tooltip: !1
|
|
113
|
+
}, null, 8, ["value"])
|
|
114
|
+
]),
|
|
115
|
+
i("p", ee, P(p(m)("config.logo.cropTip")), 1)
|
|
116
|
+
]));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
export {
|
|
120
|
+
le as default
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=LogoCropper2.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LogoCropper2.vue.js","sources":["../../../../../src/views/system/config/components/LogoCropper.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 正方形 Logo 裁剪:拖动平移、滑块或滚轮缩放,导出 256×256 PNG。\n// 只需要这三件事,所以不引裁剪库;几何计算在 lib/logoCrop.ts(有单测)。\nimport { computed, onBeforeUnmount, ref, watch } from 'vue'\nimport { NSlider } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { clampOffset, coverScale, sourceRect } from '#/lib/logoCrop'\n\nconst props = defineProps<{ file: File }>()\n\n/** 视口边长与取景框边长(px) */\nconst VIEW = 280\nconst FRAME = 232\nconst OUTPUT = 256\n\nconst { t } = useI18n()\nconst img = ref<HTMLImageElement | null>(null)\nconst url = ref('')\nconst natural = ref({ w: 1, h: 1 })\nconst zoom = ref(1)\nconst offset = ref({ x: 0, y: 0 })\n\nconst base = computed(() => coverScale(natural.value.w, natural.value.h, FRAME))\nconst scale = computed(() => base.value * zoom.value)\nconst state = computed(() => ({\n width: natural.value.w,\n height: natural.value.h,\n frame: FRAME,\n scale: scale.value,\n ...offset.value,\n}))\nconst imgStyle = computed(() => ({\n width: `${natural.value.w * scale.value}px`,\n height: `${natural.value.h * scale.value}px`,\n transform: `translate(calc(-50% + ${offset.value.x}px), calc(-50% + ${offset.value.y}px))`,\n}))\n\nwatch(\n () => props.file,\n file => {\n if (url.value) URL.revokeObjectURL(url.value)\n url.value = URL.createObjectURL(file)\n zoom.value = 1\n offset.value = { x: 0, y: 0 }\n },\n { immediate: true },\n)\nonBeforeUnmount(() => URL.revokeObjectURL(url.value))\n\nfunction onLoad(e: Event) {\n const el = e.target as HTMLImageElement\n natural.value = { w: el.naturalWidth || 1, h: el.naturalHeight || 1 }\n}\n\n// 缩放后重新收边,保证取景框里始终是图\nwatch(zoom, () => {\n offset.value = clampOffset(state.value)\n})\n\nlet drag: { x: number; y: number; ox: number; oy: number } | null = null\nfunction onPointerDown(e: PointerEvent) {\n ;(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId)\n drag = { x: e.clientX, y: e.clientY, ox: offset.value.x, oy: offset.value.y }\n}\nfunction onPointerMove(e: PointerEvent) {\n if (!drag) return\n offset.value = clampOffset({\n ...state.value,\n x: drag.ox + e.clientX - drag.x,\n y: drag.oy + e.clientY - drag.y,\n })\n}\nfunction onPointerUp() {\n drag = null\n}\n/** 键盘替代拖动:方向键平移(Shift 大步),+ / - 缩放 */\nfunction onKey(e: KeyboardEvent) {\n const step = e.shiftKey ? 20 : 5\n const d: Record<string, [number, number]> = {\n ArrowLeft: [-step, 0],\n ArrowRight: [step, 0],\n ArrowUp: [0, -step],\n ArrowDown: [0, step],\n }\n if (d[e.key]) {\n e.preventDefault()\n offset.value = clampOffset({\n ...state.value,\n x: offset.value.x + d[e.key][0],\n y: offset.value.y + d[e.key][1],\n })\n } else if (e.key === '+' || e.key === '=') {\n e.preventDefault()\n zoom.value = Math.min(4, zoom.value + 0.1)\n } else if (e.key === '-') {\n e.preventDefault()\n zoom.value = Math.max(1, zoom.value - 0.1)\n }\n}\n\nfunction onWheel(e: WheelEvent) {\n zoom.value = Math.min(4, Math.max(1, zoom.value - e.deltaY * 0.002))\n}\n\n/** 按当前取景导出 PNG。 */\nfunction crop(): Promise<Blob> {\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = OUTPUT\n const ctx = canvas.getContext('2d')\n if (!ctx || !img.value) return Promise.reject(new Error('canvas unavailable'))\n const { sx, sy, size } = sourceRect(state.value)\n ctx.imageSmoothingQuality = 'high'\n ctx.drawImage(img.value, sx, sy, size, size, 0, 0, OUTPUT, OUTPUT)\n return new Promise((resolve, reject) =>\n canvas.toBlob(b => (b ? resolve(b) : reject(new Error('toBlob failed'))), 'image/png'),\n )\n}\n\ndefineExpose({ crop })\n</script>\n\n<template>\n <div class=\"cropper\">\n <div\n class=\"viewport\"\n :style=\"{ width: `${VIEW}px`, height: `${VIEW}px` }\"\n tabindex=\"0\"\n role=\"application\"\n :aria-label=\"t('config.logo.cropTip')\"\n @keydown=\"onKey\"\n @pointerdown=\"onPointerDown\"\n @pointermove=\"onPointerMove\"\n @pointerup=\"onPointerUp\"\n @pointercancel=\"onPointerUp\"\n @wheel.prevent=\"onWheel\"\n >\n <img ref=\"img\" :src=\"url\" :style=\"imgStyle\" alt=\"\" draggable=\"false\" @load=\"onLoad\" />\n <div class=\"frame\" :style=\"{ width: `${FRAME}px`, height: `${FRAME}px` }\" />\n </div>\n <div class=\"zoom\">\n <span>{{ t('config.logo.zoom') }}</span>\n <n-slider v-model:value=\"zoom\" :min=\"1\" :max=\"4\" :step=\"0.01\" :tooltip=\"false\" />\n </div>\n <p class=\"tip\">{{ t('config.logo.cropTip') }}</p>\n </div>\n</template>\n\n<style scoped>\n.cropper {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n}\n.viewport {\n position: relative;\n overflow: hidden;\n border-radius: var(--radius-lg);\n cursor: grab;\n touch-action: none;\n user-select: none;\n background: repeating-conic-gradient(#e5e7eb 0 25%, #fff 0 50%) 0 0 / 16px 16px;\n}\n.viewport:active {\n cursor: grabbing;\n}\n.viewport:focus-visible {\n outline: 2px solid var(--color-primary);\n outline-offset: 3px;\n}\n.viewport img {\n position: absolute;\n left: 50%;\n top: 50%;\n max-width: none;\n pointer-events: none;\n}\n.frame {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n border: 2px solid #fff;\n border-radius: 12px;\n box-shadow: 0 0 0 999px rgba(0, 0, 0, 0.45);\n pointer-events: none;\n}\n.zoom {\n display: flex;\n align-items: center;\n gap: 12px;\n width: 280px;\n font-size: var(--font-size-sm);\n color: var(--color-text-tertiary);\n}\n.zoom span {\n flex: none;\n}\n.tip {\n margin: 0;\n font-size: var(--font-size-sm);\n color: var(--color-text-tertiary);\n}\n</style>\n"],"names":["VIEW","FRAME","OUTPUT","props","__props","t","useI18n","img","ref","url","natural","zoom","offset","base","computed","coverScale","scale","state","imgStyle","watch","file","onBeforeUnmount","onLoad","el","clampOffset","drag","onPointerDown","onPointerMove","onPointerUp","onKey","step","d","onWheel","crop","canvas","ctx","sx","sy","size","sourceRect","resolve","reject","b","__expose","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_normalizeStyle","_unref","_hoisted_4","_createVNode","NSlider","$event","_hoisted_5","_toDisplayString"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import o from "./LogoEditor2.vue.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
import t from "../../../../_virtual/_plugin-vue_export-helper.js";
|
|
4
|
+
const p = /* @__PURE__ */ t(o, [["__scopeId", "data-v-cec63628"]]);
|
|
5
|
+
export {
|
|
6
|
+
p as default
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=LogoEditor.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LogoEditor.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|