浏览代码

Merge pull request #529 from kailong321200875/master

release: release
Archer 7 月之前
父节点
当前提交
f6c4022261
共有 39 个文件被更改,包括 498 次插入191 次删除
  1. 0 8
      .eslintignore
  2. 0 71
      .eslintrc.cjs
  3. 1 4
      .husky/commit-msg
  4. 1 7
      .husky/pre-commit
  5. 2 8
      README.md
  6. 2 8
      README.zh-CN.md
  7. 81 0
      eslint.config.mjs
  8. 34 33
      package.json
  9. 4 4
      src/components/Button/src/Button.vue
  10. 2 2
      src/components/Descriptions/src/Descriptions.vue
  11. 178 0
      src/components/Dialog/hooks/useResize.ts
  12. 7 0
      src/components/Dialog/src/Dialog.vue
  13. 73 0
      src/components/Dialog/src/ResizeDialog.vue
  14. 1 1
      src/components/Footer/src/Footer.vue
  15. 1 1
      src/components/Form/src/helper/index.ts
  16. 19 1
      src/components/Menu/src/Menu.vue
  17. 11 2
      src/components/Menu/src/components/useRenderMenuItem.tsx
  18. 1 1
      src/components/Table/src/components/ColumnSetting.vue
  19. 1 1
      src/components/TagsView/src/TagsView.vue
  20. 1 1
      src/components/UserInfo/src/components/LockPage.vue
  21. 1 1
      src/components/VideoPlayer/src/VideoPlayer.vue
  22. 1 13
      src/layout/Layout.vue
  23. 5 1
      src/layout/components/AppView.vue
  24. 1 0
      src/locales/en.ts
  25. 1 0
      src/locales/zh-CN.ts
  26. 8 0
      src/styles/var.css
  27. 0 1
      src/utils/index.ts
  28. 6 3
      src/utils/is.ts
  29. 1 0
      src/views/Authorization/Menu/components/AddButtonPermission.vue
  30. 34 0
      src/views/Components/Dialog.vue
  31. 12 12
      src/views/Components/Form/DefaultForm.vue
  32. 1 1
      src/views/Components/Table/CardTable.vue
  33. 1 1
      src/views/Components/Table/DefaultTable.vue
  34. 1 1
      src/views/Components/Table/TableImagePreview.vue
  35. 1 1
      src/views/Components/Table/TableVideoPreview.vue
  36. 1 1
      src/views/Dashboard/Workplace.vue
  37. 0 1
      src/views/Example/Page/ExampleAdd.vue
  38. 1 1
      types/env.d.ts
  39. 2 0
      vite.config.ts

+ 0 - 8
.eslintignore

