瀏覽代碼

Merge remote-tracking branch 'yudao/master' into dev-to-dev

puhui999 2 年之前
父節點
當前提交
0b06f36aa2
共有 53 個文件被更改,包括 479 次插入395 次删除
  1. 7 3
      .vscode/extensions.json
  2. 7 9
      .vscode/settings.json
  3. 15 15
      README.md
  4. 2 2
      build/vite/index.ts
  5. 1 1
      build/vite/optimize.ts
  6. 46 45
      package.json
  7. 0 0
      public/home.png
  8. 0 5
      src/api/login/index.ts
  9. 2 0
      src/api/mall/promotion/combination/combinationactivity.ts
  10. 1 1
      src/components/ContentDetailWrap/src/ContentDetailWrap.vue
  11. 3 1
      src/components/Cropper/src/CopperModal.vue
  12. 2 2
      src/components/Descriptions/src/Descriptions.vue
  13. 12 16
      src/components/Dialog/src/Dialog.vue
  14. 2 3
      src/components/Editor/src/Editor.vue
  15. 3 1
      src/components/InputPassword/src/InputPassword.vue
  16. 21 21
      src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue
  17. 1 3
      src/hooks/web/useTable.ts
  18. 17 10
      src/layout/components/AppView.vue
  19. 1 1
      src/layout/components/LocaleDropdown/src/LocaleDropdown.vue
  20. 1 2
      src/layout/components/Logo/src/Logo.vue
  21. 0 9
      src/layout/components/Menu/src/Menu.vue
  22. 6 3
      src/layout/components/Message/src/Message.vue
  23. 0 1
      src/layout/components/Setting/src/Setting.vue
  24. 2 13
      src/layout/components/TabMenu/src/TabMenu.vue
  25. 58 62
      src/layout/components/TagsView/src/TagsView.vue
  26. 7 7
      src/layout/components/ToolHeader.vue
  27. 1 1
      src/layout/components/UserInfo/src/UserInfo.vue
  28. 54 26
      src/layout/components/useRenderLayout.tsx
  29. 2 2
      src/main.ts
  30. 1 0
      src/plugins/unocss/index.ts
  31. 0 3
      src/plugins/windi.css/index.ts
  32. 1 4
      src/store/modules/permission.ts
  33. 1 0
      src/store/modules/user.ts
  34. 3 1
      src/styles/index.scss
  35. 5 12
      src/styles/var.css
  36. 2 2
      src/views/Home/Index.vue
  37. 16 12
      src/views/Login/Login.vue
  38. 1 1
      src/views/Login/components/LoginForm.vue
  39. 10 1
      src/views/Login/components/MobileForm.vue
  40. 1 1
      src/views/Login/components/QrCodeForm.vue
  41. 12 2
      src/views/Login/components/SSOLogin.vue
  42. 2 1
      src/views/infra/build/index.vue
  43. 1 1
      src/views/infra/job/index.vue
  44. 9 8
      src/views/mall/product/spu/components/SkuList.vue
  45. 1 5
      src/views/mall/promotion/combination/activity/CombinationActivityForm.vue
  46. 2 1
      src/views/mall/promotion/combination/activity/index.vue
  47. 2 0
      src/views/mall/promotion/components/SpuAndSkuList.vue
  48. 1 5
      src/views/mall/promotion/seckill/activity/SeckillActivityForm.vue
  49. 4 4
      src/views/mall/trade/order/index.vue
  50. 23 3
      src/views/mp/components/wx-msg/comment.scss
  51. 2 2
      tsconfig.json
  52. 105 0
      uno.config.ts
  53. 0 61
      windi.config.ts

+ 7 - 3
.vscode/extensions.json

@@ -1,15 +1,19 @@
 {
   "recommendations": [
-    "voorjaar.windicss-intellisense",
+    "christian-kohler.path-intellisense",
     "vscode-icons-team.vscode-icons",
     "davidanson.vscode-markdownlint",
     "stylelint.vscode-stylelint",
     "dbaeumer.vscode-eslint",
     "esbenp.prettier-vscode",
-    "vue.volar",
+    "mrmlnc.vscode-less",
     "lokalise.i18n-ally",
+    "redhat.vscode-yaml",
+    "csstools.postcss",
     "mikestead.dotenv",
     "eamodio.gitlens",
-    "antfu.iconify"
+    "antfu.iconify",
+    "antfu.unocss",
+    "Vue.volar"
   ]
 }

+ 7 - 9
.vscode/settings.json

@@ -102,14 +102,12 @@
   "i18n-ally.displayLanguage": "zh-CN",
   "i18n-ally.enabledFrameworks": ["vue", "react"],
   "cSpell.words": [
-    "vben",
-    "windicss",
-    "tailwind",
+    "xingyu",
+    "yudao",
+    "unocss",
     "browserslist",
-    "tailwindcss",
     "esnext",
-    "antv",
-    "tinymce",
+    "unplugin",
     "qrcode",
     "sider",
     "pinia",
@@ -123,8 +121,9 @@
     "codemirror",
     "iconify",
     "commitlint",
-    "vditor",
+    "videojs",
     "echarts",
+    "wangeditor",
     "cropperjs",
     "logicflow",
     "vueuse",
@@ -132,8 +131,7 @@
     "lintstagedrc",
     "brotli",
     "sider",
-    "pnpm",
-    "antd"
+    "pnpm"
   ],
   "vetur.format.scriptInitialIndent": true,
   "vetur.format.styleInitialIndent": true,

+ 15 - 15
README.md

@@ -24,7 +24,7 @@
 * 改换 saas,自动引入等功能
 * 使用 Element Plus 免费开源的中后台模版,具备如下特性:
 
-![首页](preview/home.png)
+![首页](public/home.png)
 
 * **最新技术栈**:使用 Vue3、Vite4 等前端前沿技术开发
 * **TypeScript**: 应用程序级 JavaScript 的语言
