189 lines
6.9 KiB
Vue
189 lines
6.9 KiB
Vue
<template>
|
|
<ElConfigProvider
|
|
size="default"
|
|
:locale="locales[language]"
|
|
:z-index="3000"
|
|
:card="{
|
|
shadow: 'never'
|
|
}"
|
|
:dialog="{ alignCenter: true }"
|
|
>
|
|
<RouterView></RouterView>
|
|
<AttachmentPreviewHost />
|
|
</ElConfigProvider>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onBeforeMount, onMounted, onBeforeUnmount } from 'vue'
|
|
import { storeToRefs } from 'pinia'
|
|
import AttachmentPreviewHost from '@/components/business/attachment-preview-host/index.vue'
|
|
import { useUserStore } from './store/modules/user'
|
|
import zh from 'element-plus/es/locale/lang/zh-cn'
|
|
import en from 'element-plus/es/locale/lang/en'
|
|
import { systemUpgrade } from './utils/sys'
|
|
import { toggleTransition } from './utils/ui/animation'
|
|
import { checkStorageCompatibility } from './utils/storage'
|
|
import { initializeTheme } from './hooks/core/useTheme'
|
|
import { refreshFilterCollapse, setupFilterCollapseObserver } from './utils/ui/filter-collapse'
|
|
import { setupListTableRowSelect } from './utils/ui/list-table-row-select'
|
|
|
|
const userStore = useUserStore()
|
|
const { language } = storeToRefs(userStore)
|
|
|
|
const locales = {
|
|
zh: zh,
|
|
en: en
|
|
}
|
|
|
|
let buttonIconObserver: MutationObserver | null = null
|
|
let panelTitleObserver: MutationObserver | null = null
|
|
let filterCollapseObserver: MutationObserver | null = null
|
|
let teardownListTableRowSelect: (() => void) | null = null
|
|
|
|
const iconClassByText: Array<[RegExp, string]> = [
|
|
[/(新增|新建|创建|添加)/, 'auto-icon-plus'],
|
|
[/(删除|移除|清空|关闭)/, 'auto-icon-delete'],
|
|
[/(导入|上传)/, 'auto-icon-upload'],
|
|
[/(导出|下载)/, 'auto-icon-download'],
|
|
[/(搜索|查询|筛选)/, 'auto-icon-search'],
|
|
[/(重置|刷新)/, 'auto-icon-refresh'],
|
|
[/(编辑|修改|配置|设置)/, 'auto-icon-edit'],
|
|
[/(查看|详情)/, 'auto-icon-view'],
|
|
[/(保存|提交|确定|确认)/, 'auto-icon-check']
|
|
]
|
|
|
|
const autoIconClassList = iconClassByText.map(([, cls]) => cls)
|
|
|
|
const refreshButtonAutoIcons = () => {
|
|
const selectors = [
|
|
'.page-container .el-button:not(.is-circle):not(.el-button--text):not(:has(.el-icon))',
|
|
'.console-container .el-button:not(.is-circle):not(.el-button--text):not(:has(.el-icon))'
|
|
]
|
|
const buttons = document.querySelectorAll<HTMLElement>(selectors.join(','))
|
|
buttons.forEach((button) => {
|
|
autoIconClassList.forEach((className) => button.classList.remove(className))
|
|
// 区域/状态 Tab 仅展示文案,不参与按钮自动图标
|
|
if (button.classList.contains('region-tab-btn')) return
|
|
const text = (button.textContent || '').replace(/\s+/g, '')
|
|
const matched = iconClassByText.find(([rule]) => rule.test(text))
|
|
if (matched) button.classList.add(matched[1])
|
|
})
|
|
}
|
|
|
|
const panelTitleIconByText: Array<[RegExp, string]> = [
|
|
[/(租户)/, 'auto-title-tenant'],
|
|
[/(字典)/, 'auto-title-dict'],
|
|
[/(角色)/, 'auto-title-role'],
|
|
[/(菜单)/, 'auto-title-menu'],
|
|
[/(职位)/, 'auto-title-post'],
|
|
[/(站点)/, 'auto-title-location'],
|
|
[/(单位)/, 'auto-title-unit'],
|
|
[/(快递)/, 'auto-title-express'],
|
|
[/(组织)/, 'auto-title-dept'],
|
|
[/(SOP)/i, 'auto-title-file'],
|
|
[/(捐赠|协议)/, 'auto-title-file'],
|
|
[/(缴费|退费)/, 'auto-title-calc'],
|
|
[/(接收|检查项|样本|编码)/, 'auto-title-file'],
|
|
[/(审核|记录)/, 'auto-title-file'],
|
|
[/(客户|公司|机构|医院)/, 'auto-title-user'],
|
|
[/(采集|地区|洁净|库位)/, 'auto-title-location'],
|
|
[/(预约)/, 'auto-title-operation'],
|
|
[/(工单)/, 'auto-title-receipt'],
|
|
[/(流程|待办|已办|申请|请假|执行|计划)/, 'auto-title-operation'],
|
|
[/(耗材|试剂)/, 'auto-title-box'],
|
|
[/(路线)/, 'auto-title-route'],
|
|
[/(工艺)/, 'auto-title-settings'],
|
|
[/(规格|价格|体系|渠道|项目)/, 'auto-title-category'],
|
|
[/(类型)/, 'auto-title-category'],
|
|
[/(任务|调度)/, 'auto-title-operation'],
|
|
[/(区域)/, 'auto-title-location'],
|
|
[/(用户|账号)/, 'auto-title-user'],
|
|
[/(参数|配置)/, 'auto-title-settings'],
|
|
[/(分类)/, 'auto-title-category'],
|
|
[/(商品|产品)/, 'auto-title-box'],
|
|
[/(订单)/, 'auto-title-receipt'],
|
|
[/(发票)/, 'auto-title-file'],
|
|
[/(询价|报价)/, 'auto-title-quote'],
|
|
[/(结算)/, 'auto-title-calc'],
|
|
[/(入口|概览|选择)/, 'auto-title-menu']
|
|
]
|
|
|
|
const panelTitleClassList = [...panelTitleIconByText.map(([, cls]) => cls), 'auto-title-list']
|
|
const panelTitleBarClass = 'title-bar-heading'
|
|
const panelTitleHeadingSelector =
|
|
'.page-container .panel-top h2, .page-container .panel-top h3, .page-container .storage-content-header h3, .page-container .storage-aside__head h2, .page-container .user-dept-aside__head h2, .console-container .panel-top h2'
|
|
|
|
const refreshPanelTitleIcons = () => {
|
|
const headings = document.querySelectorAll<HTMLHeadingElement>(panelTitleHeadingSelector)
|
|
|
|
headings.forEach((heading) => {
|
|
const text = (heading.textContent || '').replace(/\s+/g, '')
|
|
const preferPanelIcon = Boolean(heading.closest('.page-container, .console-container'))
|
|
|
|
if (!preferPanelIcon) return
|
|
|
|
if (heading.classList.contains(panelTitleBarClass)) return
|
|
|
|
heading.classList.remove(panelTitleBarClass)
|
|
|
|
// 已手写 title-ic(例如 delivery-demo)则不干预
|
|
if (heading.querySelector('.title-ic')) return
|
|
|
|
// 确保存在容器
|
|
const span = document.createElement('span')
|
|
span.className = 'title-ic'
|
|
span.setAttribute('aria-hidden', 'true')
|
|
heading.prepend(span)
|
|
|
|
// 根据标题文本匹配图标类型
|
|
panelTitleClassList.forEach((className) => heading.classList.remove(className))
|
|
const matched = panelTitleIconByText.find(([rule]) => rule.test(text))
|
|
heading.classList.add(matched ? matched[1] : 'auto-title-list')
|
|
})
|
|
}
|
|
|
|
onBeforeMount(() => {
|
|
toggleTransition(true)
|
|
initializeTheme()
|
|
})
|
|
|
|
onMounted(() => {
|
|
checkStorageCompatibility()
|
|
toggleTransition(false)
|
|
systemUpgrade()
|
|
refreshButtonAutoIcons()
|
|
refreshPanelTitleIcons()
|
|
refreshFilterCollapse()
|
|
|
|
buttonIconObserver = new MutationObserver(() => {
|
|
refreshButtonAutoIcons()
|
|
})
|
|
buttonIconObserver.observe(document.body, {
|
|
childList: true,
|
|
subtree: true
|
|
})
|
|
|
|
panelTitleObserver = new MutationObserver(() => {
|
|
refreshPanelTitleIcons()
|
|
})
|
|
panelTitleObserver.observe(document.body, {
|
|
childList: true,
|
|
subtree: true
|
|
})
|
|
|
|
filterCollapseObserver = setupFilterCollapseObserver()
|
|
teardownListTableRowSelect = setupListTableRowSelect()
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
buttonIconObserver?.disconnect()
|
|
buttonIconObserver = null
|
|
panelTitleObserver?.disconnect()
|
|
panelTitleObserver = null
|
|
filterCollapseObserver?.disconnect()
|
|
filterCollapseObserver = null
|
|
teardownListTableRowSelect?.()
|
|
teardownListTableRowSelect = null
|
|
})
|
|
</script>
|