App.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <template>
  2. <ElConfigProvider
  3. size="default"
  4. :locale="locales[language]"
  5. :z-index="3000"
  6. :card="{
  7. shadow: 'never'
  8. }"
  9. :dialog="{ alignCenter: true }"
  10. >
  11. <RouterView></RouterView>
  12. </ElConfigProvider>
  13. </template>
  14. <script setup lang="ts">
  15. import { onBeforeMount, onMounted, onBeforeUnmount } from 'vue'
  16. import { storeToRefs } from 'pinia'
  17. import { useUserStore } from './store/modules/user'
  18. import zh from 'element-plus/es/locale/lang/zh-cn'
  19. import en from 'element-plus/es/locale/lang/en'
  20. import { systemUpgrade } from './utils/sys'
  21. import { toggleTransition } from './utils/ui/animation'
  22. import { checkStorageCompatibility } from './utils/storage'
  23. import { initializeTheme } from './hooks/core/useTheme'
  24. const userStore = useUserStore()
  25. const { language } = storeToRefs(userStore)
  26. const locales = {
  27. zh: zh,
  28. en: en
  29. }
  30. let buttonIconObserver: MutationObserver | null = null
  31. let panelTitleObserver: MutationObserver | null = null
  32. const iconClassByText: Array<[RegExp, string]> = [
  33. [/(新增|新建|创建|添加)/, 'auto-icon-plus'],
  34. [/(删除|移除|清空|关闭)/, 'auto-icon-delete'],
  35. [/(导入|上传)/, 'auto-icon-upload'],
  36. [/(导出|下载)/, 'auto-icon-download'],
  37. [/(搜索|查询|筛选)/, 'auto-icon-search'],
  38. [/(重置|刷新)/, 'auto-icon-refresh'],
  39. [/(编辑|修改|配置|设置)/, 'auto-icon-edit'],
  40. [/(查看|详情)/, 'auto-icon-view'],
  41. [/(保存|提交|确定|确认)/, 'auto-icon-check']
  42. ]
  43. const autoIconClassList = iconClassByText.map(([, cls]) => cls)
  44. const refreshButtonAutoIcons = () => {
  45. const selectors = [
  46. '.page-container .el-button:not(.is-circle):not(.el-button--text):not(:has(.el-icon))',
  47. '.console-container .el-button:not(.is-circle):not(.el-button--text):not(:has(.el-icon))'
  48. ]
  49. const buttons = document.querySelectorAll<HTMLElement>(selectors.join(','))
  50. buttons.forEach((button) => {
  51. autoIconClassList.forEach((className) => button.classList.remove(className))
  52. const text = (button.textContent || '').replace(/\s+/g, '')
  53. const matched = iconClassByText.find(([rule]) => rule.test(text))
  54. if (matched) button.classList.add(matched[1])
  55. })
  56. }
  57. const panelTitleIconByText: Array<[RegExp, string]> = [
  58. [/(任务|调度)/, 'auto-title-operation'],
  59. [/(区域)/, 'auto-title-location'],
  60. [/(用户|账号)/, 'auto-title-user'],
  61. [/(参数|配置)/, 'auto-title-settings'],
  62. [/(分类)/, 'auto-title-category'],
  63. [/(商品|产品)/, 'auto-title-box'],
  64. [/(订单)/, 'auto-title-receipt'],
  65. [/(发票)/, 'auto-title-file'],
  66. [/(询价|报价)/, 'auto-title-quote'],
  67. [/(结算)/, 'auto-title-calc']
  68. ]
  69. const panelTitleClassList = panelTitleIconByText.map(([, cls]) => cls)
  70. const panelTitleBarClass = 'title-bar-heading'
  71. const panelTitleBarTextRules: RegExp[] = [
  72. /(数据概览|快捷菜单|分类列表|单位列表|商品列表|参数列表|用户列表|区域列表|区域与询报价概览|报价管理|订单列表|最新订单|待结算订单|结算单记录|配送任务列表|预警列表|订单设置|客服设置|请求列表|近30日趋势|今日各区域)/
  73. ]
  74. const refreshPanelTitleIcons = () => {
  75. const headings = document.querySelectorAll<HTMLHeadingElement>(
  76. '.page-container .panel-top h2, .console-container .panel-top h2'
  77. )
  78. headings.forEach((h2) => {
  79. const text = (h2.textContent || '').replace(/\s+/g, '')
  80. const useBar = panelTitleBarTextRules.some((rule) => rule.test(text))
  81. if (useBar) {
  82. h2.querySelector('.title-ic')?.remove()
  83. panelTitleClassList.forEach((className) => h2.classList.remove(className))
  84. h2.classList.add(panelTitleBarClass)
  85. return
  86. }
  87. h2.classList.remove(panelTitleBarClass)
  88. // 已手写 title-ic(例如 delivery-demo)则不干预
  89. if (h2.querySelector('.title-ic')) return
  90. // 确保存在容器
  91. const span = document.createElement('span')
  92. span.className = 'title-ic'
  93. span.setAttribute('aria-hidden', 'true')
  94. h2.prepend(span)
  95. // 根据标题文本匹配图标类型
  96. panelTitleClassList.forEach((className) => h2.classList.remove(className))
  97. const matched = panelTitleIconByText.find(([rule]) => rule.test(text))
  98. if (matched) h2.classList.add(matched[1])
  99. })
  100. }
  101. onBeforeMount(() => {
  102. toggleTransition(true)
  103. initializeTheme()
  104. })
  105. onMounted(() => {
  106. checkStorageCompatibility()
  107. toggleTransition(false)
  108. systemUpgrade()
  109. refreshButtonAutoIcons()
  110. refreshPanelTitleIcons()
  111. buttonIconObserver = new MutationObserver(() => {
  112. refreshButtonAutoIcons()
  113. })
  114. buttonIconObserver.observe(document.body, {
  115. childList: true,
  116. subtree: true
  117. })
  118. panelTitleObserver = new MutationObserver(() => {
  119. refreshPanelTitleIcons()
  120. })
  121. panelTitleObserver.observe(document.body, {
  122. childList: true,
  123. subtree: true
  124. })
  125. })
  126. onBeforeUnmount(() => {
  127. buttonIconObserver?.disconnect()
  128. buttonIconObserver = null
  129. panelTitleObserver?.disconnect()
  130. panelTitleObserver = null
  131. })
  132. </script>