chore: 锁屏与通知组件删除(enabled:false的最终清理)
Phase 3 终审 M-1/M-2 摘入口后组件保留待决,本提交最终删除: - art-screen-lock/ 与 art-notification/ 组件目录 - component.ts screen-lock 全局组件项、headerBar.ts notification 项 - types/config HeaderBarFeatureConfig.notification 字段、 useHeaderBar shouldShowNotification - art-header-bar 通知按钮/挂载/showNotice/visibleNotice/ bodyCloseNotice 与 document 点击监听、.notice-button 样式 - components.d.ts 两条目;crypto-js 依赖全套(env.d.ts 声明/ package.json/lockfile/optimizeDeps)——唯一消费方是锁屏 - en/zh 孤儿语言键(notice.* / lockScreen.* / topBar.user.lockScreen) 与 mittBus openLockScreen 事件类型(无发射/订阅方) grep 零残留;vitest 164 passed;build 通过(主 chunk 358.45→355.84 kB, crypto-js 出预构建);auto-imports.d.ts 行尾漂移已还原。 CLAUDE.md Phase 4 清理单 6/7 结项(并补记 8/9 随 Task 5E/5D 结项)。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -147,10 +147,10 @@
|
||||
3. 生产反代需规划 admin 域的头像/品牌公开端点:`/api/public/*` 按租户 Host 解析,admin 域直挂必 404——开发期靠 Vite Host 改写代理,生产要么同源反代改 Host(单租户形态)要么前端改用租户域名绝对 URL。
|
||||
4. `VITE_TENANT_PREVIEW_HOST` 单一预览域名:开发代理只指向一个租户 Host,切换租户时头像等公开资源预览不准(.env.development 已注记)。
|
||||
5. ~~resolver 平台默认回退链接入 platform_settings~~(Phase 4 Task 5A 完成:`app/platform/settings_store.py` 内存快照 + lifespan 装载 + PUT settings 后 refresh 热刷新;config_views 的 email/ai 两组改三级回退(租户槽位 → 平台设置快照 → env),push 无平台推荐键保持两级;平台设置页"仅存储"注记移除)。
|
||||
6. **email/test 与 push/test 速率限制**(终审 I-3 记档):两测试端点无限流,复用登录限流器模式收口。
|
||||
7. **锁屏功能已摘除入口**(终审 M-1):用户菜单项删除 + 全局组件 enabled:false 不再挂载(AES 存 localStorage + 公开模板默认密钥,防御价值为零);代码待 Phase 4 决定删否。
|
||||
8. **租户切换器 composable 收拢**(终审 M-3 不做记档):仪表盘/会话/配置三页各自内联实现同一模式,收拢归后续。
|
||||
9. **ECharts 按需收窄**(终审 M-4 不做记档):stats 的 plugins/echarts.ts 全量注册,bundle 体积优化归后续。
|
||||
6. ~~email/test 与 push/test 速率限制~~(Phase 4 Task 5B 完成:config_routes 内 `SlidingWindowCounter` 滑动窗限流(登录限流器同款风格),每租户每类端点 15 分钟 5 次真实出站,超限 429;校验失败/未配置 400 不消耗预算,超限后 429 优先于一切预检)。
|
||||
7. ~~锁屏功能已摘除入口~~ + ~~通知组件摘除~~(锁屏 Phase 3 摘入口,Phase 4 Task 5F 删代码:art-screen-lock/art-notification 组件目录、component.ts/headerBar.ts 配置项、useHeaderBar.shouldShowNotification、类型字段、components.d.ts 条目、crypto-js 依赖(env.d.ts 声明/package.json/optimizeDeps/lockfile)、en/zh 孤儿语言键全部清除,grep 零残留)。
|
||||
8. ~~租户切换器 composable 收拢~~(Phase 4 Task 5E 完成:`admin/src/composables/useTenantOptions.ts` 收拢会话/配置/统计三页租户下拉(默认形态选中首个 + bootstrapTenantContext 引导;stats 用 selectFirst:false 纯数据源);dashboard 卡片墙形态不在此列)。
|
||||
9. ~~ECharts 按需收窄~~(Phase 4 Task 5D 完成:echarts.use 收窄为 Bar/Line/Pie + Title/Tooltip/Grid/Legend + CanvasRenderer——全仓 grep 证实存活消费方仅 stats 页与 useChart;echarts chunk 767,864 B → 559,737 B(-27%),dist 总体积 -273 KB)。
|
||||
|
||||
## 文档索引
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
"@vue/reactivity": "^3.5.21",
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"axios": "^1.12.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.11.2",
|
||||
"mitt": "^3.0.1",
|
||||
|
||||
Generated
-8
@@ -26,9 +26,6 @@ importers:
|
||||
axios:
|
||||
specifier: ^1.12.2
|
||||
version: 1.12.2
|
||||
crypto-js:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
echarts:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
@@ -1375,9 +1372,6 @@ packages:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
crypto-js@4.2.0:
|
||||
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
|
||||
|
||||
css-functions-list@3.2.3:
|
||||
resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==}
|
||||
engines: {node: '>=12 || >=16'}
|
||||
@@ -4224,8 +4218,6 @@ snapshots:
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
crypto-js@4.2.0: {}
|
||||
|
||||
css-functions-list@3.2.3: {}
|
||||
|
||||
css-tree@3.1.0:
|
||||
|
||||
@@ -109,16 +109,6 @@
|
||||
</template>
|
||||
</ElDropdown>
|
||||
|
||||
<!-- 通知按钮 -->
|
||||
<ArtIconButton
|
||||
v-if="shouldShowNotification"
|
||||
icon="ri:notification-2-line"
|
||||
class="notice-button relative"
|
||||
@click="visibleNotice"
|
||||
>
|
||||
<div class="absolute top-2 right-2 size-1.5 !bg-danger rounded-full"></div>
|
||||
</ArtIconButton>
|
||||
|
||||
<!-- 聊天按钮 -->
|
||||
<ArtIconButton
|
||||
v-if="shouldShowChat"
|
||||
@@ -162,9 +152,6 @@
|
||||
|
||||
<!-- 标签页 -->
|
||||
<ArtWorkTab />
|
||||
|
||||
<!-- 通知 -->
|
||||
<ArtNotification v-model:value="showNotice" ref="notice" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -205,7 +192,6 @@
|
||||
shouldShowBreadcrumb,
|
||||
shouldShowGlobalSearch,
|
||||
shouldShowFullscreen,
|
||||
shouldShowNotification,
|
||||
shouldShowChat,
|
||||
shouldShowLanguage,
|
||||
shouldShowSettings,
|
||||
@@ -219,9 +205,6 @@
|
||||
const { language } = storeToRefs(userStore)
|
||||
const { menuList } = storeToRefs(menuStore)
|
||||
|
||||
const showNotice = ref(false)
|
||||
const notice = ref(null)
|
||||
|
||||
// 菜单类型判断
|
||||
const isLeftMenu = computed(() => menuType.value === MenuTypeEnum.LEFT)
|
||||
const isDualMenu = computed(() => menuType.value === MenuTypeEnum.DUAL_MENU)
|
||||
@@ -232,11 +215,6 @@
|
||||
|
||||
onMounted(() => {
|
||||
initLanguage()
|
||||
document.addEventListener('click', bodyCloseNotice)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', bodyCloseNotice)
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -310,31 +288,6 @@
|
||||
mittBus.emit('openSearchDialog')
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击页面其他区域关闭通知面板
|
||||
* @param {Event} e - 点击事件对象
|
||||
*/
|
||||
const bodyCloseNotice = (e: any): void => {
|
||||
if (!showNotice.value) return
|
||||
|
||||
const target = e.target as HTMLElement
|
||||
|
||||
// 检查是否点击了通知按钮或通知面板内部
|
||||
const isNoticeButton = target.closest('.notice-button')
|
||||
const isNoticePanel = target.closest('.art-notification-panel')
|
||||
|
||||
if (!isNoticeButton && !isNoticePanel) {
|
||||
showNotice.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换通知面板显示状态
|
||||
*/
|
||||
const visibleNotice = (): void => {
|
||||
showNotice.value = !showNotice.value
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开聊天窗口
|
||||
*/
|
||||
@@ -457,10 +410,6 @@
|
||||
animation: shrink 0.6s forwards;
|
||||
}
|
||||
|
||||
.notice-button:hover :deep(.art-svg-icon) {
|
||||
animation: shake 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.chat-button:hover :deep(.art-svg-icon) {
|
||||
animation: shake 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -1,456 +0,0 @@
|
||||
<!-- 通知组件 -->
|
||||
<template>
|
||||
<div
|
||||
class="art-notification-panel art-card-sm !shadow-xl"
|
||||
:style="{
|
||||
transform: show ? 'scaleY(1)' : 'scaleY(0.9)',
|
||||
opacity: show ? 1 : 0
|
||||
}"
|
||||
v-show="visible"
|
||||
@click.stop
|
||||
>
|
||||
<div class="flex-cb px-3.5 mt-3.5">
|
||||
<span class="text-base font-medium text-g-800">{{ $t('notice.title') }}</span>
|
||||
<span class="text-xs text-g-800 px-1.5 py-1 c-p select-none rounded hover:bg-g-200">
|
||||
{{ $t('notice.btnRead') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<ul class="box-border flex items-end w-full h-12.5 px-3.5 border-b-d">
|
||||
<li
|
||||
v-for="(item, index) in barList"
|
||||
:key="index"
|
||||
class="h-12 leading-12 mr-5 overflow-hidden text-[13px] text-g-700 c-p select-none"
|
||||
:class="{ 'bar-active': barActiveIndex === index }"
|
||||
@click="changeBar(index)"
|
||||
>
|
||||
{{ item.name }} ({{ item.num }})
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="w-full h-[calc(100%-95px)]">
|
||||
<div class="h-[calc(100%-60px)] overflow-y-scroll scrollbar-thin">
|
||||
<!-- 通知 -->
|
||||
<ul v-show="barActiveIndex === 0">
|
||||
<li
|
||||
v-for="(item, index) in noticeList"
|
||||
:key="index"
|
||||
class="box-border flex-c px-3.5 py-3.5 c-p last:border-b-0 hover:bg-g-200/60"
|
||||
>
|
||||
<div
|
||||
class="size-9 leading-9 text-center rounded-lg flex-cc"
|
||||
:class="[getNoticeStyle(item.type).iconClass]"
|
||||
>
|
||||
<ArtSvgIcon class="text-lg !bg-transparent" :icon="getNoticeStyle(item.type).icon" />
|
||||
</div>
|
||||
<div class="w-[calc(100%-45px)] ml-3.5">
|
||||
<h4 class="text-sm font-normal leading-5.5 text-g-900">{{ item.title }}</h4>
|
||||
<p class="mt-1.5 text-xs text-g-500">{{ item.time }}</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 消息 -->
|
||||
<ul v-show="barActiveIndex === 1">
|
||||
<li
|
||||
v-for="(item, index) in msgList"
|
||||
:key="index"
|
||||
class="box-border flex-c px-3.5 py-3.5 c-p last:border-b-0 hover:bg-g-200/60"
|
||||
>
|
||||
<div class="w-9 h-9">
|
||||
<img :src="item.avatar" class="w-full h-full rounded-lg" />
|
||||
</div>
|
||||
<div class="w-[calc(100%-45px)] ml-3.5">
|
||||
<h4 class="text-xs font-normal leading-5.5">{{ item.title }}</h4>
|
||||
<p class="mt-1.5 text-xs text-g-500">{{ item.time }}</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 待办 -->
|
||||
<ul v-show="barActiveIndex === 2">
|
||||
<li
|
||||
v-for="(item, index) in pendingList"
|
||||
:key="index"
|
||||
class="box-border px-5 py-3.5 last:border-b-0"
|
||||
>
|
||||
<h4>{{ item.title }}</h4>
|
||||
<p class="text-xs text-g-500">{{ item.time }}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div
|
||||
v-show="currentTabIsEmpty"
|
||||
class="relative top-25 h-full text-g-500 text-center !bg-transparent"
|
||||
>
|
||||
<ArtSvgIcon icon="system-uicons:inbox" class="text-5xl" />
|
||||
<p class="mt-3.5 text-xs !bg-transparent"
|
||||
>{{ $t('notice.text[0]') }}{{ barList[barActiveIndex].name }}</p
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative box-border w-full px-3.5">
|
||||
<ElButton class="w-full mt-3" @click="handleViewAll" v-ripple>
|
||||
{{ $t('notice.viewAll') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-25"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch, type Ref, type ComputedRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 导入头像图片
|
||||
import avatar1 from '@/assets/images/avatar/avatar1.webp'
|
||||
import avatar2 from '@/assets/images/avatar/avatar2.webp'
|
||||
import avatar3 from '@/assets/images/avatar/avatar3.webp'
|
||||
import avatar4 from '@/assets/images/avatar/avatar4.webp'
|
||||
import avatar5 from '@/assets/images/avatar/avatar5.webp'
|
||||
import avatar6 from '@/assets/images/avatar/avatar6.webp'
|
||||
|
||||
defineOptions({ name: 'ArtNotification' })
|
||||
|
||||
interface NoticeItem {
|
||||
/** 标题 */
|
||||
title: string
|
||||
/** 时间 */
|
||||
time: string
|
||||
/** 类型 */
|
||||
type: NoticeType
|
||||
}
|
||||
|
||||
interface MessageItem {
|
||||
/** 标题 */
|
||||
title: string
|
||||
/** 时间 */
|
||||
time: string
|
||||
/** 头像 */
|
||||
avatar: string
|
||||
}
|
||||
|
||||
interface PendingItem {
|
||||
/** 标题 */
|
||||
title: string
|
||||
/** 时间 */
|
||||
time: string
|
||||
}
|
||||
|
||||
interface BarItem {
|
||||
/** 名称 */
|
||||
name: ComputedRef<string>
|
||||
/** 数量 */
|
||||
num: number
|
||||
}
|
||||
|
||||
interface NoticeStyle {
|
||||
/** 图标 */
|
||||
icon: string
|
||||
/** icon 样式 */
|
||||
iconClass: string
|
||||
}
|
||||
|
||||
type NoticeType = 'email' | 'message' | 'collection' | 'user' | 'notice'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
value: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:value': [value: boolean]
|
||||
}>()
|
||||
|
||||
const show = ref(false)
|
||||
const visible = ref(false)
|
||||
const barActiveIndex = ref(0)
|
||||
|
||||
const useNotificationData = () => {
|
||||
// 通知数据
|
||||
const noticeList = ref<NoticeItem[]>([
|
||||
{
|
||||
title: '新增国际化',
|
||||
time: '2024-6-13 0:10',
|
||||
type: 'notice'
|
||||
},
|
||||
{
|
||||
title: '冷月呆呆给你发了一条消息',
|
||||
time: '2024-4-21 8:05',
|
||||
type: 'message'
|
||||
},
|
||||
{
|
||||
title: '小肥猪关注了你',
|
||||
time: '2020-3-17 21:12',
|
||||
type: 'collection'
|
||||
},
|
||||
{
|
||||
title: '新增使用文档',
|
||||
time: '2024-02-14 0:20',
|
||||
type: 'notice'
|
||||
},
|
||||
{
|
||||
title: '小肥猪给你发了一封邮件',
|
||||
time: '2024-1-20 0:15',
|
||||
type: 'email'
|
||||
},
|
||||
{
|
||||
title: '菜单mock本地真实数据',
|
||||
time: '2024-1-17 22:06',
|
||||
type: 'notice'
|
||||
}
|
||||
])
|
||||
|
||||
// 消息数据
|
||||
const msgList = ref<MessageItem[]>([
|
||||
{
|
||||
title: '池不胖 关注了你',
|
||||
time: '2021-2-26 23:50',
|
||||
avatar: avatar1
|
||||
},
|
||||
{
|
||||
title: '唐不苦 关注了你',
|
||||
time: '2021-2-21 8:05',
|
||||
avatar: avatar2
|
||||
},
|
||||
{
|
||||
title: '中小鱼 关注了你',
|
||||
time: '2020-1-17 21:12',
|
||||
avatar: avatar3
|
||||
},
|
||||
{
|
||||
title: '何小荷 关注了你',
|
||||
time: '2021-01-14 0:20',
|
||||
avatar: avatar4
|
||||
},
|
||||
{
|
||||
title: '誶誶淰 关注了你',
|
||||
time: '2020-12-20 0:15',
|
||||
avatar: avatar5
|
||||
},
|
||||
{
|
||||
title: '冷月呆呆 关注了你',
|
||||
time: '2020-12-17 22:06',
|
||||
avatar: avatar6
|
||||
}
|
||||
])
|
||||
|
||||
// 待办数据
|
||||
const pendingList = ref<PendingItem[]>([])
|
||||
|
||||
// 标签栏数据
|
||||
const barList = computed<BarItem[]>(() => [
|
||||
{
|
||||
name: computed(() => t('notice.bar[0]')),
|
||||
num: noticeList.value.length
|
||||
},
|
||||
{
|
||||
name: computed(() => t('notice.bar[1]')),
|
||||
num: msgList.value.length
|
||||
},
|
||||
{
|
||||
name: computed(() => t('notice.bar[2]')),
|
||||
num: pendingList.value.length
|
||||
}
|
||||
])
|
||||
|
||||
return {
|
||||
noticeList,
|
||||
msgList,
|
||||
pendingList,
|
||||
barList
|
||||
}
|
||||
}
|
||||
|
||||
// 样式管理
|
||||
const useNotificationStyles = () => {
|
||||
const noticeStyleMap: Record<NoticeType, NoticeStyle> = {
|
||||
email: {
|
||||
icon: 'ri:mail-line',
|
||||
iconClass: 'bg-warning/12 text-warning'
|
||||
},
|
||||
message: {
|
||||
icon: 'ri:volume-down-line',
|
||||
iconClass: 'bg-success/12 text-success'
|
||||
},
|
||||
collection: {
|
||||
icon: 'ri:heart-3-line',
|
||||
iconClass: 'bg-danger/12 text-danger'
|
||||
},
|
||||
user: {
|
||||
icon: 'ri:volume-down-line',
|
||||
iconClass: 'bg-info/12 text-info'
|
||||
},
|
||||
notice: {
|
||||
icon: 'ri:notification-3-line',
|
||||
iconClass: 'bg-theme/12 text-theme'
|
||||
}
|
||||
}
|
||||
|
||||
const getNoticeStyle = (type: NoticeType): NoticeStyle => {
|
||||
const defaultStyle: NoticeStyle = {
|
||||
icon: 'ri:arrow-right-circle-line',
|
||||
iconClass: 'bg-theme/12 text-theme'
|
||||
}
|
||||
|
||||
return noticeStyleMap[type] || defaultStyle
|
||||
}
|
||||
|
||||
return {
|
||||
getNoticeStyle
|
||||
}
|
||||
}
|
||||
|
||||
// 动画管理
|
||||
const useNotificationAnimation = () => {
|
||||
const showNotice = (open: boolean) => {
|
||||
if (open) {
|
||||
visible.value = true
|
||||
setTimeout(() => {
|
||||
show.value = true
|
||||
}, 5)
|
||||
} else {
|
||||
show.value = false
|
||||
setTimeout(() => {
|
||||
visible.value = false
|
||||
}, 350)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
showNotice
|
||||
}
|
||||
}
|
||||
|
||||
// 标签页管理
|
||||
const useTabManagement = (
|
||||
noticeList: Ref<NoticeItem[]>,
|
||||
msgList: Ref<MessageItem[]>,
|
||||
pendingList: Ref<PendingItem[]>,
|
||||
businessHandlers: {
|
||||
handleNoticeAll: () => void
|
||||
handleMsgAll: () => void
|
||||
handlePendingAll: () => void
|
||||
}
|
||||
) => {
|
||||
const changeBar = (index: number) => {
|
||||
barActiveIndex.value = index
|
||||
}
|
||||
|
||||
// 检查当前标签页是否为空
|
||||
const currentTabIsEmpty = computed(() => {
|
||||
const tabDataMap = [noticeList.value, msgList.value, pendingList.value]
|
||||
|
||||
const currentData = tabDataMap[barActiveIndex.value]
|
||||
return currentData && currentData.length === 0
|
||||
})
|
||||
|
||||
const handleViewAll = () => {
|
||||
// 查看全部处理器映射
|
||||
const viewAllHandlers: Record<number, () => void> = {
|
||||
0: businessHandlers.handleNoticeAll,
|
||||
1: businessHandlers.handleMsgAll,
|
||||
2: businessHandlers.handlePendingAll
|
||||
}
|
||||
|
||||
const handler = viewAllHandlers[barActiveIndex.value]
|
||||
handler?.()
|
||||
|
||||
// 关闭通知面板
|
||||
emit('update:value', false)
|
||||
}
|
||||
|
||||
return {
|
||||
changeBar,
|
||||
currentTabIsEmpty,
|
||||
handleViewAll
|
||||
}
|
||||
}
|
||||
|
||||
// 业务逻辑处理
|
||||
const useBusinessLogic = () => {
|
||||
const handleNoticeAll = () => {
|
||||
// 处理查看全部通知
|
||||
console.log('查看全部通知')
|
||||
}
|
||||
|
||||
const handleMsgAll = () => {
|
||||
// 处理查看全部消息
|
||||
console.log('查看全部消息')
|
||||
}
|
||||
|
||||
const handlePendingAll = () => {
|
||||
// 处理查看全部待办
|
||||
console.log('查看全部待办')
|
||||
}
|
||||
|
||||
return {
|
||||
handleNoticeAll,
|
||||
handleMsgAll,
|
||||
handlePendingAll
|
||||
}
|
||||
}
|
||||
|
||||
// 组合所有逻辑
|
||||
const { noticeList, msgList, pendingList, barList } = useNotificationData()
|
||||
const { getNoticeStyle } = useNotificationStyles()
|
||||
const { showNotice } = useNotificationAnimation()
|
||||
const { handleNoticeAll, handleMsgAll, handlePendingAll } = useBusinessLogic()
|
||||
const { changeBar, currentTabIsEmpty, handleViewAll } = useTabManagement(
|
||||
noticeList,
|
||||
msgList,
|
||||
pendingList,
|
||||
{ handleNoticeAll, handleMsgAll, handlePendingAll }
|
||||
)
|
||||
|
||||
// 监听属性变化
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
showNotice(newValue)
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@reference '@styles/core/tailwind.css';
|
||||
|
||||
.art-notification-panel {
|
||||
@apply absolute
|
||||
top-14.5
|
||||
right-5
|
||||
w-90
|
||||
h-125
|
||||
overflow-hidden
|
||||
transition-all
|
||||
duration-300
|
||||
origin-top
|
||||
will-change-[top,left]
|
||||
max-[640px]:top-[65px]
|
||||
max-[640px]:right-0
|
||||
max-[640px]:w-full
|
||||
max-[640px]:h-[80vh];
|
||||
}
|
||||
|
||||
.bar-active {
|
||||
color: var(--theme-color) !important;
|
||||
border-bottom: 2px solid var(--theme-color);
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 5px !important;
|
||||
}
|
||||
|
||||
.dark .scrollbar-thin::-webkit-scrollbar-track {
|
||||
background-color: var(--default-box-color);
|
||||
}
|
||||
|
||||
.dark .scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background-color: #222 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -1,522 +0,0 @@
|
||||
<!-- 锁屏 -->
|
||||
<template>
|
||||
<div class="layout-lock-screen">
|
||||
<!-- 开发者工具警告覆盖层 -->
|
||||
<div
|
||||
v-if="showDevToolsWarning"
|
||||
class="fixed top-0 left-0 z-[999999] flex-cc w-full h-full text-white bg-gradient-to-br from-[#1e1e1e] to-black animate-fade-in"
|
||||
>
|
||||
<div class="p-5 text-center select-none">
|
||||
<div class="mb-7.5 text-5xl">🔒</div>
|
||||
<h1 class="m-0 mb-5 text-3xl font-semibold text-danger">系统已锁定</h1>
|
||||
<p class="max-w-125 m-0 text-lg leading-relaxed text-white">
|
||||
检测到开发者工具已打开<br />
|
||||
为了系统安全,请关闭开发者工具后继续使用
|
||||
</p>
|
||||
<div class="mt-7.5 text-sm text-gray-400">Security Lock Activated</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 锁屏弹窗 -->
|
||||
<div v-if="!isLock">
|
||||
<ElDialog v-model="visible" :width="370" :show-close="false" @open="handleDialogOpen">
|
||||
<div class="flex-c flex-col">
|
||||
<img class="w-16 h-16 rounded-full" src="@imgs/user/avatar.webp" alt="用户头像" />
|
||||
<div class="mt-7.5 mb-3.5 text-base font-medium">{{ userInfo.userName }}</div>
|
||||
<ElForm
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
class="w-[90%]"
|
||||
@submit.prevent="handleLock"
|
||||
>
|
||||
<ElFormItem prop="password">
|
||||
<ElInput
|
||||
v-model="formData.password"
|
||||
type="password"
|
||||
:placeholder="$t('lockScreen.lock.inputPlaceholder')"
|
||||
:show-password="true"
|
||||
autocomplete="new-password"
|
||||
ref="lockInputRef"
|
||||
class="w-full mt-9"
|
||||
@keyup.enter="handleLock"
|
||||
>
|
||||
<template #suffix>
|
||||
<ElIcon class="c-p" @click="handleLock">
|
||||
<Lock />
|
||||
</ElIcon>
|
||||
</template>
|
||||
</ElInput>
|
||||
</ElFormItem>
|
||||
<ElButton type="primary" class="w-full mt-0.5" @click="handleLock" v-ripple>
|
||||
{{ $t('lockScreen.lock.btnText') }}
|
||||
</ElButton>
|
||||
</ElForm>
|
||||
</div>
|
||||
</ElDialog>
|
||||
</div>
|
||||
|
||||
<!-- 解锁界面 -->
|
||||
<div v-else class="unlock-content">
|
||||
<div class="flex-c flex-col w-80">
|
||||
<img class="w-16 h-16 mt-5 rounded-full" src="@imgs/user/avatar.webp" alt="用户头像" />
|
||||
<div class="mt-3 mb-3.5 text-base font-medium">
|
||||
{{ userInfo.userName }}
|
||||
</div>
|
||||
<ElForm
|
||||
ref="unlockFormRef"
|
||||
:model="unlockForm"
|
||||
:rules="rules"
|
||||
class="w-full !px-2.5"
|
||||
@submit.prevent="handleUnlock"
|
||||
>
|
||||
<ElFormItem prop="password">
|
||||
<ElInput
|
||||
v-model="unlockForm.password"
|
||||
type="password"
|
||||
:placeholder="$t('lockScreen.unlock.inputPlaceholder')"
|
||||
:show-password="true"
|
||||
autocomplete="new-password"
|
||||
ref="unlockInputRef"
|
||||
class="mt-5"
|
||||
>
|
||||
<template #suffix>
|
||||
<ElIcon class="c-p" @click="handleUnlock">
|
||||
<Unlock />
|
||||
</ElIcon>
|
||||
</template>
|
||||
</ElInput>
|
||||
</ElFormItem>
|
||||
|
||||
<ElButton type="primary" class="w-full mt-2" @click="handleUnlock" v-ripple>
|
||||
{{ $t('lockScreen.unlock.btnText') }}
|
||||
</ElButton>
|
||||
<div class="w-full text-center">
|
||||
<ElButton
|
||||
text
|
||||
class="mt-2.5 !text-g-600 hover:!text-theme hover:!bg-transparent"
|
||||
@click="toLogin"
|
||||
>
|
||||
{{ $t('lockScreen.unlock.backBtnText') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
</ElForm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Lock, Unlock } from '@element-plus/icons-vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import CryptoJS from 'crypto-js'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useAuthStore } from '@/store/modules/auth'
|
||||
import { mittBus } from '@/utils/sys'
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
|
||||
// 环境变量
|
||||
const ENCRYPT_KEY = import.meta.env.VITE_LOCK_ENCRYPT_KEY
|
||||
|
||||
// Store
|
||||
const userStore = useUserStore()
|
||||
const authStore = useAuthStore()
|
||||
const { info: userInfo, lockPassword, isLock } = storeToRefs(userStore)
|
||||
|
||||
// 响应式数据
|
||||
const visible = ref<boolean>(false)
|
||||
const lockInputRef = ref<any>(null)
|
||||
const unlockInputRef = ref<any>(null)
|
||||
const showDevToolsWarning = ref<boolean>(false)
|
||||
|
||||
// 表单相关
|
||||
const formRef = ref<FormInstance>()
|
||||
const unlockFormRef = ref<FormInstance>()
|
||||
|
||||
const formData = reactive({
|
||||
password: ''
|
||||
})
|
||||
|
||||
const unlockForm = reactive({
|
||||
password: ''
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const rules = computed<FormRules>(() => ({
|
||||
password: [
|
||||
{
|
||||
required: true,
|
||||
message: t('lockScreen.lock.inputPlaceholder'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}))
|
||||
|
||||
// 检测是否为移动设备
|
||||
const isMobile = () => {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
||||
navigator.userAgent
|
||||
)
|
||||
}
|
||||
|
||||
// 添加禁用控制台的函数
|
||||
const disableDevTools = () => {
|
||||
// 禁用右键菜单
|
||||
const handleContextMenu = (e: Event) => {
|
||||
if (isLock.value) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
}
|
||||
document.addEventListener('contextmenu', handleContextMenu, true)
|
||||
|
||||
// 禁用开发者工具相关快捷键
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (!isLock.value) return
|
||||
|
||||
// 禁用 F12
|
||||
if (e.key === 'F12') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁用 Ctrl+Shift+I/J/C/K (开发者工具)
|
||||
if (e.ctrlKey && e.shiftKey) {
|
||||
const key = e.key.toLowerCase()
|
||||
if (['i', 'j', 'c', 'k'].includes(key)) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 禁用 Ctrl+U (查看源代码)
|
||||
if (e.ctrlKey && e.key.toLowerCase() === 'u') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁用 Ctrl+S (保存页面)
|
||||
if (e.ctrlKey && e.key.toLowerCase() === 's') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁用 Ctrl+A (全选)
|
||||
if (e.ctrlKey && e.key.toLowerCase() === 'a') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁用 Ctrl+P (打印)
|
||||
if (e.ctrlKey && e.key.toLowerCase() === 'p') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁用 Ctrl+F (查找)
|
||||
if (e.ctrlKey && e.key.toLowerCase() === 'f') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁用 Alt+Tab (切换窗口)
|
||||
if (e.altKey && e.key === 'Tab') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁用 Ctrl+Tab (切换标签页)
|
||||
if (e.ctrlKey && e.key === 'Tab') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁用 Ctrl+W (关闭标签页)
|
||||
if (e.ctrlKey && e.key.toLowerCase() === 'w') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁用 Ctrl+R 和 F5 (刷新页面)
|
||||
if ((e.ctrlKey && e.key.toLowerCase() === 'r') || e.key === 'F5') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
|
||||
// 禁用 Ctrl+Shift+R (强制刷新)
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'r') {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
return false
|
||||
}
|
||||
}
|
||||
document.addEventListener('keydown', handleKeyDown, true)
|
||||
|
||||
// 禁用选择文本
|
||||
const handleSelectStart = (e: Event) => {
|
||||
if (isLock.value) {
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
}
|
||||
document.addEventListener('selectstart', handleSelectStart, true)
|
||||
|
||||
// 禁用拖拽
|
||||
const handleDragStart = (e: Event) => {
|
||||
if (isLock.value) {
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
}
|
||||
document.addEventListener('dragstart', handleDragStart, true)
|
||||
|
||||
// 监听开发者工具打开状态(仅在桌面端启用)
|
||||
let devtools = { open: false }
|
||||
const threshold = 160
|
||||
let devToolsInterval: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
const checkDevTools = () => {
|
||||
if (!isLock.value || isMobile()) return
|
||||
|
||||
const isDevToolsOpen =
|
||||
window.outerHeight - window.innerHeight > threshold ||
|
||||
window.outerWidth - window.innerWidth > threshold
|
||||
|
||||
if (isDevToolsOpen && !devtools.open) {
|
||||
devtools.open = true
|
||||
showDevToolsWarning.value = true
|
||||
} else if (!isDevToolsOpen && devtools.open) {
|
||||
devtools.open = false
|
||||
showDevToolsWarning.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 仅在桌面端启用开发者工具检测
|
||||
if (!isMobile()) {
|
||||
devToolsInterval = setInterval(checkDevTools, 500)
|
||||
}
|
||||
|
||||
// 返回清理函数
|
||||
return () => {
|
||||
document.removeEventListener('contextmenu', handleContextMenu, true)
|
||||
document.removeEventListener('keydown', handleKeyDown, true)
|
||||
document.removeEventListener('selectstart', handleSelectStart, true)
|
||||
document.removeEventListener('dragstart', handleDragStart, true)
|
||||
if (devToolsInterval) {
|
||||
clearInterval(devToolsInterval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
const verifyPassword = (inputPassword: string, storedPassword: string): boolean => {
|
||||
try {
|
||||
const decryptedPassword = CryptoJS.AES.decrypt(storedPassword, ENCRYPT_KEY).toString(
|
||||
CryptoJS.enc.Utf8
|
||||
)
|
||||
return inputPassword === decryptedPassword
|
||||
} catch (error) {
|
||||
console.error('密码解密失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 事件处理函数
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (event.altKey && event.key.toLowerCase() === '¬') {
|
||||
event.preventDefault()
|
||||
visible.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const handleDialogOpen = () => {
|
||||
setTimeout(() => {
|
||||
lockInputRef.value?.input?.focus()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
const handleLock = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
await formRef.value.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
const encryptedPassword = CryptoJS.AES.encrypt(formData.password, ENCRYPT_KEY).toString()
|
||||
userStore.setLockStatus(true)
|
||||
userStore.setLockPassword(encryptedPassword)
|
||||
visible.value = false
|
||||
formData.password = ''
|
||||
} else {
|
||||
console.error('表单验证失败:', fields)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleUnlock = async () => {
|
||||
if (!unlockFormRef.value) return
|
||||
|
||||
await unlockFormRef.value.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
const isValid = verifyPassword(unlockForm.password, lockPassword.value)
|
||||
|
||||
if (isValid) {
|
||||
try {
|
||||
userStore.setLockStatus(false)
|
||||
userStore.setLockPassword('')
|
||||
unlockForm.password = ''
|
||||
visible.value = false
|
||||
showDevToolsWarning.value = false
|
||||
} catch (error) {
|
||||
console.error('更新store失败:', error)
|
||||
}
|
||||
} else {
|
||||
// 触发抖动动画
|
||||
const inputElement = unlockInputRef.value?.$el
|
||||
if (inputElement) {
|
||||
inputElement.classList.add('shake-animation')
|
||||
setTimeout(() => {
|
||||
inputElement.classList.remove('shake-animation')
|
||||
}, 300)
|
||||
}
|
||||
ElMessage.error(t('lockScreen.pwdError'))
|
||||
unlockForm.password = ''
|
||||
}
|
||||
} else {
|
||||
console.error('表单验证失败:', fields)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const toLogin = () => {
|
||||
// 锁屏页"返回登录":先撤销后端 token,再走本地清理 + 跳登录
|
||||
void authStore.logout()
|
||||
}
|
||||
|
||||
const openLockScreen = () => {
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// 监听锁屏状态变化
|
||||
watch(isLock, (newValue) => {
|
||||
if (newValue) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
setTimeout(() => {
|
||||
unlockInputRef.value?.input?.focus()
|
||||
}, 100)
|
||||
} else {
|
||||
document.body.style.overflow = 'auto'
|
||||
showDevToolsWarning.value = false
|
||||
}
|
||||
})
|
||||
|
||||
// 存储清理函数
|
||||
let cleanupDevTools: (() => void) | null = null
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
mittBus.on('openLockScreen', openLockScreen)
|
||||
document.addEventListener('keydown', handleKeydown)
|
||||
|
||||
if (isLock.value) {
|
||||
visible.value = true
|
||||
setTimeout(() => {
|
||||
unlockInputRef.value?.input?.focus()
|
||||
}, 100)
|
||||
}
|
||||
|
||||
// 初始化禁用开发者工具功能
|
||||
cleanupDevTools = disableDevTools()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('keydown', handleKeydown)
|
||||
document.body.style.overflow = 'auto'
|
||||
// 清理禁用开发者工具的事件监听器
|
||||
if (cleanupDevTools) {
|
||||
cleanupDevTools()
|
||||
cleanupDevTools = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-lock-screen :deep(.el-dialog) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.unlock-content {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 2500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
background-image: url('@imgs/lock/bg_light.webp');
|
||||
background-size: cover;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.unlock-content {
|
||||
background-image: url('@imgs/lock/bg_dark.webp');
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fade-in 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
10%,
|
||||
30%,
|
||||
50%,
|
||||
70%,
|
||||
90% {
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
|
||||
20%,
|
||||
40%,
|
||||
60%,
|
||||
80% {
|
||||
transform: translateX(10px);
|
||||
}
|
||||
}
|
||||
|
||||
.shake-animation {
|
||||
animation: shake 0.5s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
@@ -37,17 +37,6 @@ export const globalComponentsConfig: GlobalComponentConfig[] = [
|
||||
),
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
name: '锁屏',
|
||||
key: 'screen-lock',
|
||||
component: defineAsyncComponent(
|
||||
() => import('@/components/core/layouts/art-screen-lock/index.vue')
|
||||
),
|
||||
// 入口已摘除(Phase 3 终审 M-1):锁屏把真实密码 AES 存 localStorage
|
||||
// 且密钥是公开模板默认值,防御价值为零——enabled: false 连全局挂载
|
||||
// 也不注册;组件代码保留,Phase 4 决定删否。
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
name: '聊天窗口',
|
||||
key: 'chat-window',
|
||||
|
||||
@@ -38,12 +38,6 @@ export const headerBarConfig: HeaderBarFeatureConfig = {
|
||||
enabled: true,
|
||||
description: '全屏切换功能'
|
||||
},
|
||||
notification: {
|
||||
// 入口已摘除(Phase 3 终审 M-2):模板通知中心是硬编码演示数据,
|
||||
// 无真实数据源——enabled: false 隐藏铃铛;组件保留,接真数据源时再开。
|
||||
enabled: false,
|
||||
description: '通知中心,显示系统通知和消息'
|
||||
},
|
||||
chat: {
|
||||
enabled: true,
|
||||
description: '聊天功能,提供实时沟通'
|
||||
|
||||
Vendored
-2
@@ -2,7 +2,5 @@
|
||||
|
||||
declare module 'nprogress'
|
||||
|
||||
declare module 'crypto-js'
|
||||
|
||||
// 全局变量声明
|
||||
declare const __APP_VERSION__: string // 版本号
|
||||
|
||||
@@ -83,11 +83,6 @@ export function useHeaderBar() {
|
||||
return isFeatureEnabled('fullscreen')
|
||||
})
|
||||
|
||||
// 检查通知中心是否显示
|
||||
const shouldShowNotification = computed(() => {
|
||||
return isFeatureEnabled('notification')
|
||||
})
|
||||
|
||||
// 检查聊天功能是否显示
|
||||
const shouldShowChat = computed(() => {
|
||||
return isFeatureEnabled('chat')
|
||||
@@ -179,7 +174,6 @@ export function useHeaderBar() {
|
||||
shouldShowBreadcrumb, // 是否显示面包屑
|
||||
shouldShowGlobalSearch, // 是否显示全局搜索
|
||||
shouldShowFullscreen, // 是否显示全屏按钮
|
||||
shouldShowNotification, // 是否显示通知中心
|
||||
shouldShowChat, // 是否显示聊天功能
|
||||
shouldShowLanguage, // 是否显示语言切换
|
||||
shouldShowSettings, // 是否显示设置面板
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
},
|
||||
"user": {
|
||||
"changePassword": "Change Password",
|
||||
"lockScreen": "Lock Screen",
|
||||
"logout": "Log Out"
|
||||
},
|
||||
"guide": {
|
||||
@@ -122,19 +121,6 @@
|
||||
"resetFailed": "Reset failed, please refresh the page and try again"
|
||||
}
|
||||
},
|
||||
"notice": {
|
||||
"title": "Notice",
|
||||
"btnRead": "Mark as read",
|
||||
"bar": [
|
||||
"Notice",
|
||||
"Message",
|
||||
"Todo"
|
||||
],
|
||||
"text": [
|
||||
"No"
|
||||
],
|
||||
"viewAll": "View all"
|
||||
},
|
||||
"worktab": {
|
||||
"btn": {
|
||||
"refresh": "Refresh",
|
||||
@@ -167,18 +153,6 @@
|
||||
"message": "Welcome back"
|
||||
}
|
||||
},
|
||||
"lockScreen": {
|
||||
"pwdError": "Password error",
|
||||
"lock": {
|
||||
"inputPlaceholder": "Please input lock screen password",
|
||||
"btnText": "Lock"
|
||||
},
|
||||
"unlock": {
|
||||
"inputPlaceholder": "Please input unlock password",
|
||||
"btnText": "Unlock",
|
||||
"backBtnText": "Back to login"
|
||||
}
|
||||
},
|
||||
"greeting": {
|
||||
"dawn": "Good morning!",
|
||||
"morning": "Good morning!",
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
},
|
||||
"user": {
|
||||
"changePassword": "修改密码",
|
||||
"lockScreen": "锁定屏幕",
|
||||
"logout": "退出登录"
|
||||
},
|
||||
"guide": {
|
||||
@@ -122,19 +121,6 @@
|
||||
"resetFailed": "重置失败,请刷新页面后重试"
|
||||
}
|
||||
},
|
||||
"notice": {
|
||||
"title": "通知",
|
||||
"btnRead": "标为已读",
|
||||
"bar": [
|
||||
"通知",
|
||||
"消息",
|
||||
"代办"
|
||||
],
|
||||
"text": [
|
||||
"暂无"
|
||||
],
|
||||
"viewAll": "查看全部"
|
||||
},
|
||||
"worktab": {
|
||||
"btn": {
|
||||
"refresh": "刷新",
|
||||
@@ -167,18 +153,6 @@
|
||||
"message": "欢迎回来"
|
||||
}
|
||||
},
|
||||
"lockScreen": {
|
||||
"pwdError": "密码错误",
|
||||
"lock": {
|
||||
"inputPlaceholder": "请输入锁屏密码",
|
||||
"btnText": "锁定"
|
||||
},
|
||||
"unlock": {
|
||||
"inputPlaceholder": "请输入解锁密码",
|
||||
"btnText": "解锁",
|
||||
"backBtnText": "返回登录"
|
||||
}
|
||||
},
|
||||
"greeting": {
|
||||
"dawn": "凌晨了!",
|
||||
"morning": "上午好!",
|
||||
|
||||
@@ -198,8 +198,6 @@ export interface HeaderBarFeatureConfig {
|
||||
globalSearch: FeatureConfigItem
|
||||
/** 全屏功能 */
|
||||
fullscreen: FeatureConfigItem
|
||||
/** 通知功能 */
|
||||
notification: FeatureConfigItem
|
||||
/** 聊天功能 */
|
||||
chat: FeatureConfigItem
|
||||
/** 多语言切换 */
|
||||
|
||||
-2
@@ -40,14 +40,12 @@ declare module 'vue' {
|
||||
ArtLogo: typeof import('./../../components/core/base/art-logo/index.vue')['default']
|
||||
ArtMenuRight: typeof import('./../../components/core/others/art-menu-right/index.vue')['default']
|
||||
ArtMixedMenu: typeof import('./../../components/core/layouts/art-menus/art-mixed-menu/index.vue')['default']
|
||||
ArtNotification: typeof import('./../../components/core/layouts/art-notification/index.vue')['default']
|
||||
ArtPageContent: typeof import('./../../components/core/layouts/art-page-content/index.vue')['default']
|
||||
ArtProgressCard: typeof import('./../../components/core/cards/art-progress-card/index.vue')['default']
|
||||
ArtRadarChart: typeof import('./../../components/core/charts/art-radar-chart/index.vue')['default']
|
||||
ArtResultPage: typeof import('./../../components/core/views/result/ArtResultPage.vue')['default']
|
||||
ArtRingChart: typeof import('./../../components/core/charts/art-ring-chart/index.vue')['default']
|
||||
ArtScatterChart: typeof import('./../../components/core/charts/art-scatter-chart/index.vue')['default']
|
||||
ArtScreenLock: typeof import('./../../components/core/layouts/art-screen-lock/index.vue')['default']
|
||||
ArtSearchBar: typeof import('./../../components/core/forms/art-search-bar/index.vue')['default']
|
||||
ArtSettingsPanel: typeof import('./../../components/core/layouts/art-settings-panel/index.vue')['default']
|
||||
ArtSidebarMenu: typeof import('./../../components/core/layouts/art-menus/art-sidebar-menu/index.vue')['default']
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* ## 使用场景
|
||||
*
|
||||
* - 跨层级组件通信
|
||||
* - 全局功能触发(设置、搜索、聊天、锁屏等)
|
||||
* - 全局功能触发(设置、搜索、聊天等)
|
||||
* - 特效触发(烟花效果)
|
||||
* - 避免 props 层层传递
|
||||
*
|
||||
@@ -36,7 +36,6 @@
|
||||
* - openSetting: 打开设置面板
|
||||
* - openSearchDialog: 打开搜索对话框
|
||||
* - openChat: 打开聊天窗口
|
||||
* - openLockScreen: 打开锁屏
|
||||
*
|
||||
* @module utils/sys/mittBus
|
||||
* @author Art Design Pro Team
|
||||
@@ -53,8 +52,6 @@ type Events = {
|
||||
openSearchDialog: void
|
||||
// 打开聊天窗口事件 - 无参数
|
||||
openChat: void
|
||||
// 打开锁屏事件 - 无参数
|
||||
openLockScreen: void
|
||||
}
|
||||
|
||||
// 创建类型安全的事件总线实例
|
||||
|
||||
@@ -139,7 +139,6 @@ export default ({ mode }: { mode: string }) => {
|
||||
'echarts/charts',
|
||||
'echarts/components',
|
||||
'echarts/renderers',
|
||||
'crypto-js',
|
||||
'element-plus/es',
|
||||
'element-plus/es/components/*/style/css',
|
||||
'element-plus/es/components/*/style/index'
|
||||
|
||||
Reference in New Issue
Block a user