@liujitcn/kratos-admin-core 0.0.28 → 0.0.31
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 +7 -6
- package/build/auto-imports.json +73 -0
- package/build/plugins.ts +2 -58
- package/dist/package/declarations/src/api/base/mfa.d.ts +26 -0
- package/dist/package/declarations/src/components/Dialog/FormDialog.vue.d.ts +3 -3
- package/dist/package/declarations/src/components/Dialog/PasswordVerifyDialog.vue.d.ts +65 -0
- package/dist/package/declarations/src/components/Dialog/ProDialog.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/Grid/components/GridItem.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/Mfa/MfaRecoveryCodesDialog.vue.d.ts +19 -0
- package/dist/package/declarations/src/components/Mfa/MfaSetupPanel.vue.d.ts +8 -0
- package/dist/package/declarations/src/components/ProForm/components/ProFormItem.vue.d.ts +4 -4
- package/dist/package/declarations/src/components/ProForm/index.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/ProForm/interface/index.d.ts +4 -1
- package/dist/package/declarations/src/components/RichTextPreview/index.vue.d.ts +8 -0
- package/dist/package/declarations/src/components/Upload/Img.vue.d.ts +1 -1
- package/dist/package/declarations/src/components/Upload/Imgs.vue.d.ts +1 -1
- package/dist/package/declarations/src/layouts/components/LockScreen.vue.d.ts +3 -0
- package/dist/package/declarations/src/locales/generated.d.ts +168 -0
- package/dist/package/declarations/src/locales/index.d.ts +2 -0
- package/dist/package/declarations/src/rpc/base/v1/login.d.ts +33 -3
- package/dist/package/declarations/src/rpc/base/v1/mfa.d.ts +152 -0
- package/dist/package/declarations/src/rpc/base/v1/oauth.d.ts +25 -0
- package/dist/package/declarations/src/rpc/base/v1/oauth_client.d.ts +23 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +4 -4
- package/dist/package/declarations/src/rpc/system/admin/v1/base_i18n.d.ts +27 -23
- package/dist/package/declarations/src/security.d.ts +2 -0
- package/dist/package/declarations/src/stores/interface/index.d.ts +14 -1
- package/dist/package/declarations/src/stores/modules/lockScreen.d.ts +20 -0
- package/dist/package/declarations/src/stores/modules/user.d.ts +5 -2
- package/dist/package/declarations/src/stores/runtime.d.ts +1 -0
- package/dist/package/declarations/src/utils/clipboard.d.ts +2 -0
- package/dist/package/declarations/src/utils/webauthn.d.ts +4 -0
- package/dist/package/declarations/vite.config.d.ts +2 -0
- package/dist/package/src/api/base/mfa.ts +114 -0
- package/dist/package/src/components/Dialog/PasswordVerifyDialog.vue +176 -0
- package/dist/package/src/components/Loading/fullScreen.ts +2 -1
- package/dist/package/src/components/MarkdownPreview/index.vue +21 -1
- package/dist/package/src/components/Mfa/MfaRecoveryCodesDialog.vue +91 -0
- package/dist/package/src/components/Mfa/MfaSetupPanel.vue +81 -0
- package/dist/package/src/components/ProForm/components/ProFormItem.vue +16 -1
- package/dist/package/src/components/ProForm/interface/index.ts +4 -0
- package/dist/package/src/components/ProTable/components/ColSetting.vue +1 -1
- package/dist/package/src/components/ProTable/index.vue +6 -2
- package/dist/package/src/components/RichTextPreview/index.vue +45 -0
- package/dist/package/src/config/index.ts +1 -1
- package/dist/package/src/layouts/LayoutClassic/index.vue +3 -1
- package/dist/package/src/layouts/LayoutColumns/index.vue +3 -1
- package/dist/package/src/layouts/LayoutTransverse/index.vue +3 -1
- package/dist/package/src/layouts/LayoutVertical/index.vue +9 -1
- package/dist/package/src/layouts/components/Header/components/Avatar.vue +23 -4
- package/dist/package/src/layouts/components/Header/components/InfoDialog.vue +1 -1
- package/dist/package/src/layouts/components/Header/components/PasswordDialog.vue +1 -1
- package/dist/package/src/layouts/components/LockScreen.vue +450 -0
- package/dist/package/src/layouts/index.vue +2 -0
- package/dist/package/src/layouts/indexAsync.vue +2 -0
- package/dist/package/src/locales/README.md +2 -1
- package/dist/package/src/locales/en-US.json +43 -1
- package/dist/package/src/locales/index.ts +16 -12
- package/dist/package/src/locales/ja-JP.json +43 -1
- package/dist/package/src/locales/zh-CN.json +43 -1
- package/dist/package/src/locales/zh-TW.json +43 -1
- package/dist/package/src/rpc/base/v1/login.ts +33 -2
- package/dist/package/src/rpc/base/v1/mfa.ts +180 -0
- package/dist/package/src/rpc/base/v1/oauth.ts +25 -0
- package/dist/package/src/rpc/base/v1/oauth_client.ts +33 -0
- package/dist/package/src/rpc/system/admin/v1/base_dict.ts +4 -4
- package/dist/package/src/rpc/system/admin/v1/base_i18n.ts +27 -23
- package/dist/package/src/security.ts +2 -0
- package/dist/package/src/stores/interface/index.ts +15 -1
- package/dist/package/src/stores/modules/config.ts +4 -4
- package/dist/package/src/stores/modules/lockScreen.ts +78 -0
- package/dist/package/src/stores/modules/user.ts +16 -3
- package/dist/package/src/stores/runtime.ts +1 -0
- package/dist/package/src/utils/clipboard.ts +22 -0
- package/dist/package/src/utils/request.ts +26 -10
- package/dist/package/src/utils/webauthn.ts +90 -0
- package/dist/package/src/views/login/components/LoginForm.vue +237 -16
- package/dist/package/src/views/login/index.vue +3 -1
- package/dist/package/types/generated/auto-imports.d.ts +132 -129
- package/dist/package/types/generated/components.d.ts +4 -72
- package/package.json +22 -1
- package/types/generated/auto-imports.d.ts +132 -129
- package/types/generated/components.d.ts +4 -72
- package/vite.config.ts +3 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="uri" class="mfa-setup-panel">
|
|
3
|
+
<img v-if="qrDataUrl" class="mfa-setup-panel__qr" :src="qrDataUrl" :alt="t('core.login.mfa_setup_qr_alt')" />
|
|
4
|
+
<el-collapse class="mfa-setup-panel__uri">
|
|
5
|
+
<el-collapse-item :title="t('core.login.mfa_show_uri')" name="uri">
|
|
6
|
+
<div class="mfa-setup-panel__value">
|
|
7
|
+
<span class="mfa-setup-panel__text">{{ uri }}</span>
|
|
8
|
+
<el-tooltip :content="t('core.login.mfa_copy_uri')" placement="top">
|
|
9
|
+
<el-button text circle :aria-label="t('core.login.mfa_copy_uri')" @click.stop="copySetupUri">
|
|
10
|
+
<el-icon class="mfa-setup-panel__copy"><CopyDocument /></el-icon>
|
|
11
|
+
</el-button>
|
|
12
|
+
</el-tooltip>
|
|
13
|
+
</div>
|
|
14
|
+
</el-collapse-item>
|
|
15
|
+
</el-collapse>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts" name="MfaSetupPanel">
|
|
20
|
+
import { computed } from "vue";
|
|
21
|
+
import qrcode from "qrcode-generator";
|
|
22
|
+
import { useLocaleStore } from "../../locales";
|
|
23
|
+
import { copyText } from "../../utils/clipboard";
|
|
24
|
+
|
|
25
|
+
/** MFA TOTP 绑定面板属性。 */
|
|
26
|
+
interface MfaSetupPanelProps {
|
|
27
|
+
/** TOTP 绑定地址。 */
|
|
28
|
+
uri: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const props = defineProps<MfaSetupPanelProps>();
|
|
32
|
+
const { t } = useLocaleStore();
|
|
33
|
+
|
|
34
|
+
const qrDataUrl = computed(() => {
|
|
35
|
+
if (!props.uri) return "";
|
|
36
|
+
const code = qrcode(0, "M");
|
|
37
|
+
code.addData(props.uri);
|
|
38
|
+
code.make();
|
|
39
|
+
return code.createDataURL(4, 8);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/** 复制 TOTP 绑定地址。 */
|
|
43
|
+
async function copySetupUri() {
|
|
44
|
+
await copyText(props.uri);
|
|
45
|
+
ElMessage.success(t("core.login.mfa_uri_copied"));
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style scoped lang="scss">
|
|
50
|
+
.mfa-setup-panel__qr {
|
|
51
|
+
display: block;
|
|
52
|
+
width: 220px;
|
|
53
|
+
height: 220px;
|
|
54
|
+
margin: 12px auto;
|
|
55
|
+
image-rendering: pixelated;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.mfa-setup-panel__uri {
|
|
59
|
+
margin-top: 12px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.mfa-setup-panel__value {
|
|
63
|
+
display: flex;
|
|
64
|
+
gap: 8px;
|
|
65
|
+
align-items: flex-start;
|
|
66
|
+
color: var(--el-text-color-regular);
|
|
67
|
+
line-height: 1.6;
|
|
68
|
+
overflow-wrap: anywhere;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.mfa-setup-panel__text {
|
|
72
|
+
flex: 1;
|
|
73
|
+
min-width: 0;
|
|
74
|
+
white-space: pre-wrap;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.mfa-setup-panel__copy {
|
|
78
|
+
color: var(--el-color-primary);
|
|
79
|
+
font-size: 18px;
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
<el-input-number v-else-if="field.component === 'input-number'" v-model="fieldValue" v-bind="fieldProps" />
|
|
15
15
|
|
|
16
|
+
<el-color-picker v-else-if="field.component === 'color-picker'" v-model="fieldValue" v-bind="fieldProps" />
|
|
17
|
+
|
|
16
18
|
<el-segmented v-else-if="field.component === 'segmented'" v-model="fieldValue" :options="fieldOptions" v-bind="fieldProps" />
|
|
17
19
|
|
|
18
20
|
<el-checkbox v-else-if="field.component === 'checkbox'" v-model="fieldValue" v-bind="fieldProps">
|
|
@@ -28,7 +30,12 @@
|
|
|
28
30
|
:label="option.label"
|
|
29
31
|
:value="option.value"
|
|
30
32
|
:disabled="option.disabled"
|
|
31
|
-
|
|
33
|
+
>
|
|
34
|
+
<span class="pro-form-option">
|
|
35
|
+
<component :is="option.icon" v-if="option.icon" />
|
|
36
|
+
<span>{{ option.label }}</span>
|
|
37
|
+
</span>
|
|
38
|
+
</el-option>
|
|
32
39
|
</el-select>
|
|
33
40
|
|
|
34
41
|
<el-radio-group v-else-if="field.component === 'radio-group'" v-model="fieldValue" v-bind="fieldProps">
|
|
@@ -189,3 +196,11 @@ const multipleKvValue = computed({
|
|
|
189
196
|
set: value => setFieldValue(value)
|
|
190
197
|
});
|
|
191
198
|
</script>
|
|
199
|
+
|
|
200
|
+
<style scoped lang="scss">
|
|
201
|
+
.pro-form-option {
|
|
202
|
+
display: inline-flex;
|
|
203
|
+
align-items: center;
|
|
204
|
+
gap: 6px;
|
|
205
|
+
}
|
|
206
|
+
</style>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FormItemRule } from "element-plus";
|
|
2
|
+
import type { Component } from "vue";
|
|
2
3
|
|
|
3
4
|
/** ProForm 支持的字段组件类型。 */
|
|
4
5
|
export type ProFormComponentType =
|
|
@@ -6,6 +7,7 @@ export type ProFormComponentType =
|
|
|
6
7
|
| "password"
|
|
7
8
|
| "textarea"
|
|
8
9
|
| "input-number"
|
|
10
|
+
| "color-picker"
|
|
9
11
|
| "segmented"
|
|
10
12
|
| "switch"
|
|
11
13
|
| "checkbox"
|
|
@@ -31,6 +33,8 @@ export interface ProFormOption {
|
|
|
31
33
|
label: string;
|
|
32
34
|
value: string | number | boolean;
|
|
33
35
|
disabled?: boolean;
|
|
36
|
+
/** 下拉项前置图标。 */
|
|
37
|
+
icon?: Component;
|
|
34
38
|
children?: ProFormOption[];
|
|
35
39
|
/** Element Plus 懒加载树节点是否为叶子节点。 */
|
|
36
40
|
isLeaf?: boolean;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
</el-table-column>
|
|
13
13
|
<template #empty>
|
|
14
14
|
<div class="table-empty">
|
|
15
|
-
<img src="../../../assets/images/notData.png" alt="
|
|
15
|
+
<img src="../../../assets/images/notData.png" :alt="t('core.table.no_configurable_columns')" />
|
|
16
16
|
<div>{{ t("core.table.no_configurable_columns") }}</div>
|
|
17
17
|
</div>
|
|
18
18
|
</template>
|
|
@@ -41,7 +41,11 @@
|
|
|
41
41
|
<el-tooltip v-if="showToolButton('refresh')" :content="t('core.table.refresh')" placement="top">
|
|
42
42
|
<el-button class="tool-button" :icon="Refresh" circle @click="handleToolRefresh" />
|
|
43
43
|
</el-tooltip>
|
|
44
|
-
<el-tooltip
|
|
44
|
+
<el-tooltip
|
|
45
|
+
v-if="showToolButton('setting') && columns.length"
|
|
46
|
+
:content="t('core.table.column_setting')"
|
|
47
|
+
placement="top"
|
|
48
|
+
>
|
|
45
49
|
<el-button class="tool-button" :icon="Operation" circle @click="openColSetting" />
|
|
46
50
|
</el-tooltip>
|
|
47
51
|
<el-tooltip
|
|
@@ -107,7 +111,7 @@
|
|
|
107
111
|
<template #empty>
|
|
108
112
|
<div class="table-empty">
|
|
109
113
|
<slot name="empty">
|
|
110
|
-
<img src="../../assets/images/notData.png" alt="
|
|
114
|
+
<img src="../../assets/images/notData.png" :alt="t('common.message.no_data')" />
|
|
111
115
|
<div>{{ t("common.message.no_data") }}</div>
|
|
112
116
|
</slot>
|
|
113
117
|
</div>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="core-rich-text-preview" v-html="sanitizedHtml" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { computed } from "vue";
|
|
7
|
+
import DOMPurify from "dompurify";
|
|
8
|
+
|
|
9
|
+
defineOptions({
|
|
10
|
+
name: "CoreRichTextPreview"
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/** 富文本预览组件属性。 */
|
|
14
|
+
interface RichTextPreviewProps {
|
|
15
|
+
/** 待展示的 HTML 内容。 */
|
|
16
|
+
modelValue: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const props = defineProps<RichTextPreviewProps>();
|
|
20
|
+
const sanitizedHtml = computed(() => DOMPurify.sanitize(props.modelValue, { USE_PROFILES: { html: true } }));
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<style scoped lang="scss">
|
|
24
|
+
.core-rich-text-preview {
|
|
25
|
+
line-height: 1.7;
|
|
26
|
+
overflow-wrap: anywhere;
|
|
27
|
+
word-break: break-word;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.core-rich-text-preview :deep(p) {
|
|
31
|
+
margin: 0 0 8px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.core-rich-text-preview :deep(ul),
|
|
35
|
+
.core-rich-text-preview :deep(ol) {
|
|
36
|
+
margin: 0 0 8px;
|
|
37
|
+
padding-left: 24px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.core-rich-text-preview :deep(img),
|
|
41
|
+
.core-rich-text-preview :deep(video) {
|
|
42
|
+
max-width: 100%;
|
|
43
|
+
height: auto;
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<el-header>
|
|
5
5
|
<div class="header-lf mask-image">
|
|
6
6
|
<div class="logo flx-center">
|
|
7
|
-
<img class="logo-img" :src="logoUrl" alt="
|
|
7
|
+
<img class="logo-img" :src="logoUrl" :alt="t('core.layout.logo_alt')" />
|
|
8
8
|
<span class="logo-text">{{ title }}</span>
|
|
9
9
|
</div>
|
|
10
10
|
<ToolBarLeft />
|
|
@@ -46,11 +46,13 @@ import Main from "../../layouts/components/Main/index.vue";
|
|
|
46
46
|
import SubMenu from "../../layouts/components/Menu/SubMenu.vue";
|
|
47
47
|
import ToolBarLeft from "../../layouts/components/Header/ToolBarLeft.vue";
|
|
48
48
|
import ToolBarRight from "../../layouts/components/Header/ToolBarRight.vue";
|
|
49
|
+
import { useLocaleStore } from "../../locales";
|
|
49
50
|
|
|
50
51
|
const route = useRoute();
|
|
51
52
|
const authStore = useAuthStore();
|
|
52
53
|
const configStore = useConfigStore();
|
|
53
54
|
const globalStore = useGlobalStore();
|
|
55
|
+
const { t } = useLocaleStore();
|
|
54
56
|
const accordion = computed(() => globalStore.accordion);
|
|
55
57
|
const isCollapse = computed(() => globalStore.isCollapse);
|
|
56
58
|
const menuList = computed(() => authStore.showMenuListGet);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<el-container class="layout">
|
|
4
4
|
<div class="aside-split">
|
|
5
5
|
<div class="logo flx-center">
|
|
6
|
-
<img class="logo-img" :src="logoUrl" alt="
|
|
6
|
+
<img class="logo-img" :src="logoUrl" :alt="t('core.layout.logo_alt')" />
|
|
7
7
|
</div>
|
|
8
8
|
<el-scrollbar>
|
|
9
9
|
<div class="split-list">
|
|
@@ -60,12 +60,14 @@ import Main from "../../layouts/components/Main/index.vue";
|
|
|
60
60
|
import ToolBarLeft from "../../layouts/components/Header/ToolBarLeft.vue";
|
|
61
61
|
import ToolBarRight from "../../layouts/components/Header/ToolBarRight.vue";
|
|
62
62
|
import SubMenu from "../../layouts/components/Menu/SubMenu.vue";
|
|
63
|
+
import { useLocaleStore } from "../../locales";
|
|
63
64
|
|
|
64
65
|
const route = useRoute();
|
|
65
66
|
const router = useRouter();
|
|
66
67
|
const authStore = useAuthStore();
|
|
67
68
|
const configStore = useConfigStore();
|
|
68
69
|
const globalStore = useGlobalStore();
|
|
70
|
+
const { t } = useLocaleStore();
|
|
69
71
|
const accordion = computed(() => globalStore.accordion);
|
|
70
72
|
const isCollapse = computed(() => globalStore.isCollapse);
|
|
71
73
|
const menuList = computed(() => authStore.showMenuListGet.filter(item => Boolean(item.path) || Boolean(item.children?.length)));
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<el-container class="layout">
|
|
4
4
|
<el-header>
|
|
5
5
|
<div class="logo flx-center">
|
|
6
|
-
<img class="logo-img" :src="logoUrl" alt="
|
|
6
|
+
<img class="logo-img" :src="logoUrl" :alt="t('core.layout.logo_alt')" />
|
|
7
7
|
<span class="logo-text">{{ title }}</span>
|
|
8
8
|
</div>
|
|
9
9
|
<el-menu mode="horizontal" :router="false" :default-active="activeMenu">
|
|
@@ -44,11 +44,13 @@ import { getRouteMenuKey, getRouteMetaIcon, getRouteMetaTitle, getRouteTarget, i
|
|
|
44
44
|
import Main from "../../layouts/components/Main/index.vue";
|
|
45
45
|
import ToolBarRight from "../../layouts/components/Header/ToolBarRight.vue";
|
|
46
46
|
import SubMenu from "../../layouts/components/Menu/SubMenu.vue";
|
|
47
|
+
import { useLocaleStore } from "../../locales";
|
|
47
48
|
|
|
48
49
|
const route = useRoute();
|
|
49
50
|
const router = useRouter();
|
|
50
51
|
const authStore = useAuthStore();
|
|
51
52
|
const configStore = useConfigStore();
|
|
53
|
+
const { t } = useLocaleStore();
|
|
52
54
|
const menuList = computed(() => authStore.showMenuListGet);
|
|
53
55
|
const activeMenu = computed(() => route.path as string);
|
|
54
56
|
const title = computed(() => configStore.display.sysName || import.meta.env.VITE_GLOB_APP_TITLE);
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
<el-aside>
|
|
5
5
|
<div class="aside-box" :style="{ width: isCollapse ? '65px' : '240px' }">
|
|
6
6
|
<div ref="logoWrapperRef" :class="['logo', { 'logo--text-only': !showLogoIcon && !isCollapse }]">
|
|
7
|
-
<img
|
|
7
|
+
<img
|
|
8
|
+
v-show="isCollapse || showLogoIcon"
|
|
9
|
+
ref="logoIconRef"
|
|
10
|
+
class="logo-img"
|
|
11
|
+
:src="logoUrl"
|
|
12
|
+
:alt="t('core.layout.logo_alt')"
|
|
13
|
+
/>
|
|
8
14
|
<span
|
|
9
15
|
v-show="!isCollapse"
|
|
10
16
|
ref="logoTextRef"
|
|
@@ -46,11 +52,13 @@ import Main from "../../layouts/components/Main/index.vue";
|
|
|
46
52
|
import ToolBarLeft from "../../layouts/components/Header/ToolBarLeft.vue";
|
|
47
53
|
import ToolBarRight from "../../layouts/components/Header/ToolBarRight.vue";
|
|
48
54
|
import SubMenu from "../../layouts/components/Menu/SubMenu.vue";
|
|
55
|
+
import { useLocaleStore } from "../../locales";
|
|
49
56
|
|
|
50
57
|
const route = useRoute();
|
|
51
58
|
const authStore = useAuthStore();
|
|
52
59
|
const configStore = useConfigStore();
|
|
53
60
|
const globalStore = useGlobalStore();
|
|
61
|
+
const { t } = useLocaleStore();
|
|
54
62
|
const accordion = computed(() => globalStore.accordion);
|
|
55
63
|
const isCollapse = computed(() => globalStore.isCollapse);
|
|
56
64
|
const menuList = computed(() => authStore.showMenuListGet);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<el-dropdown ref="dropdownRef" trigger="click" placement="bottom-end" popper-class="header-avatar-dropdown">
|
|
3
3
|
<div class="avatar">
|
|
4
|
-
<img :src="avatarSrc" alt="avatar" @error="handleAvatarError" />
|
|
4
|
+
<img :src="avatarSrc" :alt="t('core.layout.avatar')" @error="handleAvatarError" />
|
|
5
5
|
</div>
|
|
6
6
|
<template #dropdown>
|
|
7
7
|
<div class="user-panel">
|
|
8
8
|
<div class="user-panel__summary">
|
|
9
9
|
<div class="user-panel__avatar">
|
|
10
|
-
<img :src="avatarSrc" alt="avatar" @error="handleAvatarError" />
|
|
10
|
+
<img :src="avatarSrc" :alt="t('core.layout.avatar')" @error="handleAvatarError" />
|
|
11
11
|
</div>
|
|
12
12
|
<div class="user-panel__identity">
|
|
13
13
|
<div class="user-panel__name">{{ displayName }}</div>
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
<el-icon v-if="action.icon"><component :is="action.icon" /></el-icon>
|
|
26
26
|
<span>{{ action.labelKey ? t(action.labelKey) : action.label }}</span>
|
|
27
27
|
</button>
|
|
28
|
+
<button class="action-btn action-btn--lock" type="button" @click.stop="lockScreen">
|
|
29
|
+
<el-icon><Lock /></el-icon>
|
|
30
|
+
<span>{{ t("core.layout.lock_screen") }}</span>
|
|
31
|
+
</button>
|
|
28
32
|
<button class="action-btn action-btn--danger" type="button" @click="logout">
|
|
29
33
|
<el-icon><SwitchButton /></el-icon>
|
|
30
34
|
<span>{{ t("common.action.logout") }}</span>
|
|
@@ -41,16 +45,17 @@ import { LOGIN_URL } from "../../../../config";
|
|
|
41
45
|
import { useRouter } from "vue-router";
|
|
42
46
|
import { useUserStore } from "../../../../stores/modules/user";
|
|
43
47
|
import { useAuthStore } from "../../../../stores/modules/auth";
|
|
44
|
-
import { ElMessageBox, ElMessage } from "element-plus";
|
|
45
48
|
import type { DropdownInstance } from "element-plus";
|
|
46
49
|
import defaultAvatar from "../../../../assets/images/avatar.png";
|
|
47
50
|
import { navigateTo } from "../../../../utils/router";
|
|
48
51
|
import { getAdminUserMenuActions } from "../../../../modules";
|
|
49
52
|
import { useLocaleStore } from "../../../../locales";
|
|
53
|
+
import { useLockScreenStore } from "../../../../stores/modules/lockScreen";
|
|
50
54
|
|
|
51
55
|
const router = useRouter();
|
|
52
56
|
const userStore = useUserStore();
|
|
53
57
|
const authStore = useAuthStore();
|
|
58
|
+
const lockScreenStore = useLockScreenStore();
|
|
54
59
|
const userMenuActions = getAdminUserMenuActions();
|
|
55
60
|
const dropdownRef = ref<DropdownInstance>();
|
|
56
61
|
const { t } = useLocaleStore();
|
|
@@ -112,6 +117,12 @@ const handleUserMenuAction = async (path: string) => {
|
|
|
112
117
|
dropdownRef.value?.handleClose();
|
|
113
118
|
await navigateTo(router, path);
|
|
114
119
|
};
|
|
120
|
+
|
|
121
|
+
/** 打开锁屏密码设置界面并关闭头像菜单。 */
|
|
122
|
+
const lockScreen = () => {
|
|
123
|
+
dropdownRef.value?.handleClose();
|
|
124
|
+
lockScreenStore.openSetup();
|
|
125
|
+
};
|
|
115
126
|
</script>
|
|
116
127
|
|
|
117
128
|
<style scoped lang="scss">
|
|
@@ -192,7 +203,7 @@ const handleUserMenuAction = async (path: string) => {
|
|
|
192
203
|
height: 38px;
|
|
193
204
|
cursor: pointer;
|
|
194
205
|
border: none;
|
|
195
|
-
border-radius:
|
|
206
|
+
border-radius: var(--admin-page-radius);
|
|
196
207
|
transition:
|
|
197
208
|
transform 0.2s ease,
|
|
198
209
|
box-shadow 0.2s ease,
|
|
@@ -218,6 +229,14 @@ const handleUserMenuAction = async (path: string) => {
|
|
|
218
229
|
box-shadow: 0 8px 18px rgb(220 38 38 / 14%);
|
|
219
230
|
}
|
|
220
231
|
}
|
|
232
|
+
&--lock {
|
|
233
|
+
color: var(--el-text-color-regular);
|
|
234
|
+
background: var(--el-fill-color-light);
|
|
235
|
+
&:hover {
|
|
236
|
+
background: var(--el-fill-color);
|
|
237
|
+
box-shadow: 0 8px 18px rgb(15 23 42 / 10%);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
221
240
|
}
|
|
222
241
|
:deep(.header-avatar-dropdown) {
|
|
223
242
|
padding: 0;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<el-dialog v-model="dialogVisible" :title="t('core.layout.info')" width="500px" draggable>
|
|
3
|
-
<span>
|
|
3
|
+
<span>{{ t("core.layout.info_content") }}</span>
|
|
4
4
|
<template #footer>
|
|
5
5
|
<span class="dialog-footer">
|
|
6
6
|
<el-button @click="dialogVisible = false">{{ t("common.action.cancel") }}</el-button>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<el-dialog v-model="dialogVisible" :title="t('core.layout.password_change')" width="500px" draggable>
|
|
3
|
-
<span>
|
|
3
|
+
<span>{{ t("core.layout.password_content") }}</span>
|
|
4
4
|
<template #footer>
|
|
5
5
|
<span class="dialog-footer">
|
|
6
6
|
<el-button @click="dialogVisible = false">{{ t("common.action.cancel") }}</el-button>
|