@@ -1,8 +0,0 @@
-/build/
-/config/
-/dist/
-/*.js
-/test/unit/coverage/
-/node_modules/*
-/dist*
-/src/main.ts

+ 0 - 71
.eslintrc.cjs

@@ -1,71 +0,0 @@
-// @ts-check
-const { defineConfig } = require('eslint-define-config')
-module.exports = defineConfig({
-  root: true,
-  env: {
-    browser: true,
-    node: true,
-    es6: true
-  },
-  parser: 'vue-eslint-parser',
-  parserOptions: {
-    parser: '@typescript-eslint/parser',
-    ecmaVersion: 2020,
-    sourceType: 'module',
-    jsxPragma: 'React',
-    ecmaFeatures: {
-      jsx: true
-    }
-  },
-  extends: [
-    'plugin:vue/vue3-recommended',
-    'plugin:@typescript-eslint/recommended',
-    'prettier',
-    'plugin:prettier/recommended'
-  ],
-  rules: {
-    'vue/no-setup-props-destructure': 'off',
-    'vue/script-setup-uses-vars': 'error',
-    'vue/no-reserved-component-names': 'off',
-    '@typescript-eslint/ban-ts-ignore': 'off',
-    '@typescript-eslint/explicit-function-return-type': 'off',
-    '@typescript-eslint/no-explicit-any': 'off',
-    '@typescript-eslint/no-var-requires': 'off',
-    '@typescript-eslint/no-empty-function': 'off',
-    'vue/custom-event-name-casing': 'off',
-    'no-use-before-define': 'off',
-    '@typescript-eslint/no-use-before-define': 'off',
-    '@typescript-eslint/ban-ts-comment': 'off',
-    '@typescript-eslint/ban-types': 'off',
-    '@typescript-eslint/no-non-null-assertion': 'off',
-    '@typescript-eslint/explicit-module-boundary-types': 'off',
-    '@typescript-eslint/no-unused-vars': 'off',
-    'no-unused-vars': 'off',
-    'space-before-function-paren': 'off',
-
-    'vue/attributes-order': 'off',
-    'vue/one-component-per-file': 'off',
-    'vue/html-closing-bracket-newline': 'off',
-    'vue/max-attributes-per-line': 'off',
-    'vue/multiline-html-element-content-newline': 'off',
-    'vue/singleline-html-element-content-newline': 'off',
-    'vue/attribute-hyphenation': 'off',
-    'vue/require-default-prop': 'off',
-    'vue/require-explicit-emits': 'off',
-    'vue/html-self-closing': [
-      'error',
-      {
-        html: {
-          void: 'always',
-          normal: 'never',
-          component: 'always'
-        },
-        svg: 'always',
-        math: 'always'
-      }
-    ],
-    'vue/multi-word-component-names': 'off',
-    'vue/no-v-html': 'off',
-    'vue/require-toggle-inside-transition': 'off'
-  }
-})

+ 1 - 4
.husky/commit-msg

@@ -1,4 +1 @@
-#!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
-
-pnpm commitlint --edit "$1"
+npx --no -- commitlint --edit $1

+ 1 - 7
.husky/pre-commit

@@ -1,8 +1,2 @@
-#!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
-
-[ -n "$CI" ] && exit 0
-
-# Format and submit code according to lintstagedrc.js configuration
 npm run ts:check
-npm run lint:lint-staged
+npm run lint:lint-staged

+ 2 - 8
README.md

@@ -137,16 +137,10 @@ If you find this project helpful, welcome sponsorship to show your support~
 
 <img src="https://github.com/kailong321200875/my-image/raw/master/pay.jpg" />
 
-## Group
-
-If you want to join the technical communication group for discussion, please scan the code to join the group or add me as a friend
-
-### Group QR code
-
-<img src="https://github.com/kailong321200875/my-image/raw/master/WechatIMG435.jpg" />
-
 ### My QR code
 
+If you have any project cooperation or outsourcing, please scan the code to add me as a friend and leave a note of your purpose.
+
 <img src="https://github.com/kailong321200875/my-image/raw/master/me.jpg" />
 
 ## License

+ 2 - 8
README.zh-CN.md

@@ -137,16 +137,10 @@ pnpm run build:pro
 
 <img src="https://gitee.com/kailong110120130/my-image/raw/master/pay.jpg" />
 
-## 交流群
-
-如果你想进入技术交流群讨论,请扫码加入或者添加我为好友邀请入群
-
-### 群二维码
-
-<img src="https://gitee.com/kailong110120130/my-image/raw/master/WechatIMG435.jpg" />
-
 ### 我的二维码
 
+如有项目合作或项目外包,扫码加我好友,请备注来意。
+
 <img src="https://gitee.com/kailong110120130/my-image/raw/master/me.jpg" />
 
 ## 许可证

+ 81 - 0
eslint.config.mjs

@@ -0,0 +1,81 @@
+// 引入vue模版的eslint
+import pluginVue from 'eslint-plugin-vue'
+import eslint from '@eslint/js'
+// ts-eslint解析器,使 eslint 可以解析 ts 语法
+import tseslint from 'typescript-eslint'
+// vue文件解析器
+import vueParser from 'vue-eslint-parser'
+import prettier from 'eslint-plugin-prettier'
+
+export default tseslint.config({
+  // ignores: ['node_modules', 'prettier.config.cjs', 'dist*'],
+  files: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'],
+  // tseslint.config添加了extends扁平函数,直接用。否则是eslint9.0版本是没有extends的
+  extends: [
+    eslint.configs.recommended,
+    ...tseslint.configs.recommended,
+    ...pluginVue.configs['flat/essential']
+  ],
+  plugins: {
+    prettier
+  },
+  languageOptions: {
+    parser: vueParser, // 使用vue解析器,这个可以识别vue文件
+    parserOptions: {
+      parser: tseslint.parser, // 在vue文件上使用ts解析器
+      sourceType: 'module',
+      ecmaVersion: 2020,
+      ecmaFeatures: {
+        jsx: true
+      }
+    }
+  },
+  rules: {
+    'prettier/prettier': 'error',
+    'no-useless-escape': 0,
+    'no-undef': 0,
+    'vue/no-setup-props-destructure': 0,
+    'vue/script-setup-uses-vars': 1,
+    'vue/no-reserved-component-names': 0,
+    '@typescript-eslint/ban-ts-ignore': 0,
+    '@typescript-eslint/explicit-function-return-type': 0,
+    '@typescript-eslint/no-explicit-any': 0,
+    '@typescript-eslint/no-var-requires': 0,
+    '@typescript-eslint/no-empty-function': 0,
+    'vue/custom-event-name-casing': 0,
+    'no-use-before-define': 0,
+    '@typescript-eslint/no-use-before-define': 0,
+    '@typescript-eslint/ban-ts-comment': 0,
+    '@typescript-eslint/ban-types': 0,
+    '@typescript-eslint/no-non-null-assertion': 0,
+    '@typescript-eslint/explicit-module-boundary-types': 0,
+    '@typescript-eslint/no-unused-vars': 0,
+    'no-unused-vars': 0,
+    'space-before-function-paren': 0,
+
+    'vue/attributes-order': 0,
+    'vue/one-component-per-file': 0,
+    'vue/html-closing-bracket-newline': 0,
+    'vue/max-attributes-per-line': 0,
+    'vue/multiline-html-element-content-newline': 0,
+    'vue/singleline-html-element-content-newline': 0,
+    'vue/attribute-hyphenation': 0,
+    'vue/require-default-prop': 0,
+    'vue/require-explicit-emits': 0,
+    'vue/html-self-closing': [
+      1,
+      {
+        html: {
+          void: 'always',
+          normal: 'never',
+          component: 'always'
+        },
+        svg: 'always',
+        math: 'always'
+      }
+    ],
+    'vue/multi-word-component-names': 0,
+    'vue/no-v-html': 0,
+    'vue/require-toggle-inside-transition': 0
+  }
+})

+ 34 - 33
package.json

@@ -19,7 +19,7 @@
     "npm:check": "pnpx npm-check-updates -u",
     "clean": "pnpx rimraf node_modules",
     "clean:cache": "pnpx rimraf node_modules/.cache",
-    "lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
+    "lint:eslint": "eslint . --fix \"src/**/*.{js,ts,tsx,vue,html}\"",
     "lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,vue,html,md}\"",
     "lint:style": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
     "lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.cjs",
