| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- <template>
- <ElConfigProvider
- size="default"
- :locale="locales[language]"
- :z-index="3000"
- :card="{
- shadow: 'never'
- }"
- :dialog="{ alignCenter: true }"
- >
- <RouterView></RouterView>
- </ElConfigProvider>
- </template>
- <script setup lang="ts">
- import { onBeforeMount, onMounted, onBeforeUnmount } from 'vue'
- import { storeToRefs } from 'pinia'
- 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 { toggleTransition } from './utils/ui/animation'
- import { checkStorageCompatibility } from './utils/storage'
- import { initializeTheme } from './hooks/core/useTheme'
- const userStore = useUserStore()
- const { language } = storeToRefs(userStore)
- const locales = {
- zh: zh,
- en: en
- }
- let buttonIconObserver: MutationObserver | null = null
- let panelTitleObserver: MutationObserver | 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-garment'],
- [/(采集|地区|洁净|库位)/, 'auto-title-location'],
- [/(预约)/, 'auto-title-operation'],
- [/(工单)/, 'auto-title-receipt'],
- [/(流程|待办|已办|申请|请假|执行|计划)/, 'auto-title-operation'],
- [/(耗材|试剂|设备|物料库)/, 'auto-title-box'],
- [/(路线)/, 'auto-title-route'],
- [/(工艺库)/, 'auto-title-process'],
- [/(工艺基础)/, 'auto-title-category'],
- [/(工艺)/, '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)
- refreshButtonAutoIcons()
- refreshPanelTitleIcons()
- buttonIconObserver = new MutationObserver(() => {
- refreshButtonAutoIcons()
- })
- buttonIconObserver.observe(document.body, {
- childList: true,
- subtree: true
- })
- panelTitleObserver = new MutationObserver(() => {
- refreshPanelTitleIcons()
- })
- panelTitleObserver.observe(document.body, {
- childList: true,
- subtree: true
- })
- })
- onBeforeUnmount(() => {
- buttonIconObserver?.disconnect()
- buttonIconObserver = null
- panelTitleObserver?.disconnect()
- panelTitleObserver = null
- })
- </script>
|