@@ -39,15 +39,15 @@
 | 框架                                                                   | 说明               | 版本     |
 |----------------------------------------------------------------------|------------------|--------|
 | [Vue](https://staging-cn.vuejs.org/)                                 | Vue 框架           | 3.3.4 |
-| [Vite](https://cn.vitejs.dev//)                                      | 开发与构建工具          | 4.3.9  |
-| [Element Plus](https://element-plus.org/zh-CN/)                      | Element Plus     | 2.3.7 |
-| [TypeScript](https://www.typescriptlang.org/docs/)                   | JavaScript 的超集   | 5.0.4  |
-| [pinia](https://pinia.vuejs.org/)                                    | Vue 存储库 替代 vuex5 | 2.1.4 |
-| [vueuse](https://vueuse.org/)                                        | 常用工具集            | 10.2.0 |
+| [Vite](https://cn.vitejs.dev//)                                      | 开发与构建工具          | 4.4.7  |
+| [Element Plus](https://element-plus.org/zh-CN/)                      | Element Plus     | 2.3.8 |
+| [TypeScript](https://www.typescriptlang.org/docs/)                   | JavaScript 的超集   | 5.1.6  |
+| [pinia](https://pinia.vuejs.org/)                                    | Vue 存储库 替代 vuex5 | 2.1.6 |
+| [vueuse](https://vueuse.org/)                                        | 常用工具集            | 10.2.1 |
 | [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化              | 9.2.2  |
-| [vue-router](https://router.vuejs.org/)                              | Vue 路由           | 4.2.1  |
-| [windicss](https://cn.windicss.org/)                                 | 下一代工具优先的 CSS 框架  | 3.5.6  |
-| [iconify](https://icon-sets.iconify.design/)                         | 在线图标库            | 3.1.0  |
+| [vue-router](https://router.vuejs.org/)                              | Vue 路由           | 4.2.4  |
+| [unocss](https://uno.antfu.me/)                                      | 原子 css          | 0.54.0  |
+| [iconify](https://icon-sets.iconify.design/)                         | 在线图标库            | 3.1.1  |
 | [wangeditor](https://www.wangeditor.com/)                            | 富文本编辑器           | 5.1.23 |
 
 ## 开发工具
@@ -56,14 +56,14 @@
 
 | 插件名                           | 功能                       |
 |-------------------------------|--------------------------|
-| TypeScript Vue Plugin (Volar) | 用于 TypeScript 的 Vue 插件   |
+| TypeScript Vue Plugin (Volar) | 用于 TypeScript 的 Vue 插件  |
 | Vue Language Features (Volar) | Vue3.0 语法支持              |
-| WindiCSS IntelliSense         | 自动完成、语法突出显示、代码折叠和构建等高级功能 |
-| Iconify IntelliSense          | Iconify 预览和搜索            |
-| i18n Ally                     | 国际化智能提示                  |
+| unocss                        | unocss for vscode           |
+| Iconify IntelliSense          | Iconify 预览和搜索           |
+| i18n Ally                     | 国际化智能提示               |
 | Stylelint                     | Css    格式化               |
-| Prettier                      | 代码格式化                    |
-| ESLint                        | 脚本代码检查                   |
+| Prettier                      | 代码格式化                   |
+| ESLint                        | 脚本代码检查                  |
 | DotENV                        | env 文件高亮                 |
 
 ## 内置功能

+ 2 - 2
build/vite/index.ts

@@ -1,7 +1,6 @@
 import { resolve } from 'path'
 import Vue from '@vitejs/plugin-vue'
 import VueJsx from '@vitejs/plugin-vue-jsx'
-import WindiCSS from 'vite-plugin-windicss'
 import progress from 'vite-plugin-progress'
 import EslintPlugin from 'vite-plugin-eslint'
 import PurgeIcons from 'vite-plugin-purge-icons'
@@ -15,6 +14,7 @@ import viteCompression from 'vite-plugin-compression'
 import topLevelAwait from 'vite-plugin-top-level-await'
 import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
 import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
+import UnoCSS from 'unocss/vite'
 
 export function createVitePlugins() {
   const root = process.cwd()
@@ -27,7 +27,7 @@ export function createVitePlugins() {
   return [
     Vue(),
     VueJsx(),
-    WindiCSS(),
+    UnoCSS(),
     progress(),
     PurgeIcons(),
     ElementPlus({}),

+ 1 - 1
build/vite/optimize.ts

@@ -8,7 +8,7 @@ const include = [
   'pinia',
   'dayjs',
   'qrcode',
-  'windicss',
+  'unocss',
   'vue-router',
   'vue-types',
   'vue-i18n',

+ 46 - 45
package.json

@@ -1,6 +1,6 @@
 {
   "name": "yudao-ui-admin-vue3",
-  "version": "1.7.3-snapshot",
+  "version": "1.8.0-snapshot",
   "description": "基于vue3、vite4、element-plus、typesScript",
   "author": "xingyu",
   "private": false,
@@ -32,12 +32,12 @@
     "@element-plus/icons-vue": "^2.1.0",
     "@form-create/designer": "^3.1.0",
     "@form-create/element-ui": "^3.1.17",
-    "@iconify/iconify": "^3.1.0",
+    "@iconify/iconify": "^3.1.1",
     "@videojs-player/vue": "^1.0.0",
-    "@vueuse/core": "^10.2.0",
+    "@vueuse/core": "^10.2.1",
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^5.1.10",
-    "@zxcvbn-ts/core": "^3.0.2",
+    "@zxcvbn-ts/core": "^3.0.3",
     "animate.css": "^4.1.1",
     "axios": "^1.4.0",
     "benz-amr-recorder": "^1.1.5",
@@ -45,79 +45,82 @@
     "camunda-bpmn-moddle": "^7.0.1",
     "cropperjs": "^1.5.13",
     "crypto-js": "^4.1.1",
-    "dayjs": "^1.11.8",
+    "dayjs": "^1.11.9",
     "diagram-js": "^11.6.0",
-    "echarts": "^5.4.2",
+    "echarts": "^5.4.3",
     "echarts-wordcloud": "^2.1.0",
-    "element-plus": "2.3.7",
-    "fast-xml-parser": "^4.2.4",
+    "element-plus": "2.3.8",
+    "fast-xml-parser": "^4.2.6",
     "highlight.js": "^11.8.0",
     "intro.js": "^7.0.1",
     "jsencrypt": "^3.3.2",
     "lodash-es": "^4.17.21",
     "min-dash": "^4.1.1",
-    "mitt": "^3.0.0",
+    "mitt": "^3.0.1",
     "nprogress": "^0.2.0",
-    "pinia": "^2.1.4",
+    "pinia": "^2.1.6",
     "qrcode": "^1.5.3",
     "qs": "^6.11.2",
     "steady-xml": "^0.1.0",
     "url": "^0.11.1",
-    "video.js": "^8.3.0",
+    "video.js": "^7.21.5",
     "vue": "3.3.4",
     "vue-dompurify-html": "^4.1.4",
     "vue-i18n": "9.2.2",
-    "vue-router": "^4.2.2",
-    "vue-types": "^5.0.4",
+    "vue-router": "^4.2.4",
+    "vue-types": "^5.1.1",
     "vuedraggable": "^4.1.0",
     "web-storage-cache": "^1.1.1",
     "xml-js": "^1.6.11"
   },
   "devDependencies": {
-    "@commitlint/cli": "^17.6.5",
-    "@commitlint/config-conventional": "^17.6.5",
-    "@iconify/json": "^2.2.80",
-    "@intlify/unplugin-vue-i18n": "^0.11.0",
+    "@commitlint/cli": "^17.6.7",
+    "@commitlint/config-conventional": "^17.6.7",
+    "@iconify/json": "^2.2.95",
+    "@intlify/unplugin-vue-i18n": "^0.12.2",
     "@purge-icons/generated": "^0.9.0",
     "@types/intro.js": "^5.1.1",
-    "@types/lodash-es": "^4.17.7",
-    "@types/node": "^20.3.1",
+    "@types/lodash-es": "^4.17.8",
+    "@types/node": "^20.4.0",
     "@types/nprogress": "^0.2.0",
-    "@types/qrcode": "^1.5.0",
+    "@types/qrcode": "^1.5.1",
     "@types/qs": "^6.9.7",
-    "@typescript-eslint/eslint-plugin": "^5.59.11",
-    "@typescript-eslint/parser": "^5.59.11",
-    "@vitejs/plugin-legacy": "^4.0.4",
+    "@typescript-eslint/eslint-plugin": "^6.2.0",
+    "@typescript-eslint/parser": "^6.2.0",
+    "@unocss/transformer-variant-group": "^0.51.4",
+    "@vitejs/plugin-legacy": "^4.1.1",
     "@vitejs/plugin-vue": "^4.2.3",
     "@vitejs/plugin-vue-jsx": "^3.0.1",
+    "@vue-macros/volar": "^0.12.3",
     "autoprefixer": "^10.4.14",
     "bpmn-js": "^8.9.0",
     "bpmn-js-properties-panel": "^0.46.0",
-    "consola": "^3.1.0",
-    "eslint": "^8.43.0",
-    "eslint-config-prettier": "^8.8.0",
+    "consola": "^3.2.3",
+    "eslint": "^8.46.0",
+    "eslint-config-prettier": "^8.9.0",
     "eslint-define-config": "^1.21.0",
-    "eslint-plugin-prettier": "^4.2.1",
-    "eslint-plugin-vue": "^9.15.0",
-    "lint-staged": "^13.2.2",
-    "postcss": "^8.4.24",
+    "eslint-plugin-prettier": "^5.0.0",
+    "eslint-plugin-vue": "^9.15.1",
+    "lint-staged": "^13.2.3",
+    "postcss": "^8.4.27",
     "postcss-html": "^1.5.0",
     "postcss-scss": "^4.0.6",
-    "prettier": "^2.8.8",
+    "prettier": "^3.0.0",
     "rimraf": "^5.0.1",
-    "rollup": "^3.25.1",
-    "sass": "^1.63.5",
-    "stylelint": "^15.8.0",
-    "stylelint-config-recommended": "^12.0.0",
-    "stylelint-config-recommended-vue": "^1.4.0",
-    "stylelint-config-standard": "^33.0.0",
+    "rollup": "^3.27.0",
+    "sass": "^1.64.1",
+    "stylelint": "^15.10.2",
+    "stylelint-config-recommended": "^13.0.0",
+    "stylelint-config-recommended-vue": "^1.5.0",
+    "stylelint-config-standard": "^34.0.0",
     "stylelint-order": "^6.0.3",
-    "terser": "^5.18.1",
-    "typescript": "5.0.4",
-    "unplugin-auto-import": "^0.16.4",
-    "unplugin-element-plus": "^0.7.1",
+    "terser": "^5.19.2",
+    "typescript": "5.1.6",
+    "unocss": "^0.54.0",
+    "unplugin-auto-import": "^0.16.6",
+    "unplugin-element-plus": "^0.7.2",
     "unplugin-vue-components": "^0.25.1",
-    "vite": "4.3.9",
+    "vite": "4.4.7",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-ejs": "^1.6.4",
     "vite-plugin-eslint": "^1.8.1",
@@ -125,9 +128,7 @@
     "vite-plugin-purge-icons": "^0.9.2",
     "vite-plugin-svg-icons": "^2.0.1",
     "vite-plugin-top-level-await": "^1.3.1",
-    "vite-plugin-windicss": "^1.9.0",
-    "vue-tsc": "^1.8.1",
-    "windicss": "^3.5.6"
+    "vue-tsc": "^1.8.8"
   },
   "license": "MIT",
   "repository": {

+ 0 - 0
preview/home.png → public/home.png


+ 0 - 5
src/api/login/index.ts

@@ -37,11 +37,6 @@ export const getInfo = () => {
   return request.get({ url: '/system/auth/get-permission-info' })
 }
 
-// 路由
-export const getAsyncRoutes = () => {
-  return request.get({ url: '/system/auth/list-menus' })
-}
-
 //获取登录验证码
 export const sendSmsCode = (data: SmsCodeVO) => {
   return request.post({ url: '/system/auth/send-sms-code', data })

+ 2 - 0
src/api/mall/promotion/combination/combinationactivity.ts

@@ -1,6 +1,8 @@
 import request from '@/config/axios'
 import { Sku, Spu } from '@/api/mall/product/spu'
 
+// TODO @puhui999: combinationActivity.ts
+
 export interface CombinationActivityVO {
   id?: number
   name?: string

+ 1 - 1
src/components/ContentDetailWrap/src/ContentDetailWrap.vue

@@ -28,7 +28,7 @@ onMounted(() => {
       <div
         :class="[
           `${prefixCls}-header`,
-          'flex border-bottom-1 h-50px items-center text-center pr-10px'
+          'flex b-b-1 h-50px items-center text-center bg-white pr-10px'
         ]"
       >
         <div :class="[`${prefixCls}-header__back`, 'flex pl-10px pr-10px ']">

+ 3 - 1
src/components/Cropper/src/CopperModal.vue

@@ -222,7 +222,9 @@ $prefix-cls: #{$namespace}-cropper-am;
         transparent 75%,
         rgb(0 0 0 / 25%) 0
       );
-    background-position: 0 0, 12px 12px;
+    background-position:
+      0 0,
+      12px 12px;
     background-size: 24px 24px;
   }
 

+ 2 - 2
src/components/Descriptions/src/Descriptions.vue

@@ -71,14 +71,14 @@ const toggleClick = () => {
   <div
     :class="[
       prefixCls,
-      'bg-[var(--el-color-white)] dark:(bg-[var(--el-bg-color)] border-[var(--el-border-color)] border-1px)'
+      'bg-[var(--el-color-white)] dark:bg-[var(--el-bg-color)] dark:border-[var(--el-border-color)] dark:border-1px'
     ]"
   >
     <div
       v-if="title"
       :class="[
         `${prefixCls}-header`,
-        'h-50px flex justify-between items-center border-bottom-1 border-solid border-[var(--tags-view-border-color)] px-10px cursor-pointer dark:border-[var(--el-border-color)]'
+        'h-50px flex justify-between items-center b-b-1 border-solid border-[var(--el-border-color)] px-10px cursor-pointer dark:border-[var(--el-border-color)]'
       ]"
       @click="toggleClick"
     >

+ 12 - 16
src/components/Dialog/src/Dialog.vue

@@ -99,13 +99,19 @@ const dialogStyle = computed(() => {
 </template>
 
 <style lang="scss">
-.#{$elNamespace}-dialog__header {
-  margin-right: 0 !important;
-  border-bottom: 1px solid var(--tags-view-border-color);
-}
+.#{$elNamespace}-dialog {
+  &__header {
+    margin-right: 0 !important;
+    border-bottom: 1px solid var(--el-border-color);
+  }
 
-.#{$elNamespace}-dialog__footer {
-  border-top: 1px solid var(--tags-view-border-color);
+  &__body {
+    padding: 0 !important;
+  }
+
+  &__footer {
+    border-top: 1px solid var(--el-border-color);
+  }
 }
 
 .is-hover {
@@ -113,14 +119,4 @@ const dialogStyle = computed(() => {
     color: var(--el-color-primary) !important;
   }
 }
-
-.dark {
-  .#{$elNamespace}-dialog__header {
-    border-bottom: 1px solid var(--el-border-color);
-  }
-
-  .#{$elNamespace}-dialog__footer {
-    border-top: 1px solid var(--el-border-color);
-  }
-}
 </style>

+ 2 - 3
src/components/Editor/src/Editor.vue

@@ -164,7 +164,6 @@ const handleChange = (editor: IDomEditor) => {
 // 组件销毁时,及时销毁编辑器
 onBeforeUnmount(() => {
   const editor = unref(editorRef.value)
-  if (editor === null) return
 
   // 销毁,并移除 editor
   editor?.destroy()
@@ -181,12 +180,12 @@ defineExpose({
 </script>
 
 <template>
-  <div class="border-1 border-solid border-[var(--tags-view-border-color)] z-99">
+  <div class="border-1 border-solid border-[var(--el-border-color)] z-99">
     <!-- 工具栏 -->
     <Toolbar
       :editor="editorRef"
       :editorId="editorId"
-      class="border-bottom-1 border-solid border-[var(--tags-view-border-color)]"
+      class="border-0 b-b-1 border-solid border-[var(--el-border-color)]"
     />
     <!-- 编辑器 -->
     <Editor

+ 3 - 1
src/components/InputPassword/src/InputPassword.vue

@@ -114,7 +114,9 @@ $prefix-cls: #{$namespace}-input-password;
       height: inherit;
       background-color: transparent;
       border-radius: inherit;
-      transition: width 0.5s ease-in-out, background 0.25s;
+      transition:
+        width 0.5s ease-in-out,
+        background 0.25s;
 
       &[data-score='0'] {
         width: 20%;

+ 21 - 21
src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue

@@ -109,22 +109,22 @@ const elementBusinessObject = ref<any>({}) // 元素 businessObject 镜像,提
 const conditionFormVisible = ref(false) // 流转条件设置
 const formVisible = ref(false) // 表单配置
 const bpmnElement = ref()
-const timer = ref()
+
 provide('prefix', props.prefix)
 provide('width', props.width)
 const bpmnInstances = () => (window as any)?.bpmnInstances
-const initModels = () => {
-  // console.log(props, 'props')
-  // console.log(props.bpmnModeler, 'sakdjjaskdsajdkasdjkadsjk')
-  // 初始化 modeler 以及其他 moddle
-  // nextTick(() => {
-  if (!props.bpmnModeler) {
+
+// 监听 props.bpmnModeler 然后 initModels
+const unwatchBpmn = watch(
+  () => props.bpmnModeler,
+  () => {
     // 避免加载时 流程图 并未加载完成
-    timer.value = setTimeout(() => initModels(), 10)
-    return
-  }
-  if (timer.value) {
-    clearTimeout(timer.value)
+    if (!props.bpmnModeler) {
+      console.log('缺少props.bpmnModeler')
+      return
+    }
+
+    console.log('props.bpmnModeler 有值了!!!')
     const w = window as any
     w.bpmnInstances = {
       modeler: props.bpmnModeler,
@@ -137,12 +137,16 @@ const initModels = () => {
       replace: props.bpmnModeler.get('replace'),
       selection: props.bpmnModeler.get('selection')
     }
+
+    console.log(bpmnInstances(), 'window.bpmnInstances')
+    getActiveElement()
+    unwatchBpmn()
+  },
+  {
+    immediate: true
   }
+)
 
-  console.log(bpmnInstances(), 'window.bpmnInstances')
-  getActiveElement()
-  // })
-}
 const getActiveElement = () => {
   // 初始第一个选中元素 bpmn:Process
   initFormOnChanged(null)
@@ -190,11 +194,7 @@ const initFormOnChanged = (element) => {
   )
   formVisible.value = elementType.value === 'UserTask' || elementType.value === 'StartEvent'
 }
-onMounted(() => {
-  setTimeout(() => {
-    initModels()
-  }, 100)
-})
+
 onBeforeUnmount(() => {
   const w = window as any
   w.bpmnInstances = null

+ 1 - 3
src/hooks/web/useTable.ts

@@ -136,9 +136,7 @@ export const useTable = <T = any>(config?: UseTableConfig<T>) => {
       })
       if (res) {
         tableObject.tableList = (res as unknown as ResponseType).list
-        if ((res as unknown as ResponseType).total) {
-          tableObject.total = (res as unknown as ResponseType).total as unknown as number
-        }
+        tableObject.total = (res as unknown as ResponseType).total ?? 0
       }
     },
     setProps: async (props: TableProps = {}) => {

+ 17 - 10
src/layout/components/AppView.vue

@@ -18,26 +18,33 @@ const tagsViewStore = useTagsViewStore()
 const getCaches = computed((): string[] => {
   return tagsViewStore.getCachedViews
 })
+
+const tagsView = computed(() => appStore.getTagsView)
 </script>
 
 <template>
   <section
     :class="[
-      'p-[var(--app-content-padding)] w-[100%] bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]',
+      'p-[var(--app-content-padding)] w-[calc(100%-var(--app-content-padding)-var(--app-content-padding))] bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]',
       {
-        '!min-h-[calc(100%-var(--app-footer-height))]':
-          ((fixedHeader && (layout === 'classic' || layout === 'topLeft')) || layout === 'top') &&
-          footer,
-
-        '!min-h-[calc(100%-var(--tags-view-height)-var(--top-tool-height)-var(--app-footer-height))]':
+        '!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
+          (fixedHeader &&
+            (layout === 'classic' || layout === 'topLeft' || layout === 'top') &&
+            footer) ||
+          (!tagsView && layout === 'top' && footer),
+        '!min-h-[calc(100%-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height)-var(--tags-view-height))]':
+          tagsView && layout === 'top' && footer,
+
+        '!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--top-tool-height)-var(--app-footer-height))]':
           !fixedHeader && layout === 'classic' && footer,
 
-        '!min-h-[calc(100%-var(--tags-view-height)-var(--app-footer-height))]':
-          !fixedHeader && (layout === 'topLeft' || layout === 'top') && footer,
+        '!min-h-[calc(100%-var(--tags-view-height)-var(--app-content-padding)-var(--app-content-padding)-var(--app-footer-height))]':
+          !fixedHeader && layout === 'topLeft' && footer,
 
-        '!min-h-[calc(100%-var(--top-tool-height))]': fixedHeader && layout === 'cutMenu' && footer,
+        '!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding))]':
+          fixedHeader && layout === 'cutMenu' && footer,
 
-        '!min-h-[calc(100%-var(--top-tool-height)-var(--tags-view-height))]':
+        '!min-h-[calc(100%-var(--top-tool-height)-var(--app-content-padding)-var(--app-content-padding)-var(--tags-view-height))]':
           !fixedHeader && layout === 'cutMenu' && footer
       }
     ]"

+ 1 - 1
src/layout/components/LocaleDropdown/src/LocaleDropdown.vue

@@ -38,7 +38,7 @@ const setLang = (lang: LocaleType) => {
       :class="$attrs.class"
       :color="color"
       :size="18"
-      class="cursor-pointer"
+      class="cursor-pointer !p-0"
       icon="ion:language-sharp"
     />
     <template #dropdown>

+ 1 - 2
src/layout/components/Logo/src/Logo.vue

@@ -62,8 +62,7 @@ watch(
       :class="[
         prefixCls,
         layout !== 'classic' ? `${prefixCls}__Top` : '',
-        'flex !h-[var(--logo-height)] items-center cursor-pointer justify-center relative',
-        'dark:bg-[var(--el-bg-color)]'
+        'flex !h-[var(--logo-height)] items-center cursor-pointer pl-8px relative decoration-none overflow-hidden'
       ]"
       to="/"
     >

+ 0 - 9
src/layout/components/Menu/src/Menu.vue

@@ -138,15 +138,6 @@ $prefix-cls: #{$namespace}-menu;
   position: relative;
   transition: width var(--transition-time-02);
 
-  &:after {
-    position: absolute;
-    top: 0;
-    right: 0;
-    height: 100%;
-    border-left: 1px solid var(--left-menu-border-color);
-    content: '';
-  }
-
   :deep(.#{$elNamespace}-menu) {
     width: 100% !important;
     border-right: none;

+ 6 - 3
src/layout/components/Message/src/Message.vue

@@ -35,9 +35,12 @@ onMounted(() => {
   // 首次加载小红点
   getUnreadCount()
   // 轮询刷新小红点
-  setInterval(() => {
-    getUnreadCount()
-  }, 1000 * 60 * 2)
+  setInterval(
+    () => {
+      getUnreadCount()
+    },
+    1000 * 60 * 2
+  )
 })
 </script>
 <template>

+ 0 - 1
src/layout/components/Setting/src/Setting.vue

@@ -44,7 +44,6 @@ const setHeaderTheme = (color: string) => {
   setCssVar('--top-header-bg-color', color)
   setCssVar('--top-header-text-color', textColor)
   setCssVar('--top-header-hover-color', textHoverColor)
-  setCssVar('--top-tool-border-color', topToolBorderColor)
   appStore.setTheme({
     topHeaderBgColor: color,
     topHeaderTextColor: textColor,

+ 2 - 13
src/layout/components/TabMenu/src/TabMenu.vue

@@ -139,7 +139,7 @@ export default defineComponent({
         id={`${variables.namespace}-menu`}
         class={[
           prefixCls,
-          'relative bg-[var(--left-menu-bg-color)] top-1px z-3000',
+          'relative bg-[var(--left-menu-bg-color)] top-1px z-3000 layout-border__right',
           {
             'w-[var(--tab-menu-max-width)]': !unref(collapse),
             'w-[var(--tab-menu-min-width)]': unref(collapse)
@@ -195,7 +195,7 @@ export default defineComponent({
         </div>
         <Menu
           class={[
-            '!absolute top-0 border-left-1 border-solid border-[var(--left-menu-bg-light-color)]',
+            '!absolute top-0',
             {
               '!left-[var(--tab-menu-min-width)]': unref(collapse),
               '!left-[var(--tab-menu-max-width)]': !unref(collapse),
@@ -217,16 +217,6 @@ $prefix-cls: #{$namespace}-tab-menu;
 .#{$prefix-cls} {
   transition: all var(--transition-time-02);
 
-  &::after {
-    position: absolute;
-    top: 0;
-    right: 0;
-    width: 1px;
-    height: 100%;
-    border-left: 1px solid var(--left-menu-border-color);
-    content: '';
-  }
-
   &__item {
     color: var(--left-menu-text-color);
     transition: all var(--transition-time-02);
@@ -240,7 +230,6 @@ $prefix-cls: #{$namespace}-tab-menu;
   &--collapse {
     color: var(--left-menu-text-color);
     background-color: var(--left-menu-bg-light-color);
-    border-top: 1px solid var(--left-menu-border-color);
   }
 
   .is-active {

+ 58 - 62
src/layout/components/TagsView/src/TagsView.vue

@@ -1,17 +1,17 @@
 <script lang="ts" setup>
+import { onMounted, watch, computed, unref, ref, nextTick } from 'vue'
+import { useRouter } from 'vue-router'
 import type { RouteLocationNormalizedLoaded, RouterLinkProps } from 'vue-router'
 import { usePermissionStore } from '@/store/modules/permission'
 import { useTagsViewStore } from '@/store/modules/tagsView'
 import { useAppStore } from '@/store/modules/app'
-
+import { useI18n } from '@/hooks/web/useI18n'
 import { filterAffixTags } from './helper'
 import { ContextMenu, ContextMenuExpose } from '@/layout/components/ContextMenu'
 import { useDesign } from '@/hooks/web/useDesign'
+import { useTemplateRefsList } from '@vueuse/core'
 import { ElScrollbar } from 'element-plus'
 import { useScrollTo } from '@/hooks/event/useScrollTo'
-import { useTemplateRefsList } from '@vueuse/core'
-
-defineOptions({ name: 'TagsView' })
 
 const { getPrefixCls } = useDesign()
 
@@ -35,6 +35,8 @@ const appStore = useAppStore()
 
 const tagsViewIcon = computed(() => appStore.getTagsViewIcon)
 
+const isDark = computed(() => appStore.getIsDark)
+
 // 初始化tag
 const initTags = () => {
   affixTagArr.value = filterAffixTags(unref(routers))
@@ -73,7 +75,7 @@ const closeAllTags = () => {
   toLastView()
 }
 
-// 关闭其
+// 关闭其
 const closeOthersTags = () => {
   tagsViewStore.delOthersViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
 }
@@ -128,6 +130,7 @@ const moveToCurrentTag = async () => {
       if (v.fullPath !== unref(currentRoute).fullPath) {
         tagsViewStore.updateVisitedView(unref(currentRoute))
       }
+
       break
     }
   }
@@ -263,29 +266,21 @@ watch(
     class="flex w-full relative bg-[#fff] dark:bg-[var(--el-bg-color)]"
   >
     <span
-      :class="`${prefixCls}__tool`"
-      class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] cursor-pointer"
+      :class="`${prefixCls}__tool ${prefixCls}__tool--first`"
+      class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] flex items-center justify-center cursor-pointer"
       @click="move(-200)"
     >
       <Icon
-        :color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
         icon="ep:d-arrow-left"
+        color="var(--el-text-color-placeholder)"
+        :hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
       />
     </span>
     <div class="overflow-hidden flex-1">
       <ElScrollbar ref="scrollbarRef" class="h-full" @scroll="scroll">
         <div class="flex h-full">
           <ContextMenu
-            v-for="item in visitedViews"
-            :key="item.fullPath"
             :ref="itemRefs.set"
-            :class="[
-              `${prefixCls}__item`,
-              item?.meta?.affix ? `${prefixCls}__item--affix` : '',
-              {
-                'is-active': isActive(item)
-              }
-            ]"
             :schema="[
               {
                 icon: 'ep:refresh',
@@ -343,14 +338,23 @@ watch(
                 }
               }
             ]"
+            v-for="item in visitedViews"
+            :key="item.fullPath"
             :tag-item="item"
+            :class="[
+              `${prefixCls}__item`,
+              item?.meta?.affix ? `${prefixCls}__item--affix` : '',
+              {
+                'is-active': isActive(item)
+              }
+            ]"
             @visible-change="visibleChange"
           >
             <div>
-              <router-link :ref="tagLinksRefs.set" v-slot="{ navigate }" :to="{ ...item }" custom>
+              <router-link :ref="tagLinksRefs.set" :to="{ ...item }" custom v-slot="{ navigate }">
                 <div
-                  class="h-full flex justify-center items-center whitespace-nowrap pl-15px"
                   @click="navigate"
+                  class="h-full flex justify-center items-center whitespace-nowrap pl-15px"
                 >
                   <Icon
                     v-if="
@@ -366,9 +370,9 @@ watch(
                   {{ t(item?.meta?.title as string) }}
                   <Icon
                     :class="`${prefixCls}__item--close`"
-                    :size="12"
                     color="#333"
                     icon="ep:close"
+                    :size="12"
                     @click.prevent.stop="closeSelectedTag(item)"
                   />
                 </div>
@@ -380,25 +384,28 @@ watch(
     </div>
     <span
       :class="`${prefixCls}__tool`"
-      class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] text-center leading-[var(--tags-view-height)] cursor-pointer"
+      class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] flex items-center justify-center cursor-pointer"
       @click="move(200)"
     >
       <Icon
-        :color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
         icon="ep:d-arrow-right"
+        color="var(--el-text-color-placeholder)"
+        :hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
       />
     </span>
     <span
       :class="`${prefixCls}__tool`"
-      class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] text-center leading-[var(--tags-view-height)] cursor-pointer"
+      class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] flex items-center justify-center cursor-pointer"
       @click="refreshSelectedTag(selectedTag)"
     >
       <Icon
-        :color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
         icon="ep:refresh-right"
+        color="var(--el-text-color-placeholder)"
+        :hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
       />
     </span>
     <ContextMenu
+      trigger="click"
       :schema="[
         {
           icon: 'ep:refresh',
@@ -410,7 +417,7 @@ watch(
         {
           icon: 'ep:close',
           label: t('common.closeTab'),
-           disabled: !!visitedViews?.length && selectedTag?.meta.affix,
+          disabled: !!visitedViews?.length && selectedTag?.meta.affix,
           command: () => {
             closeSelectedTag(selectedTag!)
           }
@@ -450,15 +457,15 @@ watch(
           }
         }
       ]"
-      trigger="click"
     >
       <span
         :class="`${prefixCls}__tool`"
-        class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] text-center leading-[var(--tags-view-height)] cursor-pointer block"
+        class="w-[var(--tags-view-height)] h-[var(--tags-view-height)] flex items-center justify-center cursor-pointer block"
       >
         <Icon
-          :color="appStore.getIsDark ? 'var(--el-text-color-regular)' : '#333'"
           icon="ep:menu"
+          color="var(--el-text-color-placeholder)"
+          :hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
         />
       </span>
     </ContextMenu>
@@ -475,47 +482,49 @@ $prefix-cls: #{$namespace}-tags-view;
 
   &__tool {
     position: relative;
-    display: inline-flex;
-    align-items: center;
-    justify-content: center;
-
-    &:hover {
-      :deep(span) {
-        color: var(--el-color-black) !important;
-      }
-    }
 
-    &::after {
+    &::before {
       position: absolute;
       top: 1px;
       left: 0;
       width: 100%;
       height: calc(100% - 1px);
-      border-right: 1px solid var(--tags-view-border-color);
-      border-left: 1px solid var(--tags-view-border-color);
+      border-left: 1px solid var(--el-border-color);
       content: '';
     }
+
+    &--first {
+      &::before {
+        position: absolute;
+        top: 1px;
+        left: 0;
+        width: 100%;
+        height: calc(100% - 1px);
+        border-right: 1px solid var(--el-border-color);
+        border-left: none;
+        content: '';
+      }
+    }
   }
 
   &__item {
     position: relative;
     top: 2px;
-    height: calc(100% - 4px);
-    padding-right: 16px;
+    height: calc(100% - 6px);
+    padding-right: 25px;
     margin-left: 4px;
     font-size: 12px;
-    border-radius: 3px;
     cursor: pointer;
     border: 1px solid #d9d9d9;
+    border-radius: 2px;
 
     &--close {
       position: absolute;
       top: 50%;
-      right: 3px;
+      right: 5px;
       display: none;
       transform: translate(0, -50%);
     }
-
     &:not(.#{$prefix-cls}__item--affix):hover {
       .#{$prefix-cls}__item--close {
         display: block;
@@ -533,7 +542,6 @@ $prefix-cls: #{$namespace}-tags-view;
     color: var(--el-color-white);
     background-color: var(--el-color-primary);
     border: 1px solid var(--el-color-primary);
-
     .#{$prefix-cls}__item--close {
       :deep(span) {
         color: var(--el-color-white) !important;
@@ -545,26 +553,14 @@ $prefix-cls: #{$namespace}-tags-view;
 .dark {
   .#{$prefix-cls} {
     &__tool {
-      &:hover {
-        :deep(span) {
-          color: #fff !important;
+      &--first {
+        &::after {
+          display: none;
         }
       }
-
-      &::after {
-        border-right: 1px solid var(--el-border-color);
-        border-left: 1px solid var(--el-border-color);
-      }
     }
 
     &__item {
-      position: relative;
-      top: 2px;
-      height: calc(100% - 4px);
-      padding-right: 16px;
-      font-size: 12px;
-      border-radius: 3px;
-      cursor: pointer;
       border: 1px solid var(--el-border-color);
     }
 
@@ -577,7 +573,7 @@ $prefix-cls: #{$namespace}-tags-view;
     &__item.is-active {
       color: var(--el-color-white);
       background-color: var(--el-color-primary);
-
+      border: 1px solid var(--el-color-primary);
       .#{$prefix-cls}__item--close {
         :deep(span) {
           color: var(--el-color-white) !important;

+ 7 - 7
src/layout/components/ToolHeader.vue

@@ -52,28 +52,28 @@ export default defineComponent({
         {layout.value !== 'top' ? (
           <div class="h-full flex items-center">
             {hamburger.value && layout.value !== 'cutMenu' ? (
-              <Collapse class="hover-trigger" color="var(--top-header-text-color)"></Collapse>
+              <Collapse class="custom-hover" color="var(--top-header-text-color)"></Collapse>
             ) : undefined}
-            {breadcrumb.value ? <Breadcrumb class="<md:hidden"></Breadcrumb> : undefined}
+            {breadcrumb.value ? <Breadcrumb class="lt-md:hidden"></Breadcrumb> : undefined}
           </div>
         ) : undefined}
         <div class="h-full flex items-center">
           {screenfull.value ? (
-            <Screenfull class="hover-trigger" color="var(--top-header-text-color)"></Screenfull>
+            <Screenfull class="custom-hover" color="var(--top-header-text-color)"></Screenfull>
           ) : undefined}
           {size.value ? (
-            <SizeDropdown class="hover-trigger" color="var(--top-header-text-color)"></SizeDropdown>
+            <SizeDropdown class="custom-hover" color="var(--top-header-text-color)"></SizeDropdown>
           ) : undefined}
           {locale.value ? (
             <LocaleDropdown
-              class="hover-trigger"
+              class="custom-hover"
               color="var(--top-header-text-color)"
             ></LocaleDropdown>
           ) : undefined}
           {message.value ? (
-            <Message class="hover-trigger" color="var(--top-header-text-color)"></Message>
+            <Message class="custom-hover" color="var(--top-header-text-color)"></Message>
           ) : undefined}
-          <UserInfo class="hover-trigger"></UserInfo>
+          <UserInfo></UserInfo>
         </div>
       </div>
     )

+ 1 - 1
src/layout/components/UserInfo/src/UserInfo.vue

@@ -51,7 +51,7 @@ const toDocument = () => {
 </script>
 
 <template>
-  <ElDropdown :class="prefixCls" trigger="click">
+  <ElDropdown class="custom-hover" :class="prefixCls" trigger="click">
     <div class="flex items-center">
       <img :src="avatar" alt="" class="w-[calc(var(--logo-height)-25px)] rounded-[50%]" />
       <span class="<lg:hidden text-14px pl-[5px] text-[var(--top-header-text-color)]">

+ 54 - 26
src/layout/components/useRenderLayout.tsx

@@ -39,11 +39,16 @@ export const useRenderLayout = () => {
   const renderClassic = () => {
     return (
       <>
-        <div class={['absolute top-0 left-0 h-full', { '!fixed z-3000': mobile.value }]}>
+        <div
+          class={[
+            'absolute top-0 left-0 h-full layout-border__right',
+            { '!fixed z-3000': mobile.value }
+          ]}
+        >
           {logo.value ? (
             <Logo
               class={[
-                'bg-[var(--left-menu-bg-color)] border-bottom-1 border-solid border-[var(--logo-border-color)] dark:border-[var(--el-border-color)]',
+                'bg-[var(--left-menu-bg-color)] relative',
                 {
                   '!pl-0': mobile.value && collapse.value,
                   'w-[var(--left-menu-min-width)]': appStore.getCollapse,
@@ -83,19 +88,26 @@ export const useRenderLayout = () => {
               class={[
                 {
                   'fixed top-0 left-0 z-10': fixedHeader.value,
-                  'w-[calc(100%-var(--left-menu-min-width))] left-[var(--left-menu-min-width)]':
+                  'w-[calc(100%-var(--left-menu-min-width))] !left-[var(--left-menu-min-width)]':
                     collapse.value && fixedHeader.value && !mobile.value,
-                  'w-[calc(100%-var(--left-menu-max-width))] left-[var(--left-menu-max-width)]':
+                  'w-[calc(100%-var(--left-menu-max-width))] !left-[var(--left-menu-max-width)]':
                     !collapse.value && fixedHeader.value && !mobile.value,
                   '!w-full !left-0': mobile.value
                 }
               ]}
               style="transition: all var(--transition-time-02);"
             >
-              <ToolHeader class="border-bottom-1 border-solid border-[var(--top-tool-border-color)] bg-[var(--top-header-bg-color)] dark:border-[var(--el-border-color)]"></ToolHeader>
+              <ToolHeader
+                class={[
+                  'bg-[var(--top-header-bg-color)]',
+                  {
+                    'layout-border__bottom': !tagsView.value
+                  }
+                ]}
+              ></ToolHeader>
 
               {tagsView.value ? (
-                <TagsView class="border-bottom-1 border-top-1 border-solid border-[var(--tags-view-border-color)] dark:border-[var(--el-border-color)]"></TagsView>
+                <TagsView class="layout-border__bottom layout-border__top"></TagsView>
               ) : undefined}
             </div>
 
@@ -109,13 +121,13 @@ export const useRenderLayout = () => {
   const renderTopLeft = () => {
     return (
       <>
-        <div class="flex items-center bg-[var(--top-header-bg-color)] border-bottom-1 border-solid border-[var(--top-tool-border-color)] dark:border-[var(--el-border-color)]">
-          {logo.value ? <Logo class="hover-trigger !pr-15px"></Logo> : undefined}
+        <div class="flex items-center bg-[var(--top-header-bg-color)] relative layout-border__bottom dark:bg-[var(--el-bg-color)]">
+          {logo.value ? <Logo class="custom-hover"></Logo> : undefined}
 
           <ToolHeader class="flex-1"></ToolHeader>
         </div>
         <div class="absolute top-[var(--logo-height)+1px] left-0 w-full h-[calc(100%-1px-var(--logo-height))] flex">
-          <Menu class="!h-full"></Menu>
+          <Menu class="!h-full relative layout-border__right"></Menu>
           <div
             class={[
               `${prefixCls}-content`,
@@ -142,12 +154,12 @@ export const useRenderLayout = () => {
               {tagsView.value ? (
                 <TagsView
                   class={[
-                    'border-bottom-1 border-top-1 border-solid border-[var(--tags-view-border-color)] dark:border-[var(--el-border-color)]',
+                    'layout-border__bottom absolute',
                     {
                       '!fixed top-0 left-0 z-10': fixedHeader.value,
-                      'w-[calc(100%-var(--left-menu-min-width))] left-[var(--left-menu-min-width)] mt-[var(--logo-height)]':
+                      'w-[calc(100%-var(--left-menu-min-width))] !left-[var(--left-menu-min-width)] mt-[calc(var(--logo-height)+1px)]':
                         collapse.value && fixedHeader.value,
-                      'w-[calc(100%-var(--left-menu-max-width))] left-[var(--left-menu-max-width)] mt-[var(--logo-height)]':
+                      'w-[calc(100%-var(--left-menu-max-width))] !left-[var(--left-menu-max-width)] mt-[calc(var(--logo-height)+1px)]':
                         !collapse.value && fixedHeader.value
                     }
                   ]}
@@ -166,12 +178,28 @@ export const useRenderLayout = () => {
   const renderTop = () => {
     return (
       <>
-        <div class="flex items-center justify-between bg-[var(--top-header-bg-color)] border-bottom-1 border-solid border-[var(--top-tool-border-color)] dark:border-[var(--el-border-color)]">
-          {logo.value ? <Logo class="hover-trigger"></Logo> : undefined}
+        <div
+          class={[
+            'flex items-center justify-between bg-[var(--top-header-bg-color)] relative',
+            {
+              'layout-border__bottom': !tagsView.value
+            }
+          ]}
+        >
+          {logo.value ? <Logo class="custom-hover"></Logo> : undefined}
           <Menu class="flex-1 px-10px h-[var(--top-tool-height)]"></Menu>
           <ToolHeader></ToolHeader>
         </div>
-        <div class={[`${prefixCls}-content`, 'h-full w-full']}>
+        <div
+          class={[
+            `${prefixCls}-content`,
+            'w-full',
+            {
+              'h-[calc(100%-var(--app-footer-height))]': !fixedHeader.value,
+              'h-[calc(100%-var(--tags-view-height)-var(--app-footer-height))]': fixedHeader.value
+            }
+          ]}
+        >
           <ElScrollbar
             v-loading={pageLoading.value}
             class={[
@@ -186,9 +214,9 @@ export const useRenderLayout = () => {
             {tagsView.value ? (
               <TagsView
                 class={[
-                  'border-bottom-1 border-top-1 border-solid border-[var(--tags-view-border-color)] dark:border-[var(--el-border-color)]',
+                  'layout-border__bottom layout-border__top relative',
                   {
-                    '!fixed w-full top-[var(--top-tool-height)] left-0': fixedHeader.value
+                    '!fixed w-full top-[calc(var(--top-tool-height)+1px)] left-0': fixedHeader.value
                   }
                 ]}
                 style="transition: width var(--transition-time-02), left var(--transition-time-02);"
@@ -205,12 +233,12 @@ export const useRenderLayout = () => {
   const renderCutMenu = () => {
     return (
       <>
-        <div class="flex items-center bg-[var(--top-header-bg-color)] border-bottom-1 border-solid border-[var(--top-tool-border-color)] dark:border-[var(--el-border-color)]">
-          {logo.value ? <Logo class="hover-trigger !pr-15px"></Logo> : undefined}
+        <div class="flex items-center bg-[var(--top-header-bg-color)] relative layout-border__bottom">
+          {logo.value ? <Logo class="custom-hover !pr-15px"></Logo> : undefined}
 
           <ToolHeader class="flex-1"></ToolHeader>
         </div>
-        <div class="absolute top-[var(--logo-height)] left-0 w-full h-[calc(100%-var(--logo-height))] flex">
+        <div class="absolute top-[var(--logo-height)] left-0 w-[calc(100%-2px)] h-[calc(100%-var(--logo-height))] flex">
           <TabMenu></TabMenu>
           <div
             class={[
@@ -242,18 +270,18 @@ export const useRenderLayout = () => {
               {tagsView.value ? (
                 <TagsView
                   class={[
-                    'border-bottom-1 border-top-1 border-solid border-[var(--tags-view-border-color)] dark:border-[var(--el-border-color)]',
+                    'relative layout-border__bottom layout-border__top',
                     {
                       '!fixed top-0 left-0 z-10': fixedHeader.value,
-                      'w-[calc(100%-var(--tab-menu-min-width))] left-[var(--tab-menu-min-width)] mt-[var(--logo-height)]':
+                      'w-[calc(100%-var(--tab-menu-min-width))] !left-[var(--tab-menu-min-width)] mt-[var(--logo-height)]':
                         collapse.value && fixedHeader.value,
-                      'w-[calc(100%-var(--tab-menu-max-width))] left-[var(--tab-menu-max-width)] mt-[var(--logo-height)]':
+                      'w-[calc(100%-var(--tab-menu-max-width))] !left-[var(--tab-menu-max-width)] mt-[var(--logo-height)]':
                         !collapse.value && fixedHeader.value,
-                      '!fixed top-0 left-[var(--tab-menu-min-width)+var(--left-menu-max-width)] z-10':
+                      '!fixed top-0 !left-[var(--tab-menu-min-width)+var(--left-menu-max-width)] z-10':
                         fixedHeader.value && fixedMenu.value,
-                      'w-[calc(100%-var(--tab-menu-min-width)-var(--left-menu-max-width))] left-[var(--tab-menu-min-width)+var(--left-menu-max-width)] mt-[var(--logo-height)]':
+                      'w-[calc(100%-var(--tab-menu-min-width)-var(--left-menu-max-width))] !left-[var(--tab-menu-min-width)+var(--left-menu-max-width)] mt-[var(--logo-height)]':
                         collapse.value && fixedHeader.value && fixedMenu.value,
-                      'w-[calc(100%-var(--tab-menu-max-width)-var(--left-menu-max-width))] left-[var(--tab-menu-max-width)+var(--left-menu-max-width)] mt-[var(--logo-height)]':
+                      'w-[calc(100%-var(--tab-menu-max-width)-var(--left-menu-max-width))] !left-[var(--tab-menu-max-width)+var(--left-menu-max-width)] mt-[var(--logo-height)]':
                         !collapse.value && fixedHeader.value && fixedMenu.value
                     }
                   ]}

+ 2 - 2
src/main.ts

@@ -1,5 +1,5 @@
-// 引入windi css
-import '@/plugins/windi.css'
+// 引入unocss css
+import '@/plugins/unocss'
 
 // 导入全局的svg图标
 import '@/plugins/svgIcon'

+ 1 - 0
src/plugins/unocss/index.ts

@@ -0,0 +1 @@
+import 'virtual:uno.css'

+ 0 - 3
src/plugins/windi.css/index.ts

@@ -1,3 +0,0 @@
-import 'virtual:windi.css'
-
-import 'virtual:windi-devtools'

+ 1 - 4
src/store/modules/permission.ts

@@ -3,7 +3,6 @@ import { store } from '../index'
 import { cloneDeep } from 'lodash-es'
 import remainingRouter from '@/router/modules/remaining'
 import { generateRoute, flatMultiLevelRoutes } from '@/utils/routerHelper'
-import { getAsyncRoutes } from '@/api/login'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 
 const { wsCache } = useCache()
@@ -34,12 +33,10 @@ export const usePermissionStore = defineStore('permission', {
   actions: {
     async generateRoutes(): Promise<unknown> {
       return new Promise<void>(async (resolve) => {
+        // 获得菜单列表,它在登录的时候,setUserInfoAction 方法中已经进行获取
         let res: AppCustomRouteRecordRaw[]
         if (wsCache.get(CACHE_KEY.ROLE_ROUTERS)) {
           res = wsCache.get(CACHE_KEY.ROLE_ROUTERS) as AppCustomRouteRecordRaw[]
-        } else {
-          res = await getAsyncRoutes()
-          wsCache.set(CACHE_KEY.ROLE_ROUTERS, res)
         }
         const routerMap: AppRouteRecordRaw[] = generateRoute(res as AppCustomRouteRecordRaw[])
         // 动态路由,404一定要放到最后面

+ 1 - 0
src/store/modules/user.ts

@@ -58,6 +58,7 @@ export const useUserStore = defineStore('admin-user', {
       this.user = userInfo.user
       this.isSetUser = true
       wsCache.set(CACHE_KEY.USER, userInfo)
+      wsCache.set(CACHE_KEY.ROLE_ROUTERS, userInfo.menus)
     },
     async loginOut() {
       await loginOut()

+ 3 - 1
src/styles/index.scss

@@ -23,7 +23,9 @@
   }
 
   & .peg {
-    box-shadow: 0 0 10px var(--el-color-primary), 0 0 5px var(--el-color-primary) !important;
+    box-shadow:
+      0 0 10px var(--el-color-primary),
+      0 0 5px var(--el-color-primary) !important;
   }
 
   & .spinner-icon {

+ 5 - 12
src/styles/var.css

@@ -1,8 +1,5 @@
 :root {
-  --dark-bg-color: #293146;
-
-  /* left menu start */
-  --left-menu-border-color: '#eee';
+  --login-bg-color: #293146;
 
   --left-menu-max-width: 200px;
 
@@ -25,8 +22,6 @@
   --logo-height: 50px;
 
   --logo-title-text-color: #fff;
-
-  --logo-border-color: 'inherit';
   /* logo end */
 
   /* header start */
@@ -40,11 +35,7 @@
 
   --top-tool-p-x: 0;
 
-  --top-tool-border-color: #eee;
-
   --tags-view-height: 35px;
-
-  --tags-view-border-color: #eee;
   /* header start */
 
   /* tab menu start */
@@ -53,8 +44,6 @@
   --tab-menu-min-width: 30px;
 
   --tab-menu-collapse-height: 36px;
-
-  --tab-menu-border-color: #eee;
   /* tab menu end */
 
   --app-content-padding: 20px;
@@ -66,6 +55,10 @@
   --transition-time-02: 0.2s;
 }
 
+.dark {
+  --app-content-bg-color: var(--el-bg-color);
+}
+
 html,
 body {
   -webkit-font-smoothing: antialiased;

+ 2 - 2
src/views/Home/Index.vue

@@ -7,7 +7,7 @@
             <div class="flex items-center">
               <img :src="avatar" alt="" class="w-70px h-70px rounded-[50%] mr-20px" />
               <div>
-                <div class="text-20px text-700">
+                <div class="text-20px">
                   {{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
                 </div>
                 <div class="mt-10px text-14px text-gray-500">
@@ -17,7 +17,7 @@
             </div>
           </el-col>
           <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
-            <div class="flex h-70px items-center justify-end <sm:mt-10px">
+            <div class="flex h-70px items-center justify-end lt-sm:mt-10px">
               <div class="px-8px text-right">
                 <div class="text-14px text-gray-400 mb-20px">{{ t('workplace.project') }}</div>
                 <CountTo

+ 16 - 12
src/views/Login/Login.vue

@@ -1,11 +1,11 @@
 <template>
   <div
     :class="prefixCls"
-    class="h-[100%] relative <xl:bg-v-dark <sm:px-10px <xl:px-10px <md:px-10px"
+    class="h-[100%] relative lt-xl:bg-[var(--login-bg-color)] lt-sm:px-10px lt-xl:px-10px lt-md:px-10px"
   >
     <div class="relative h-full flex mx-auto">
       <div
-        :class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px <xl:hidden`"
+        :class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden`"
       >
         <!-- 左上角的 logo + 系统标题 -->
         <div class="flex items-center relative text-white">
@@ -27,33 +27,35 @@
           </TransitionGroup>
         </div>
       </div>
-      <div class="flex-1 p-30px <sm:p-10px dark:bg-v-dark relative">
+      <div class="flex-1 p-30px lt-sm:p-10px dark:bg-[var(--login-bg-color)] relative">
         <!-- 右上角的主题、语言选择 -->
-        <div class="flex justify-between items-center text-white @2xl:justify-end @xl:justify-end">
-          <div class="flex items-center @2xl:hidden @xl:hidden">
+        <div
+          class="flex justify-between items-center text-white at-2xl:justify-end at-xl:justify-end"
+        >
+          <div class="flex items-center at-2xl:hidden at-xl:hidden">
             <img alt="" class="w-48px h-48px mr-10px" src="@/assets/imgs/logo.png" />
             <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
           </div>
           <div class="flex justify-end items-center space-x-10px">
             <ThemeSwitch />
-            <LocaleDropdown class="<xl:text-white dark:text-white" />
+            <LocaleDropdown class="lt-xl:text-white dark:text-white" />
           </div>
         </div>
         <!-- 右边的登录界面 -->
         <Transition appear enter-active-class="animate__animated animate__bounceInRight">
           <div
-            class="h-full flex items-center m-auto w-[100%] @2xl:max-w-500px @xl:max-w-500px @md:max-w-500px @lg:max-w-500px"
+            class="h-full flex items-center m-auto w-[100%] at-2xl:max-w-500px at-xl:max-w-500px at-md:max-w-500px at-lg:max-w-500px"
           >
             <!-- 账号登录 -->
-            <LoginForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" />
+            <LoginForm class="p-20px h-auto m-auto lt-xl:(rounded-3xl light:bg-white)" />
             <!-- 手机登录 -->
-            <MobileForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" />
+            <MobileForm class="p-20px h-auto m-auto lt-xl:(rounded-3xl light:bg-white)" />
             <!-- 二维码登录 -->
-            <QrCodeForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" />
+            <QrCodeForm class="p-20px h-auto m-auto lt-xl:(rounded-3xl light:bg-white)" />
             <!-- 注册 -->
-            <RegisterForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" />
+            <RegisterForm class="p-20px h-auto m-auto lt-xl:(rounded-3xl light:bg-white)" />
             <!-- 三方登录 -->
-            <SSOLoginVue class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" />
+            <SSOLoginVue class="p-20px h-auto m-auto lt-xl:(rounded-3xl light:bg-white)" />
           </div>
         </Transition>
       </div>
@@ -82,6 +84,8 @@ const prefixCls = getPrefixCls('login')
 $prefix-cls: #{$namespace}-login;
 
 .#{$prefix-cls} {
+  overflow: auto;
+
   &__left {
     &::before {
       position: absolute;

+ 1 - 1
src/views/Login/components/LoginForm.vue

@@ -9,7 +9,7 @@
     label-width="120px"
     size="large"
   >
-    <el-row style="maring-left: -10px; maring-right: -10px">
+    <el-row style="margin-left: -10px; margin-right: -10px">
       <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
         <el-form-item>
           <LoginFormTitle style="width: 100%" />

+ 10 - 1
src/views/Login/components/MobileForm.vue

@@ -185,12 +185,17 @@ const signIn = async () => {
   await getTenantId()
   const data = await validForm()
   if (!data) return
+  ElLoading.service({
+    lock: true,
+    text: '正在加载系统中...',
+    background: 'rgba(0, 0, 0, 0.7)'
+  })
   loginLoading.value = true
   smsVO.loginSms.mobile = loginData.loginForm.mobileNumber
   smsVO.loginSms.code = loginData.loginForm.code
   await smsLogin(smsVO.loginSms)
     .then(async (res) => {
-      setToken(res?.token)
+      setToken(res)
       if (!redirect.value) {
         redirect.value = '/'
       }
@@ -199,6 +204,10 @@ const signIn = async () => {
     .catch(() => {})
     .finally(() => {
       loginLoading.value = false
+      setTimeout(() => {
+        const loadingInstance = ElLoading.service()
+        loadingInstance.close()
+      }, 400)
     })
 }
 </script>

+ 1 - 1
src/views/Login/components/QrCodeForm.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-row v-show="getShow" style="maring-left: -10px; maring-right: -10px">
+  <el-row v-show="getShow" style="margin-left: -10px; margin-right: -10px">
     <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
       <LoginFormTitle style="width: 100%" />
     </el-col>

+ 12 - 2
src/views/Login/components/SSOLogin.vue

@@ -55,7 +55,14 @@ const client = ref({
   name: '',
   logo: ''
 })
-const queryParams = reactive({
+interface queryType {
+  responseType: string
+  clientId: string
+  redirectUri: string
+  state: string
+  scopes: string[]
+}
+const queryParams = reactive<queryType>({
   // URL 上的 client_id、scope 等参数
   responseType: '',
   clientId: '',
@@ -64,7 +71,10 @@ const queryParams = reactive({
   scopes: [] // 优先从 query 参数获取;如果未传递,从后端获取
 })
 const ssoVisible = computed(() => unref(getLoginState) === LoginStateEnum.SSO) // 是否展示 SSO 登录的表单
-const formData = reactive({
+interface formType {
+  scopes: string[]
+}
+const formData = reactive<formType>({
   scopes: [] // 已选中的 scope 数组
 })
 const formLoading = ref(false) // 表单是否提交中

+ 2 - 1
src/views/infra/build/index.vue

@@ -23,7 +23,7 @@
       </el-button>
       <el-scrollbar height="580">
         <div>
-          <pre><code class="hljs" v-html="highlightedCode(formData)"></code></pre>
+          <pre><code class="hljs" v-dompurify-html="highlightedCode(formData)"></code></pre>
         </div>
       </el-scrollbar>
     </div>
@@ -39,6 +39,7 @@ import hljs from 'highlight.js' // 导入代码高亮文件
 import 'highlight.js/styles/github.css' // 导入代码高亮样式
 import xml from 'highlight.js/lib/languages/java'
 import json from 'highlight.js/lib/languages/json'
+import formCreate from '@form-create/element-ui'
 
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息

+ 1 - 1
src/views/infra/job/index.vue

@@ -106,7 +106,7 @@
           <el-button
             type="danger"
             link
-            @click="handleDelete(scope.row)"
+            @click="handleDelete(scope.row.id)"
             v-hasPermi="['infra:job:delete']"
           >
             删除

+ 9 - 8
src/views/mall/product/spu/components/SkuList.vue

@@ -443,14 +443,15 @@ const generateTableData = (propertyList: any[]) => {
  */
 const validateData = (propertyList: any[]) => {
   const skuPropertyIds: number[] = []
-  formData.value!.skus!.forEach((sku) =>
-    sku.properties
-      ?.map((property) => property.propertyId)
-      ?.forEach((propertyId) => {
-        if (skuPropertyIds.indexOf(propertyId!) === -1) {
-          skuPropertyIds.push(propertyId!)
-        }
-      })
+  formData.value!.skus!.forEach(
+    (sku) =>
+      sku.properties
+        ?.map((property) => property.propertyId)
+        ?.forEach((propertyId) => {
+          if (skuPropertyIds.indexOf(propertyId!) === -1) {
+            skuPropertyIds.push(propertyId!)
+          }
+        })
   )
   const propertyIds = propertyList.map((item) => item.id)
   return skuPropertyIds.length === propertyIds.length

+ 1 - 5
src/views/mall/promotion/combination/activity/CombinationActivityForm.vue

@@ -134,11 +134,7 @@ const open = async (type: string, id?: number) => {
       const data = (await CombinationActivityApi.getCombinationActivity(
         id
       )) as CombinationActivityApi.CombinationActivityVO
-      await getSpuDetails(
-        data.spuId!,
-        data.products?.map((sku) => sku.skuId),
-        data.products
-      )
+      await getSpuDetails(data.spuId!, data.products?.map((sku) => sku.skuId), data.products)
       formRef.value.setValues(data)
     } finally {
       formLoading.value = false

+ 2 - 1
src/views/mall/promotion/combination/activity/index.vue

@@ -98,9 +98,10 @@ const handleDelete = (id: number) => {
   tableMethods.delList(id, false)
 }
 
+// TODO @puhui999:要不还是使用原生的 element plus 做。感觉 crud schema 复杂界面,做起来麻烦
 /** 初始化 **/
 onMounted(() => {
-  /*
+  /**
  TODO
  后面准备封装成一个函数来操作 tableColumns 重新排列:比如说需求是表单上商品选择是在后面的而列表展示的时候需要调到位置。
  封装效果支持批量操作,给出 field 和需要插入的位置,例:[{field:'spuId',index: 1}] 效果为把 field 为 spuId 的 column 移动到第一个位置

+ 2 - 0
src/views/mall/promotion/components/SpuAndSkuList.vue

@@ -50,8 +50,10 @@ const spuData = ref<Spu[]>([]) // spu 详情数据列表
 const skuListRef = ref() // 商品属性列表Ref
 const spuPropertyList = ref<SpuProperty<T>[]>([]) // spuId 对应的 sku 的属性列表
 const expandRowKeys = ref<number[]>() // 控制展开行需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。
+
 /**
  * 获取所有 sku 活动配置
+ *
  * @param extendedAttribute 在 sku 上扩展的属性,例:秒杀活动 sku 扩展属性 productConfig 请参考 seckillActivity.ts
  */
 const getSkuConfigs = (extendedAttribute: string) => {

+ 1 - 5
src/views/mall/promotion/seckill/activity/SeckillActivityForm.vue

@@ -144,11 +144,7 @@ const open = async (type: string, id?: number) => {
       const data = (await SeckillActivityApi.getSeckillActivity(
         id
       )) as SeckillActivityApi.SeckillActivityVO
-      await getSpuDetails(
-        data.spuId!,
-        data.products?.map((sku) => sku.skuId),
-        data.products
-      )
+      await getSpuDetails(data.spuId!, data.products?.map((sku) => sku.skuId), data.products)
       formRef.value.setValues(data)
     } finally {
       formLoading.value = false

+ 4 - 4
src/views/mall/trade/order/index.vue

@@ -12,7 +12,7 @@
         <el-select class="!w-280px" v-model="queryParams.status" clearable placeholder="全部">
           <el-option
             v-for="dict in getStrDictOptions(DICT_TYPE.TRADE_ORDER_STATUS)"
-            :key="(dict.value as string)"
+            :key="dict.value as string"
             :label="dict.label"
             :value="dict.value"
           />
@@ -27,7 +27,7 @@
         >
           <el-option
             v-for="dict in getStrDictOptions(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)"
-            :key="(dict.value as string)"
+            :key="dict.value as string"
             :label="dict.label"
             :value="dict.value"
           />
@@ -48,7 +48,7 @@
         <el-select class="!w-280px" v-model="queryParams.terminal" clearable placeholder="全部">
           <el-option
             v-for="dict in getStrDictOptions(DICT_TYPE.TERMINAL)"
-            :key="(dict.value as string)"
+            :key="dict.value as string"
             :label="dict.label"
             :value="dict.value"
           />
@@ -58,7 +58,7 @@
         <el-select class="!w-280px" v-model="queryParams.type" clearable placeholder="全部">
           <el-option
             v-for="dict in getStrDictOptions(DICT_TYPE.TRADE_ORDER_TYPE)"
-            :key="(dict.value as string)"
+            :key="dict.value as string"
             :label="dict.label"
             :value="dict.value"
           />

+ 23 - 3
src/views/mp/components/wx-msg/comment.scss

@@ -90,8 +90,19 @@
     padding: 15px;
     overflow: hidden;
     background: #fff;
-    font-family: Segoe UI, Lucida Grande, Helvetica, Arial, Microsoft YaHei, FreeSans, Arimo,
-      Droid Sans, wenquanyi micro hei, Hiragino Sans GB, Hiragino Sans GB W3, FontAwesome,
+    font-family:
+      Segoe UI,
+      Lucida Grande,
+      Helvetica,
+      Arial,
+      Microsoft YaHei,
+      FreeSans,
+      Arimo,
+      Droid Sans,
+      wenquanyi micro hei,
+      Hiragino Sans GB,
+      Hiragino Sans GB W3,
+      FontAwesome,
       sans-serif;
     color: #333;
     font-size: 14px;
@@ -99,7 +110,16 @@
 
   blockquote {
     margin: 0;
-    font-family: Georgia, Times New Roman, Times, Kai, Kaiti SC, KaiTi, BiauKai, FontAwesome, serif;
+    font-family:
+      Georgia,
+      Times New Roman,
+      Times,
+      Kai,
+      Kaiti SC,
+      KaiTi,
+      BiauKai,
+      FontAwesome,
+      serif;
     padding: 1px 0 1px 15px;
     border-left: 4px solid #ddd;
   }

+ 2 - 2
tsconfig.json

@@ -10,7 +10,7 @@
     "resolveJsonModule": true,
     "esModuleInterop": true,
     "lib": ["esnext", "dom"],
-    "baseUrl": ".",
+    "baseUrl": "./",
     "allowJs": true,
     "forceConsistentCasingInFileNames": true,
     "allowSyntheticDefaultImports": true,
@@ -36,7 +36,7 @@
     "typeRoots": ["./node_modules/@types/", "./types"]
   },
   "include": [
-    "src/**/*",
+    "src",
     "types/**/*.d.ts",
     "src/types/auto-imports.d.ts",
     "src/types/auto-components.d.ts"

+ 105 - 0
uno.config.ts

@@ -0,0 +1,105 @@
+import { defineConfig, toEscapedSelector as e, presetUno } from 'unocss'
+import transformerVariantGroup from '@unocss/transformer-variant-group'
+
+export default defineConfig({
+  // ...UnoCSS options
+  rules: [
+    [
+      /^custom-hover$/,
+      ([], { rawSelector }) => {
+        const selector = e(rawSelector)
+        return `
+${selector} {
+  display: flex;
+  height: 100%;
+  padding: 1px 10px 0;
+  cursor: pointer;
+  align-items: center;
+  transition: background var(--transition-time-02);
+}
+/* you can have multiple rules */
+${selector}:hover {
+  background-color: var(--top-header-hover-color);
+}
+.dark ${selector}:hover {
+  background-color: var(--el-bg-color-overlay);
+}
+`
+      }
+    ],
+    [
+      /^layout-border__left$/,
+      ([], { rawSelector }) => {
+        const selector = e(rawSelector)
+        return `
+${selector}:before {
+  content: "";
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 1px;
+  height: 100%;
+  background-color: var(--el-border-color);
+  z-index: 3;
+}
+`
+      }
+    ],
+    [
+      /^layout-border__right$/,
+      ([], { rawSelector }) => {
+        const selector = e(rawSelector)
+        return `
+${selector}:after {
+  content: "";
+  position: absolute;
+  top: 0;
+  right: 0;
+  width: 1px;
+  height: 100%;
+  background-color: var(--el-border-color);
+  z-index: 3;
+}
+`
+      }
+    ],
+    [
+      /^layout-border__top$/,
+      ([], { rawSelector }) => {
+        const selector = e(rawSelector)
+        return `
+${selector}:before {
+  content: "";
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 1px;
+  background-color: var(--el-border-color);
+  z-index: 3;
+}
+`
+      }
+    ],
+    [
+      /^layout-border__bottom$/,
+      ([], { rawSelector }) => {
+        const selector = e(rawSelector)
+        return `
+${selector}:after {
+  content: "";
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 1px;
+  background-color: var(--el-border-color);
+  z-index: 3;
+}
+`
+      }
+    ]
+  ],
+  presets: [presetUno({ dark: 'class', attributify: false })],
+  transformers: [transformerVariantGroup()]
+})

+ 0 - 61
windi.config.ts

@@ -1,61 +0,0 @@
-import { defineConfig } from 'vite-plugin-windicss'
-import plugin from 'windicss/plugin'
-
-function range(size, startAt = 1) {
-  return Array.from(Array(size).keys()).map((i) => i + startAt)
-}
-
-export default defineConfig({
-  extract: {
-    include: ['src/**/*.{vue,html,jsx,tsx}'],
-    exclude: ['node_modules', '.git']
-  },
-  darkMode: 'class',
-  attributify: false,
-  theme: {
-    extend: {
-      backgroundColor: {
-        // 暗黑背景色
-        'v-dark': 'var(--dark-bg-color)'
-      }
-    }
-  },
-  plugins: [
-    plugin(({ addComponents }) => {
-      const obj = {}
-      range(50).map((i) => {
-        obj[`.border-top-${i}`] = {
-          borderTopWidth: `${i}px`
-        }
-        obj[`.border-left-${i}`] = {
-          borderLeftWidth: `${i}px`
-        }
-        obj[`.border-right-${i}`] = {
-          borderRightWidth: `${i}px`
-        }
-        obj[`.border-bottom-${i}`] = {
-          borderBottomWidth: `${i}px`
-        }
-      })
-      addComponents({
-        '.hover-trigger': {
-          display: 'flex',
-          height: '100%',
-          padding: '1px 10px 0',
-          cursor: 'pointer',
-          alignItems: 'center',
-          transition: 'background var(--transition-time-02)',
-          '&:hover': {
-            backgroundColor: 'var(--top-header-hover-color)'
-          }
-        },
-        '.dark .hover-trigger': {
-          '&:hover': {
-            backgroundColor: 'var(--el-bg-color-overlay)'
-          }
-        },
-        ...obj
-      })
-    })
-  ]
-})