@@ -30,7 +30,7 @@
   "dependencies": {
     "@iconify/iconify": "^3.1.1",
     "@iconify/vue": "^4.1.2",
-    "@vueuse/core": "^10.10.0",
+    "@vueuse/core": "^10.11.0",
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^5.1.10",
     "@zxcvbn-ts/core": "^3.0.4",
@@ -39,78 +39,79 @@
     "cropperjs": "^1.6.2",
     "dayjs": "^1.11.11",
     "driver.js": "^1.3.1",
-    "echarts": "^5.5.0",
+    "echarts": "^5.5.1",
     "echarts-wordcloud": "^2.1.0",
-    "element-plus": "2.7.4",
+    "element-plus": "2.7.7",
     "lodash-es": "^4.17.21",
     "mitt": "^3.0.1",
-    "monaco-editor": "^0.49.0",
+    "monaco-editor": "^0.50.0",
     "nprogress": "^0.2.0",
     "pinia": "^2.1.7",
     "pinia-plugin-persistedstate": "^3.2.1",
     "qrcode": "^1.5.3",
-    "qs": "^6.12.1",
+    "qs": "^6.12.3",
     "url": "^0.11.3",
-    "vue": "3.4.27",
-    "vue-draggable-plus": "^0.5.0",
+    "vue": "3.4.32",
+    "vue-draggable-plus": "^0.5.2",
     "vue-i18n": "9.13.1",
     "vue-json-pretty": "^2.4.0",
-    "vue-router": "^4.3.2",
-    "vue-types": "^5.1.2",
+    "vue-router": "^4.4.0",
+    "vue-types": "^5.1.3",
     "xgplayer": "^3.0.18"
   },
   "devDependencies": {
     "@commitlint/cli": "^19.3.0",
     "@commitlint/config-conventional": "^19.2.2",
-    "@iconify/json": "^2.2.215",
+    "@iconify/json": "^2.2.229",
     "@intlify/unplugin-vue-i18n": "^4.0.0",
     "@types/fs-extra": "^11.0.4",
     "@types/inquirer": "^9.0.7",
     "@types/lodash-es": "^4.17.12",
     "@types/mockjs": "^1.0.10",
-    "@types/node": "^20.13.0",
+    "@types/node": "^20.14.11",
     "@types/nprogress": "^0.2.3",
     "@types/qrcode": "^1.5.5",
     "@types/qs": "^6.9.15",
     "@types/sortablejs": "^1.15.8",
-    "@typescript-eslint/eslint-plugin": "^7.11.0",
-    "@typescript-eslint/parser": "^7.11.0",
-    "@unocss/transformer-variant-group": "^0.60.4",
+    "@typescript-eslint/eslint-plugin": "^7.16.1",
+    "@typescript-eslint/parser": "^7.16.1",
+    "@unocss/transformer-variant-group": "^0.61.5",
     "@vitejs/plugin-legacy": "^5.4.1",
     "@vitejs/plugin-vue": "^5.0.5",
     "@vitejs/plugin-vue-jsx": "^4.0.0",
     "autoprefixer": "^10.4.19",
     "chalk": "^5.3.0",
     "consola": "^3.2.3",
-    "eslint": "^8.57.0",
+    "eslint": "^9.7.0",
     "eslint-config-prettier": "^9.1.0",
     "eslint-define-config": "^2.1.0",
-    "eslint-plugin-prettier": "^5.1.3",
-    "eslint-plugin-vue": "^9.26.0",
+    "eslint-plugin-prettier": "^5.2.1",
+    "eslint-plugin-vue": "^9.27.0",
     "esno": "^4.7.0",
     "fs-extra": "^11.2.0",
-    "husky": "^9.0.11",
-    "inquirer": "^9.2.23",
+    "husky": "^9.1.0",
+    "inquirer": "^10.0.3",
     "less": "^4.2.0",
-    "lint-staged": "^15.2.5",
+    "lint-staged": "^15.2.7",
     "mockjs": "^1.1.0",
     "plop": "^4.0.1",
-    "postcss": "^8.4.38",
+    "postcss": "^8.4.39",
     "postcss-html": "^1.7.0",
     "postcss-less": "^6.0.0",
-    "prettier": "^3.2.5",
-    "rimraf": "^5.0.7",
-    "rollup": "^4.18.0",
+    "prettier": "^3.3.3",
+    "rimraf": "^6.0.1",
+    "rollup": "^4.18.1",
     "rollup-plugin-visualizer": "^5.12.0",
-    "stylelint": "^16.6.1",
+    "stylelint": "^16.7.0",
     "stylelint-config-html": "^1.1.0",
-    "stylelint-config-recommended": "^14.0.0",
-    "stylelint-config-standard": "^36.0.0",
+    "stylelint-config-recommended": "^14.0.1",
+    "stylelint-config-standard": "^36.0.1",
     "stylelint-order": "^6.0.4",
-    "terser": "^5.31.0",
-    "typescript": "5.4.5",
-    "unocss": "^0.60.4",
-    "vite": "5.2.12",
+    "terser": "^5.31.3",
+    "typescript": "5.5.3",
+    "typescript-eslint": "^7.16.1",
+    "unocss": "^0.61.5",
+    "vite": "5.3.4",
     "vite-plugin-ejs": "^1.7.0",
     "vite-plugin-eslint": "^1.8.1",
     "vite-plugin-mock": "2.9.6",
@@ -119,7 +120,7 @@
     "vite-plugin-style-import": "2.0.0",
     "vite-plugin-svg-icons": "^2.0.1",
     "vite-plugin-url-copy": "^1.1.4",
-    "vue-tsc": "^2.0.19"
+    "vue-tsc": "^2.0.26"
   },
   "packageManager": "pnpm@8.1.0",
   "engines": {

+ 4 - 4
src/components/Button/src/Button.vue

@@ -90,16 +90,16 @@ const props = defineProps({
 const emits = defineEmits(['click'])
 
 const color = computed(() => {
-  const { type } = props
-  if (type === 'primary') {
+  const { type, link } = props
+  if (type === 'primary' && !link) {
     return unref(getTheme).elColorPrimary
   }
   return ''
 })
 
 const style = computed(() => {
-  const { type } = props
-  if (type === 'primary') {
+  const { type, link } = props
+  if (type === 'primary' && !link) {
     return '--el-button-text-color: #fff; --el-button-hover-text-color: #fff'
   }
   return ''

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

@@ -133,7 +133,7 @@ export default defineComponent({
                           <div class="flex-1 px-8px py-11px bg-[var(--el-bg-color)] color-[var(--el-text-color-primary)] text-size-14px">
                             {item.slots?.default
                               ? item.slots?.default(props.data)
-                              : get(props.data, item.field) ?? defaultData}
+                              : (get(props.data, item.field) ?? defaultData)}
                           </div>
                         </div>
                       ) : (
@@ -147,7 +147,7 @@ export default defineComponent({
                           <div class="flex-1 px-8px py-11px bg-[var(--el-bg-color)] color-[var(--el-text-color-primary)] text-size-14px">
                             {item.slots?.default
                               ? item.slots?.default(props.data)
-                              : get(props.data, item.field) ?? defaultData}
+                              : (get(props.data, item.field) ?? defaultData)}
                           </div>
                         </div>
                       )}

+ 178 - 0
src/components/Dialog/hooks/useResize.ts

@@ -0,0 +1,178 @@
+import { ref } from 'vue'
+
+export const useResize = (props?: {
+  minHeightPx?: number
+  minWidthPx?: number
+  initHeight?: number
+  initWidth?: number
+}) => {
+  const {
+    minHeightPx = 400,
+    minWidthPx = window.innerWidth / 2,
+    initHeight = 400,
+    initWidth = window.innerWidth / 2
+  } = props || {}
+  // 屏幕宽度的 50% 作为最小宽度
+  //   const minWidthPx = window.innerWidth / 2
+  // 固定的最小高度 400px
+  //   const minHeightPx = 400
+  // 初始高度限制为 400px
+  const maxHeight = ref(initHeight + 'px')
+  // 初始宽度限制为 50%
+  const minWidth = ref(initWidth + 'px')
+  const setupDrag = (elDialog: any, el: any) => {
+    // 获取对话框元素
+    // 是否正在调整大小的标志
+    let isResizing = false
+    // 当前调整的方向
+    let currentResizeDirection = ''
+
+    // 鼠标移动时的事件处理器,用于检测鼠标位置并设置相应的光标样式
+    const handleMouseMove = (e: any) => {
+      const rect = elDialog.getBoundingClientRect()
+      // 鼠标相对于对话框左侧的偏移量
+      const offsetX = e.clientX - rect.left
+      // 鼠标相对于对话框顶部的偏移量
+      const offsetY = e.clientY - rect.top
+      const width = elDialog.clientWidth
+      const height = elDialog.clientHeight
+
+      // 获取对话框的内边距
+      const computedStyle = window.getComputedStyle(elDialog)
+      const paddingLeft = parseFloat(computedStyle.paddingLeft)
+      const paddingRight = parseFloat(computedStyle.paddingRight)
+      const paddingBottom = parseFloat(computedStyle.paddingBottom)
+      const paddingTop = parseFloat(computedStyle.paddingTop)
+
+      // 根据鼠标位置设置相应的光标样式和调整方向
+      if (!isResizing) {
+        if (offsetX < paddingLeft && offsetY > paddingTop && offsetY < height - paddingBottom) {
+          elDialog.style.cursor = 'ew-resize' // 左右箭头
+          currentResizeDirection = 'left'
+        } else if (
+          offsetX > width - paddingRight &&
+          offsetY > paddingTop &&
+          offsetY < height - paddingBottom
+        ) {
+          elDialog.style.cursor = 'ew-resize' // 左右箭头
+          currentResizeDirection = 'right'
+        } else if (
+          offsetY < paddingTop &&
+          offsetX > paddingLeft &&
+          offsetX < width - paddingRight
+        ) {
+          elDialog.style.cursor = 'ns-resize' // 上下箭头
+          currentResizeDirection = 'top'
+        } else if (
+          offsetY > height - paddingBottom &&
+          offsetX > paddingLeft &&
+          offsetX < width - paddingRight
+        ) {
+          elDialog.style.cursor = 'ns-resize' // 上下箭头
+          currentResizeDirection = 'bottom'
+        } else if (offsetX < paddingLeft && offsetY < paddingTop) {
+          elDialog.style.cursor = 'nwse-resize' // 左上右下箭头
+          currentResizeDirection = 'top-left'
+        } else if (offsetX > width - paddingRight && offsetY < paddingTop) {
+          elDialog.style.cursor = 'nesw-resize' // 右上左下箭头
+          currentResizeDirection = 'top-right'
+        } else if (offsetX < paddingLeft && offsetY > height - paddingBottom) {
+          elDialog.style.cursor = 'nesw-resize' // 右上左下箭头
+          currentResizeDirection = 'bottom-left'
+        } else if (offsetX > width - paddingRight && offsetY > height - paddingBottom) {
+          elDialog.style.cursor = 'nwse-resize' // 左上右下箭头
+          currentResizeDirection = 'bottom-right'
+        } else {
+          elDialog.style.cursor = 'default'
+          currentResizeDirection = ''
+        }
+      }
+    }
+
+    // 鼠标按下时的事件处理器,开始调整对话框大小
+    const handleMouseDown = (e) => {
+      if (currentResizeDirection) {
+        isResizing = true
+
+        const initialX = e.clientX
+        const initialY = e.clientY
+        const initialWidth = elDialog.clientWidth
+        const initialHeight = el.querySelector('.el-dialog__body').clientHeight
+
+        // 调整大小的事件处理器
+        const handleResizing = (e: any) => {
+          if (!isResizing) return
+
+          let newWidth = initialWidth
+          let newHeight = initialHeight
+
+          // 根据当前调整方向计算新的宽度和高度
+          if (currentResizeDirection.includes('right')) {
+            newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2)
+            minWidth.value = `${newWidth}px`
+          }
+
+          if (currentResizeDirection.includes('left')) {
+            newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2)
+            minWidth.value = `${newWidth}px`
+          }
+
+          if (currentResizeDirection.includes('bottom')) {
+            newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20)
+            maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
+          }
+
+          if (currentResizeDirection.includes('top')) {
+            newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20)
+            maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
+          }
+
+          if (currentResizeDirection === 'top-left') {
+            newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2)
+            minWidth.value = `${newWidth}px`
+            newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20)
+            maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
+          }
+
+          if (currentResizeDirection === 'top-right') {
+            newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2)
+            minWidth.value = `${newWidth}px`
+            newHeight = Math.max(minHeightPx, initialHeight - (e.clientY - initialY) * 2 - 20)
+            maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
+          }
+
+          if (currentResizeDirection === 'bottom-left') {
+            newWidth = Math.max(minWidthPx, initialWidth - (e.clientX - initialX) * 2)
+            minWidth.value = `${newWidth}px`
+            newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20)
+            maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
+          }
+
+          if (currentResizeDirection === 'bottom-right') {
+            newWidth = Math.max(minWidthPx, initialWidth + (e.clientX - initialX) * 2)
+            minWidth.value = `${newWidth}px`
+            newHeight = Math.max(minHeightPx, initialHeight + (e.clientY - initialY) * 2 - 20)
+            maxHeight.value = `${Math.min(newHeight, window.innerHeight - 165)}px`
+          }
+        }
+        // 停止调整大小的事件处理器
+        const stopResizing = () => {
+          isResizing = false
+          document.removeEventListener('mousemove', handleResizing)
+          document.removeEventListener('mouseup', stopResizing)
+        }
+
+        document.addEventListener('mousemove', handleResizing)
+        document.addEventListener('mouseup', stopResizing)
+      }
+    }
+    elDialog.addEventListener('mousemove', handleMouseMove)
+    elDialog.addEventListener('mousedown', handleMouseDown)
+  }
+
+  return {
+    setupDrag,
+    maxHeight,
+    minWidth
+  }
+}

+ 7 - 0
src/components/Dialog/src/Dialog.vue

@@ -49,6 +49,13 @@ watch(
   }
 )
 
+watch(
+  () => props.maxHeight,
+  (val) => {
+    dialogHeight.value = isNumber(val) ? `${val}px` : val
+  }
+)
+
 const dialogStyle = computed(() => {
   return {
     height: unref(dialogHeight)

+ 73 - 0
src/components/Dialog/src/ResizeDialog.vue

@@ -0,0 +1,73 @@
+<script lang="tsx" setup>
+import { propTypes } from '@/utils/propTypes'
+import { computed, getCurrentInstance, onMounted, unref, useAttrs, useSlots } from 'vue'
+import Dialog from './Dialog.vue'
+import { useResize } from '../hooks/useResize'
+
+const props = defineProps({
+  modelValue: propTypes.bool.def(false),
+  title: propTypes.string.def('Dialog'),
+  fullscreen: propTypes.bool.def(true),
+  initWidth: propTypes.number.def(window.innerWidth / 2),
+  initHeight: propTypes.number.def(200),
+  minResizeWidth: propTypes.number.def(window.innerWidth / 2),
+  minResizeHeight: propTypes.number.def(200)
+})
+const { maxHeight, minWidth, setupDrag } = useResize({
+  minHeightPx: props.minResizeHeight,
+  minWidthPx: props.minResizeWidth,
+  initHeight: props.initHeight,
+  initWidth: props.initWidth
+})
+
+const vResize = {
+  mounted(el) {
+    const observer = new MutationObserver(() => {
+      const elDialog = el.querySelector('.el-dialog')
+
+      if (elDialog) {
+        // 在确认 `elDialog` 已渲染后进行处理
+        setupDrag(elDialog, el)
+        // observer.disconnect() // 一旦获取到元素,停止观察
+      }
+    })
+    // 开始观察子节点的变化
+    observer.observe(el, { childList: true, subtree: true })
+  }
+}
+
+const attrs = useAttrs()
+const slots = useSlots()
+const getBindValue = computed(() => {
+  const delArr: string[] = ['maxHeight', 'width']
+  const obj = Object.assign({}, { ...unref(attrs), ...props })
+  for (const key in obj) {
+    if (delArr.indexOf(key) !== -1) {
+      delete obj[key]
+    }
+  }
+  return obj
+})
+const instance = getCurrentInstance()
+const initDirective = () => {
+  const directives = instance?.appContext?.app._context?.directives
+
+  // 检查指令是否已经注册
+  if (!directives || !directives['resize']) {
+    instance?.appContext?.app.directive('resize', vResize)
+  }
+}
+onMounted(() => {
+  initDirective()
+})
+</script>
+<template>
+  <div v-resize>
+    <Dialog v-bind="getBindValue" :maxHeight="maxHeight" :width="minWidth">
+      <slot></slot>
+      <template v-if="slots.footer" #footer>
+        <slot name="footer"></slot>
+      </template>
+    </Dialog>
+  </div>
+</template>

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

@@ -15,7 +15,7 @@ const title = computed(() => appStore.getTitle)
 <template>
   <div
     :class="prefixCls"
-    class="shrink-0 text-center text-[var(--el-text-color-placeholder)] bg-[var(--app-content-bg-color)] h-[var(--app-footer-height)] leading-[var(--app-footer-height)] dark:bg-[var(--el-bg-color)]"
+    class="text-center text-[var(--el-text-color-placeholder)] bg-[var(--app-content-bg-color)] h-[var(--app-footer-height)] leading-[var(--app-footer-height)] dark:bg-[var(--el-bg-color)] overflow-hidden"
   >
     Copyright ©2021-present {{ title }}
   </div>

+ 1 - 1
src/components/Form/src/helper/index.ts

@@ -161,7 +161,7 @@ export const initModel = (schema: FormSchema[], formModel: Recordable) => {
   // 如果 schema 对应的 field 不存在,则删除 model 中的对应的 field
   for (let i = 0; i < schema.length; i++) {
     const key = schema[i].field
-    if (!get(model, key)) {
+    if (!get(model, key) && get(model, key) !== 0) {
       delete model[key]
     }
   }

+ 19 - 1
src/components/Menu/src/Menu.vue

@@ -98,7 +98,7 @@ export default defineComponent({
         >
           {{
             default: () => {
-              const { renderMenuItem } = useRenderMenuItem()
+              const { renderMenuItem } = useRenderMenuItem(menuMode)
               return renderMenuItem(unref(routers))
             }
           }}
@@ -257,4 +257,22 @@ export default defineComponent({
     }
   }
 }
+
+@submenu-prefix-cls: ~'@{adminNamespace}-submenu-popper';
+
+// 设置子菜单溢出时滚动样式
+.@{submenu-prefix-cls}--vertical {
+  max-height: 100%;
+  overflow-y: auto;
+
+  &::-webkit-scrollbar {
+    width: 6px;
+    background-color: transparent;
+  }
+
+  &::-webkit-scrollbar-thumb {
+    background-color: rgb(144 147 153 / 30%);
+    border-radius: 4px;
+  }
+}
 </style>

+ 11 - 2
src/components/Menu/src/components/useRenderMenuItem.tsx

@@ -1,12 +1,17 @@
 import { ElSubMenu, ElMenuItem } from 'element-plus'
+import { unref } from 'vue'
 import { hasOneShowingChild } from '../helper'
 import { isUrl } from '@/utils/is'
 import { useRenderMenuTitle } from './useRenderMenuTitle'
 import { pathResolve } from '@/utils/routerHelper'
+import { useDesign } from '@/hooks/web/useDesign'
+
+const { getPrefixCls } = useDesign()
+const prefixCls = getPrefixCls('submenu')
 
 const { renderMenuTitle } = useRenderMenuTitle()
 
-export const useRenderMenuItem = () =>
+export const useRenderMenuItem = (menuMode) =>
   // allRouters: AppRouteRecordRaw[] = [],
   {
     const renderMenuItem = (routers: AppRouteRecordRaw[], parentPath = '/') => {
@@ -33,7 +38,11 @@ export const useRenderMenuItem = () =>
             )
           } else {
             return (
-              <ElSubMenu index={fullPath}>
+              <ElSubMenu
+                index={fullPath}
+                teleported
+                popperClass={unref(menuMode) === 'vertical' ? `${prefixCls}-popper--vertical` : ''}
+              >
                 {{
                   title: () => renderMenuTitle(meta),
                   default: () => renderMenuItem(v.children!, fullPath)

+ 1 - 1
src/components/Table/src/components/ColumnSetting.vue

@@ -50,7 +50,7 @@ const handleCheckedColumnsChange = (value: string[]) => {
 const confirm = () => {
   const newColumns = cloneDeep(unref(settingColumns))?.map((item) => {
     const fixed = unref(settingColumns)?.find((col) => col.field === item.field)?.fixed
-    item.hidden = !!!unref(checkColumns)?.includes(item.field)
+    item.hidden = !unref(checkColumns)?.includes(item.field)
     item.fixed = fixed ? fixed : undefined
     return item
   })

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

@@ -514,7 +514,7 @@ watch(
 
   &__item {
     position: relative;
-    top: 2px;
+    top: 3px;
     height: calc(~'100% - 6px');
     padding-right: 25px;
     margin-left: 4px;

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

@@ -37,7 +37,7 @@ async function unLock() {
   if (!password.value) {
     return
   }
-  let pwd = password.value
+  const pwd = password.value
   try {
     loading.value = true
     const res = await lockStore.unLock(pwd)

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

@@ -21,7 +21,7 @@ const videoEl = ref<HTMLDivElement>()
 
 const intiPlayer = () => {
   if (!unref(videoEl)) return
-  new Player({
+  playerRef.value = new Player({
     autoplay: false,
     ...props,
     el: unref(videoEl)

+ 1 - 13
src/layout/Layout.vue

@@ -27,18 +27,15 @@ const handleClickOutside = () => {
 }
 
 const renderLayout = () => {
+  const { renderClassic, renderTopLeft, renderTop, renderCutMenu } = useRenderLayout()
   switch (unref(layout)) {
     case 'classic':
-      const { renderClassic } = useRenderLayout()
       return renderClassic()
     case 'topLeft':
-      const { renderTopLeft } = useRenderLayout()
       return renderTopLeft()
     case 'top':
-      const { renderTop } = useRenderLayout()
       return renderTop()
     case 'cutMenu':
-      const { renderCutMenu } = useRenderLayout()
       return renderCutMenu()
     default:
       break
@@ -73,14 +70,5 @@ export default defineComponent({
 
 .@{prefix-cls} {
   background-color: var(--app-content-bg-color);
-  .@{prefix-cls}-content-scrollbar {
-    & > :deep(.el-scrollbar__wrap) {
-      & > .@{elNamespace}-scrollbar__view {
-        display: flex;
-        height: 100% !important;
-        flex-direction: column;
-      }
-    }
-  }
 }
 </style>

+ 5 - 1
src/layout/components/AppView.vue

@@ -18,7 +18,11 @@ const getCaches = computed((): string[] => {
 <template>
   <section
     :class="[
-      'flex-1 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)]'
+      'box-border p-[var(--app-content-padding)] w-full bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]',
+      {
+        '!min-h-[calc(100vh-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))] pb-0':
+          footer
+      }
     ]"
   >
     <router-view>

+ 1 - 0
src/locales/en.ts

@@ -480,6 +480,7 @@ export default {
   },
   dialogDemo: {
     dialog: 'Dialog',
+    resizeDialog: 'Resize dialog',
     dialogDes: 'Secondary packaging of Dialog components based on ElementPlus',
     open: 'Open',
     close: 'Close',

+ 1 - 0
src/locales/zh-CN.ts

@@ -471,6 +471,7 @@ export default {
   },
   dialogDemo: {
     dialog: '弹窗',
+    resizeDialog: '可自定义调节弹窗大小的弹窗',
     dialogDes: '基于 ElementPlus 的 Dialog 组件二次封装',
     open: '打开',
     close: '关闭',

+ 8 - 0
src/styles/var.css

@@ -59,3 +59,11 @@
 .dark {
   --app-content-bg-color: var(--el-bg-color);
 }
+
+*,
+:after,
+:before {
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}

+ 0 - 1
src/utils/index.ts

@@ -54,7 +54,6 @@ export const getCssVar = (prop: string, dom = document.documentElement) => {
  * @param {Array} ary 查找的数组
  * @param {Functon} fn 判断的方法
  */
-// eslint-disable-next-line
 export const findIndex = <T = Recordable>(ary: Array<T>, fn: Fn): number => {
   if (ary.findIndex) {
     return ary.findIndex(fn)

+ 6 - 3
src/utils/is.ts

@@ -95,9 +95,12 @@ export const isServer = typeof window === 'undefined'
 export const isClient = !isServer
 
 export const isUrl = (path: string): boolean => {
-  const reg =
-    /(((^https?:(?:\/\/)?)(?:[-:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&%@.\w_]*)#?(?:[\w]*))?)$/
-  return reg.test(path)
+  try {
+    new URL(path)
+    return true
+  } catch (_error) {
+    return false
+  }
 }
 
 export const isDark = (): boolean => {

+ 1 - 0
src/views/Authorization/Menu/components/AddButtonPermission.vue

@@ -46,6 +46,7 @@ const confirm = async () => {
   })
   if (valid) {
     const formData = await getFormData()
+    formData.id = Date.now()
     emit('confirm', formData)
     modelValue.value = false
   }

+ 34 - 0
src/views/Components/Dialog.vue

@@ -8,6 +8,7 @@ import { useValidator } from '@/hooks/web/useValidator'
 import { getDictOneApi } from '@/api/common'
 import { useForm } from '@/hooks/web/useForm'
 import Echart from './Echart.vue'
+import ResizeDialog from '@/components/Dialog/src/ResizeDialog.vue'
 
 const { required } = useValidator()
 
@@ -17,6 +18,10 @@ const dialogVisible = ref(false)
 
 const dialogVisible2 = ref(false)
 
+const dialogVisible3 = ref(false)
+
+const dialogVisible4 = ref(false)
+
 const { formRegister, formMethods } = useForm()
 const { getElFormExpose } = formMethods
 
@@ -128,4 +133,33 @@ const formSubmit = async () => {
       </template>
     </Dialog>
   </ContentWrap>
+
+  <ContentWrap
+    class="mt-10px"
+    :title="t('dialogDemo.resizeDialog')"
+    :message="t('dialogDemo.dialogDes')"
+  >
+    <BaseButton type="primary" @click="dialogVisible3 = !dialogVisible3">
+      {{ t('dialogDemo.open') }}
+    </BaseButton>
+
+    <BaseButton type="primary" @click="dialogVisible4 = !dialogVisible4">
+      {{ t('dialogDemo.combineWithForm') }}
+    </BaseButton>
+
+    <ResizeDialog v-model="dialogVisible3" :title="t('dialogDemo.dialog')">
+      <Echart />
+      <template #footer>
+        <BaseButton @click="dialogVisible3 = false">{{ t('dialogDemo.close') }}</BaseButton>
+      </template>
+    </ResizeDialog>
+
+    <ResizeDialog v-model="dialogVisible4" :title="t('dialogDemo.dialog')">
+      <Form :schema="schema" @register="formRegister" />
+      <template #footer>
+        <BaseButton type="primary" @click="formSubmit">{{ t('dialogDemo.submit') }}</BaseButton>
+        <BaseButton @click="dialogVisible4 = false">{{ t('dialogDemo.close') }}</BaseButton>
+      </template>
+    </ResizeDialog>
+  </ContentWrap>
 </template>

+ 12 - 12
src/views/Components/Form/DefaultForm.vue

@@ -475,8 +475,8 @@ const schema = reactive<FormSchema[]>([
     label: `${t('formDemo.icon')}1`,
     component: 'Input',
     componentProps: {
-      suffixIcon: <Icon icon="ep:calendar" />,
-      prefixIcon: <Icon icon="ep:share" />
+      suffixIcon: <Icon icon="vi-ep:calendar" />,
+      prefixIcon: <Icon icon="vi-ep:share" />
     }
   },
   {
@@ -486,9 +486,9 @@ const schema = reactive<FormSchema[]>([
     componentProps: {
       slots: {
         suffix: () => {
-          return <Icon icon="ep:share" />
+          return <Icon icon="vi-ep:share" />
         },
-        prefix: () => <Icon icon="ep:calendar" />
+        prefix: () => <Icon icon="vi-ep:calendar" />
       }
     }
   },
@@ -498,8 +498,8 @@ const schema = reactive<FormSchema[]>([
     component: 'Input',
     componentProps: {
       slots: {
-        prepend: () => <Icon icon="ep:calendar" />,
-        append: () => <Icon icon="ep:share" />
+        prepend: () => <Icon icon="vi-ep:calendar" />,
+        append: () => <Icon icon="vi-ep:share" />
       }
     }
   },
@@ -636,7 +636,7 @@ const schema = reactive<FormSchema[]>([
             return null
           }
         },
-        prefix: () => <Icon icon="ep:calendar" />
+        prefix: () => <Icon icon="vi-ep:calendar" />
       }
     }
   },
@@ -901,11 +901,11 @@ const schema = reactive<FormSchema[]>([
     component: 'Rate',
     value: null,
     componentProps: {
-      voidIcon: <Icon icon="ep:chat-round" />,
+      voidIcon: <Icon icon="vi-ep:chat-round" />,
       icons: [
-        <Icon icon="ep:chat-round" />,
-        <Icon icon="ep:chat-line-round" />,
-        <Icon icon="ep:chat-dot-round" />
+        <Icon icon="vi-ep:chat-round" />,
+        <Icon icon="vi-ep:chat-line-round" />,
+        <Icon icon="vi-ep:chat-dot-round" />
       ]
     }
   },
@@ -1768,7 +1768,7 @@ const schema = reactive<FormSchema[]>([
     field: 'field88',
     component: 'IconPicker',
     label: t('formDemo.default'),
-    value: 'tdesign:archway'
+    value: 'vi-tdesign:archway'
   },
   {
     field: 'field89',

+ 1 - 1
src/views/Components/Table/CardTable.vue

@@ -15,7 +15,7 @@ const { t } = useI18n()
 
 const loading = ref(true)
 
-let tableDataList = ref<any[]>([])
+const tableDataList = ref<any[]>([])
 
 const getTableList = async (params?: Params) => {
   const res = await getCardTableListApi(

+ 1 - 1
src/views/Components/Table/DefaultTable.vue

@@ -68,7 +68,7 @@ const columns: TableColumn[] = [
 
 const loading = ref(true)
 
-let tableDataList = ref<TableData[]>([])
+const tableDataList = ref<TableData[]>([])
 
 const getTableList = async (params?: Params) => {
   const res = await getTableListApi(

+ 1 - 1
src/views/Components/Table/TableImagePreview.vue

@@ -54,7 +54,7 @@ const columns: TableColumn[] = [
 
 const loading = ref(true)
 
-let tableDataList = ref<TableData[]>([])
+const tableDataList = ref<TableData[]>([])
 
 const getTableList = async (params?: Params) => {
   const res = await getTableListApi(

+ 1 - 1
src/views/Components/Table/TableVideoPreview.vue

@@ -38,7 +38,7 @@ const columns: TableColumn[] = [
 
 const loading = ref(true)
 
-let tableDataList = ref<TableData[]>([])
+const tableDataList = ref<TableData[]>([])
 
 const getTableList = async (params?: Params) => {
   const res = await getTableListApi(

+ 1 - 1
src/views/Dashboard/Workplace.vue

@@ -66,7 +66,7 @@ const getTeam = async () => {
 }
 
 // 获取指数
-let radarOptionData = reactive<EChartsOption>(radarOption) as EChartsOption
+const radarOptionData = reactive<EChartsOption>(radarOption) as EChartsOption
 
 const getRadar = async () => {
   const res = await getRadarApi().catch(() => {})

+ 0 - 1
src/views/Example/Page/ExampleAdd.vue

@@ -49,4 +49,3 @@ const save = async () => {
     </template>
   </ContentDetailWrap>
 </template>
-@/hooks/event/useEventBus

+ 1 - 1
types/env.d.ts

@@ -2,7 +2,7 @@
 
 declare module '*.vue' {
   import { DefineComponent } from 'vue'
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
+
   const component: DefineComponent<{}, {}, any>
   export default component
 }

+ 2 - 0
vite.config.ts

@@ -61,6 +61,8 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
         : undefined,
       EslintPlugin({
         cache: false,
+        failOnWarning: false,
+        failOnError: false,
         include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件
       }),
       VueI18nPlugin({