Merge pull request #215 from yudaocode/upgrade
# Conflicts: # build/vite/optimize.ts # package-lock.json # package.json # pnpm-lock.yamlpull/878/head
commit
a3f89d686c
|
|
@ -1,8 +0,0 @@
|
|||
/build/
|
||||
/config/
|
||||
/dist/
|
||||
/*.js
|
||||
/test/unit/coverage/
|
||||
/node_modules/*
|
||||
/dist*
|
||||
/src/main.ts
|
||||
75
.eslintrc.js
75
.eslintrc.js
|
|
@ -1,75 +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',
|
||||
'@unocss'
|
||||
],
|
||||
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/require-toggle-inside-transition': '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',
|
||||
'prettier/prettier': 'off', // 芋艿:默认关闭 prettier 的 ESLint 校验,因为我们使用的是 IDE 的 Prettier 插件
|
||||
'@unocss/order': 'off', // 芋艿:禁用 unocss 【css】顺序的提示,因为暂时不需要这么严格,警告也有点繁琐
|
||||
'@unocss/order-attributify': 'off' // 芋艿:禁用 unocss 【属性】顺序的提示,因为暂时不需要这么严格,警告也有点繁琐
|
||||
}
|
||||
})
|
||||
|
|
@ -2,8 +2,7 @@ import { resolve } from 'path'
|
|||
import Vue from '@vitejs/plugin-vue'
|
||||
import VueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import progress from 'vite-plugin-progress'
|
||||
import EslintPlugin from 'vite-plugin-eslint'
|
||||
import PurgeIcons from 'vite-plugin-purge-icons'
|
||||
import EslintPlugin from 'vite-plugin-eslint2'
|
||||
import { ViteEjsPlugin } from 'vite-plugin-ejs'
|
||||
// @ts-ignore
|
||||
import ElementPlus from 'unplugin-element-plus/vite'
|
||||
|
|
@ -29,7 +28,6 @@ export function createVitePlugins() {
|
|||
VueJsx(),
|
||||
UnoCSS(),
|
||||
progress(),
|
||||
PurgeIcons(),
|
||||
ElementPlus({}),
|
||||
AutoImport({
|
||||
include: [
|
||||
|
|
@ -64,7 +62,7 @@ export function createVitePlugins() {
|
|||
dts: 'src/types/auto-components.d.ts',
|
||||
// 自定义组件的解析器
|
||||
resolvers: [ElementPlusResolver()],
|
||||
globs: ["src/components/**/**.{vue, md}", '!src/components/DiyEditor/components/mobile/**']
|
||||
globs: ['src/components/**/**.{vue, md}', '!src/components/DiyEditor/components/mobile/**']
|
||||
}),
|
||||
EslintPlugin({
|
||||
cache: false,
|
||||
|
|
@ -77,7 +75,7 @@ export function createVitePlugins() {
|
|||
}),
|
||||
createSvgIconsPlugin({
|
||||
iconDirs: [pathResolve('src/assets/svgs')],
|
||||
symbolId: 'icon-[dir]-[name]',
|
||||
symbolId: 'icon-[dir]-[name]'
|
||||
}),
|
||||
viteCompression({
|
||||
verbose: true, // 是否在控制台输出压缩结果
|
||||
|
|
|
|||
|
|
@ -2,22 +2,18 @@ const include = [
|
|||
'qs',
|
||||
'url',
|
||||
'vue',
|
||||
'sass',
|
||||
'mitt',
|
||||
'axios',
|
||||
'pinia',
|
||||
'dayjs',
|
||||
'qrcode',
|
||||
'unocss',
|
||||
'vue-router',
|
||||
'vue-types',
|
||||
'vue-i18n',
|
||||
'crypto-js',
|
||||
'cropperjs',
|
||||
'lodash-es',
|
||||
'nprogress',
|
||||
'web-storage-cache',
|
||||
'@iconify/iconify',
|
||||
'@vueuse/core',
|
||||
'@zxcvbn-ts/core',
|
||||
'echarts/core',
|
||||
|
|
@ -92,17 +88,8 @@ const include = [
|
|||
'element-plus/es/components/dropdown-item/style/css',
|
||||
'element-plus/es/components/skeleton/style/css',
|
||||
'element-plus/es/components/skeleton-item/style/css',
|
||||
'element-plus/es/components/backtop/style/css',
|
||||
'element-plus/es/components/menu/style/css',
|
||||
'element-plus/es/components/sub-menu/style/css',
|
||||
'element-plus/es/components/menu-item/style/css',
|
||||
'element-plus/es/components/dropdown/style/css',
|
||||
'element-plus/es/components/tree/style/css',
|
||||
'element-plus/es/components/dropdown-menu/style/css',
|
||||
'element-plus/es/components/dropdown-item/style/css',
|
||||
'element-plus/es/components/badge/style/css',
|
||||
'element-plus/es/components/breadcrumb/style/css',
|
||||
'element-plus/es/components/breadcrumb-item/style/css',
|
||||
'element-plus/es/components/image/style/css',
|
||||
'element-plus/es/components/collapse-transition/style/css',
|
||||
'element-plus/es/components/timeline/style/css',
|
||||
|
|
@ -119,6 +106,6 @@ const include = [
|
|||
'element-plus/es/components/progress/style/css'
|
||||
]
|
||||
|
||||
const exclude = ['@iconify/json']
|
||||
const exclude: string[] = []
|
||||
|
||||
export { include, exclude }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
import pluginVue from 'eslint-plugin-vue'
|
||||
import tseslint from 'typescript-eslint'
|
||||
import unocss from '@unocss/eslint-config/flat'
|
||||
import autoImportGlobals from './.eslintrc-auto-import.json' with { type: 'json' }
|
||||
|
||||
export default tseslint.config(
|
||||
// Global ignores (replaces .eslintignore)
|
||||
{
|
||||
ignores: [
|
||||
'build/',
|
||||
'config/',
|
||||
'dist/',
|
||||
'dist*/',
|
||||
'*.js',
|
||||
'*.mjs',
|
||||
'!eslint.config.mjs',
|
||||
'test/unit/coverage/',
|
||||
'node_modules/',
|
||||
'src/main.ts',
|
||||
'src/types/auto-components.d.ts'
|
||||
]
|
||||
},
|
||||
|
||||
// Base TypeScript config
|
||||
...tseslint.configs.recommended,
|
||||
|
||||
// Vue recommended config
|
||||
...pluginVue.configs['flat/recommended'],
|
||||
|
||||
// UnoCSS config
|
||||
unocss,
|
||||
|
||||
// Vue files use vue-eslint-parser with TypeScript parser
|
||||
{
|
||||
files: ['**/*.vue'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: tseslint.parser
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Main rules config
|
||||
{
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...autoImportGlobals.globals
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true
|
||||
}
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
// Vue rules
|
||||
'vue/no-reserved-component-names': 'off',
|
||||
'vue/custom-event-name-casing': '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/require-toggle-inside-transition': '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',
|
||||
|
||||
// TypeScript rules
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
'@typescript-eslint/no-unsafe-function-type': 'off',
|
||||
'@typescript-eslint/no-wrapper-object-types': 'off',
|
||||
'@typescript-eslint/no-this-alias': 'off',
|
||||
'@typescript-eslint/no-empty-object-type': 'off',
|
||||
'vue/no-ref-as-operand': 'off',
|
||||
'vue/no-mutating-props': 'off',
|
||||
'vue/no-side-effects-in-computed-properties': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'space-before-function-paren': 'off',
|
||||
|
||||
// UnoCSS rules - 芋艿:禁用 unocss 顺序提示
|
||||
'@unocss/order': 'off',
|
||||
'@unocss/order-attributify': 'off',
|
||||
'unocss/order': 'off',
|
||||
'unocss/order-attributify': 'off'
|
||||
}
|
||||
}
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
189
package.json
189
package.json
|
|
@ -8,144 +8,137 @@
|
|||
"i": "pnpm install",
|
||||
"dev": "vite --mode env.local",
|
||||
"dev-server": "vite --mode dev",
|
||||
"ts:check": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vue-tsc --noEmit",
|
||||
"build:local": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode env.local",
|
||||
"build:dev": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode dev",
|
||||
"build:test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode test",
|
||||
"build:stage": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode stage",
|
||||
"build:prod": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode prod",
|
||||
"ts:check": "node --max_old_space_size=8192 ./node_modules/vue-tsc/bin/vue-tsc.js --noEmit",
|
||||
"build:local": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode env.local",
|
||||
"build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode dev",
|
||||
"build:test": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode test",
|
||||
"build:stage": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode stage",
|
||||
"build:prod": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode prod",
|
||||
"serve:dev": "vite preview --mode dev",
|
||||
"serve:prod": "vite preview --mode prod",
|
||||
"preview": "pnpm build:local && vite preview",
|
||||
"clean": "npx rimraf node_modules",
|
||||
"clean:cache": "npx rimraf node_modules/.cache",
|
||||
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
|
||||
"lint:eslint": "eslint --fix ./src",
|
||||
"lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"",
|
||||
"lint:style": "stylelint --fix \"./src/**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
|
||||
"lint:lint-staged": "lint-staged -c "
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "2.3.2",
|
||||
"@form-create/designer": "^3.2.6",
|
||||
"@form-create/element-ui": "^3.2.11",
|
||||
"@iconify/iconify": "^3.1.1",
|
||||
"@form-create/designer": "^3.4.0",
|
||||
"@form-create/element-ui": "^3.2.38",
|
||||
"@iconify/vue": "^5.0.1",
|
||||
"@microsoft/fetch-event-source": "^2.0.1",
|
||||
"@videojs-player/vue": "^1.0.0",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"@wangeditor-next/editor": "^5.6.46",
|
||||
"@vueuse/core": "^14.3.0",
|
||||
"@wangeditor-next/editor": "^5.7.0",
|
||||
"@wangeditor-next/editor-for-vue": "^5.1.14",
|
||||
"@wangeditor-next/plugin-mention": "^1.0.16",
|
||||
"@wangeditor-next/plugin-mention": "^2.0.0",
|
||||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "1.9.0",
|
||||
"axios": "1.16.0",
|
||||
"benz-amr-recorder": "^1.1.5",
|
||||
"bpmn-js-token-simulation": "^0.36.0",
|
||||
"bpmn-js-token-simulation": "^0.39.3",
|
||||
"camunda-bpmn-moddle": "^7.0.1",
|
||||
"cropperjs": "^1.6.1",
|
||||
"cropperjs": "^2.1.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"dayjs": "^1.11.20",
|
||||
"dayjs-plugin-lunar": "^1.4.1",
|
||||
"dhtmlx-gantt": "^9.1.1",
|
||||
"diagram-js": "^12.8.0",
|
||||
"driver.js": "^1.3.1",
|
||||
"echarts": "^5.5.0",
|
||||
"diagram-js": "^15.14.0",
|
||||
"driver.js": "^1.4.0",
|
||||
"echarts": "^6.0.0",
|
||||
"echarts-wordcloud": "^2.1.0",
|
||||
"element-plus": "2.11.1",
|
||||
"element-plus": "2.13.7",
|
||||
"fast-xml-parser": "^4.3.2",
|
||||
"highlight.js": "^11.9.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"jsbarcode": "^3.12.3",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"jsoneditor": "^10.1.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markmap-common": "^0.16.0",
|
||||
"markmap-lib": "^0.16.1",
|
||||
"markmap-toolbar": "^0.17.0",
|
||||
"markmap-view": "^0.16.0",
|
||||
"min-dash": "^4.1.1",
|
||||
"jsencrypt": "^3.5.4",
|
||||
"jsoneditor": "^10.4.3",
|
||||
"lodash-es": "^4.18.1",
|
||||
"markdown-it": "^14.1.1",
|
||||
"markmap-common": "^0.18.9",
|
||||
"markmap-lib": "^0.18.12",
|
||||
"markmap-toolbar": "^0.18.12",
|
||||
"markmap-view": "^0.18.12",
|
||||
"min-dash": "^5.0.0",
|
||||
"mitt": "^3.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"qrcode": "^1.5.3",
|
||||
"qs": "^6.12.0",
|
||||
"snabbdom": "^3.6.2",
|
||||
"sortablejs": "^1.15.3",
|
||||
"pinia": "^3.0.4",
|
||||
"pinia-plugin-persistedstate": "^4.7.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"qs": "^6.15.1",
|
||||
"snabbdom": "^3.6.3",
|
||||
"sortablejs": "^1.15.7",
|
||||
"steady-xml": "^0.1.0",
|
||||
"tyme4ts": "^1.4.6",
|
||||
"url": "^0.11.3",
|
||||
"video.js": "^7.21.5",
|
||||
"vue": "3.5.12",
|
||||
"vue-dompurify-html": "^4.1.4",
|
||||
"vue-i18n": "9.10.2",
|
||||
"vue-router": "4.4.5",
|
||||
"vue-types": "^5.1.1",
|
||||
"url": "^0.11.4",
|
||||
"video.js": "^8.23.8",
|
||||
"vue": "3.5.34",
|
||||
"vue-dompurify-html": "^5.3.0",
|
||||
"vue-i18n": "11.4.0",
|
||||
"vue-router": "5.0.6",
|
||||
"vue-types": "^6.0.0",
|
||||
"vue3-print-nb": "^0.1.4",
|
||||
"vue3-signature": "^0.2.4",
|
||||
"vue3-signature": "^0.4.4",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"web-storage-cache": "^1.1.1",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.0.1",
|
||||
"@commitlint/config-conventional": "^19.0.0",
|
||||
"@iconify/json": "^2.2.187",
|
||||
"@intlify/unplugin-vue-i18n": "^2.0.0",
|
||||
"@purge-icons/generated": "^0.9.0",
|
||||
"@types/jsoneditor": "^9.9.5",
|
||||
"@commitlint/cli": "^20.5.3",
|
||||
"@commitlint/config-conventional": "^20.5.3",
|
||||
"@iconify/json": "^2.2.470",
|
||||
"@intlify/unplugin-vue-i18n": "^11.1.2",
|
||||
"@types/jsoneditor": "^9.9.6",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^20.11.21",
|
||||
"@types/node": "^25.6.0",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/qs": "^6.9.12",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||
"@typescript-eslint/parser": "^7.1.0",
|
||||
"@unocss/eslint-config": "^0.57.4",
|
||||
"@unocss/eslint-plugin": "66.1.0-beta.5",
|
||||
"@unocss/transformer-variant-group": "^0.58.5",
|
||||
"@vitejs/plugin-legacy": "^5.3.1",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"bpmn-js": "^17.9.2",
|
||||
"bpmn-js-properties-panel": "5.23.0",
|
||||
"consola": "^3.2.3",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-define-config": "^2.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-vue": "^9.22.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"postcss": "^8.4.35",
|
||||
"postcss-html": "^1.6.0",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"@types/qs": "^6.15.0",
|
||||
"@unocss/eslint-config": "^66.6.8",
|
||||
"@unocss/eslint-plugin": "66.6.8",
|
||||
"@unocss/transformer-variant-group": "^66.6.8",
|
||||
"@vitejs/plugin-legacy": "^8.0.1",
|
||||
"@vitejs/plugin-vue": "^6.0.6",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
||||
"autoprefixer": "^10.5.0",
|
||||
"bpmn-js": "^18.16.1",
|
||||
"bpmn-js-properties-panel": "5.54.0",
|
||||
"consola": "^3.4.2",
|
||||
"eslint": "^10.3.0",
|
||||
"eslint-plugin-vue": "^10.9.1",
|
||||
"lint-staged": "^16.4.0",
|
||||
"postcss": "^8.5.14",
|
||||
"postcss-html": "^1.8.1",
|
||||
"postcss-scss": "^4.0.9",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-eslint": "^16.3.0",
|
||||
"rimraf": "^5.0.5",
|
||||
"rollup": "^4.12.0",
|
||||
"sass": "^1.69.5",
|
||||
"stylelint": "^16.2.1",
|
||||
"prettier": "^3.8.3",
|
||||
"prettier-eslint": "^16.4.2",
|
||||
"rimraf": "^6.1.3",
|
||||
"rollup": "^4.60.3",
|
||||
"sass": "^1.99.0",
|
||||
"stylelint": "^17.11.0",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
"stylelint-config-recommended": "^14.0.0",
|
||||
"stylelint-config-standard": "^36.0.0",
|
||||
"stylelint-order": "^6.0.4",
|
||||
"terser": "^5.28.1",
|
||||
"typescript": "5.3.3",
|
||||
"unocss": "^0.58.5",
|
||||
"unplugin-auto-import": "^0.16.7",
|
||||
"unplugin-element-plus": "^0.8.0",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"vite": "5.1.4",
|
||||
"stylelint-config-recommended": "^18.0.0",
|
||||
"stylelint-config-standard": "^40.0.0",
|
||||
"stylelint-order": "^8.1.1",
|
||||
"terser": "^5.46.2",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "^8.59.2",
|
||||
"unocss": "^66.6.8",
|
||||
"unplugin-auto-import": "^21.0.0",
|
||||
"unplugin-element-plus": "^0.11.2",
|
||||
"unplugin-vue-components": "^32.0.0",
|
||||
"vite": "8.0.10",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-ejs": "^1.7.0",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-eslint2": "^5.1.0",
|
||||
"vite-plugin-progress": "^0.0.7",
|
||||
"vite-plugin-purge-icons": "^0.10.0",
|
||||
"vite-plugin-svg-icons-ng": "^1.3.1",
|
||||
"vite-plugin-top-level-await": "^1.4.4",
|
||||
"vue-eslint-parser": "^9.3.2",
|
||||
"vue-tsc": "^1.8.27"
|
||||
"vite-plugin-svg-icons-ng": "^1.9.0",
|
||||
"vite-plugin-top-level-await": "^1.6.0",
|
||||
"vue-eslint-parser": "^10.4.0",
|
||||
"vue-tsc": "^3.2.8"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
|
@ -158,7 +151,7 @@
|
|||
"homepage": "https://gitee.com/yudaocode/yudao-ui-admin-vue3",
|
||||
"web-types": "./web-types.json",
|
||||
"engines": {
|
||||
"node": ">= 16.0.0",
|
||||
"node": ">= 20.19.0",
|
||||
"pnpm": ">=8.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10985
pnpm-lock.yaml
10985
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -162,13 +162,24 @@ function handleReady(cropperInstance: Cropper) {
|
|||
}
|
||||
|
||||
function handlerToolbar(event: string, arg?: number) {
|
||||
if (event === 'scaleX') {
|
||||
scaleX = arg = scaleX === -1 ? 1 : -1
|
||||
if (!cropper.value) return
|
||||
const cropperImage = cropper.value.getCropperImage()
|
||||
const cropperSelection = cropper.value.getCropperSelection()
|
||||
|
||||
if (event === 'reset') {
|
||||
cropperImage?.$resetTransform()
|
||||
cropperSelection?.$reset()
|
||||
} else if (event === 'rotate') {
|
||||
cropperImage?.$rotate(`${arg}deg`)
|
||||
} else if (event === 'scaleX') {
|
||||
scaleX = scaleX === -1 ? 1 : -1
|
||||
cropperImage?.$scale(scaleX, 1)
|
||||
} else if (event === 'scaleY') {
|
||||
scaleY = scaleY === -1 ? 1 : -1
|
||||
cropperImage?.$scale(1, scaleY)
|
||||
} else if (event === 'zoom') {
|
||||
cropperImage?.$zoom(arg!)
|
||||
}
|
||||
if (event === 'scaleY') {
|
||||
scaleY = arg = scaleY === -1 ? 1 : -1
|
||||
}
|
||||
cropper?.value?.[event]?.(arg)
|
||||
}
|
||||
|
||||
async function handleOk() {
|
||||
|
|
@ -208,7 +219,8 @@ $prefix-cls: #{$namespace}-cropper-am;
|
|||
&-cropper {
|
||||
height: 300px;
|
||||
background: #eee;
|
||||
background-image: linear-gradient(
|
||||
background-image:
|
||||
linear-gradient(
|
||||
45deg,
|
||||
rgb(0 0 0 / 25%) 25%,
|
||||
transparent 0,
|
||||
|
|
|
|||
|
|
@ -1,50 +1,17 @@
|
|||
<template>
|
||||
<div :class="getClass" :style="getWrapperStyle">
|
||||
<img
|
||||
v-show="isReady"
|
||||
ref="imgElRef"
|
||||
:alt="alt"
|
||||
:crossorigin="crossorigin"
|
||||
:src="src"
|
||||
:style="getImageStyle"
|
||||
/>
|
||||
<div ref="containerRef" :class="getClass" :style="getWrapperStyle">
|
||||
<img v-show="false" ref="imgElRef" :alt="alt" :crossorigin="crossorigin" :src="src" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { CSSProperties, PropType } from 'vue'
|
||||
import Cropper from 'cropperjs'
|
||||
import 'cropperjs/dist/cropper.css'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
|
||||
defineOptions({ name: 'Cropper' })
|
||||
|
||||
type Options = Cropper.Options
|
||||
|
||||
const defaultOptions: Options = {
|
||||
aspectRatio: 1,
|
||||
zoomable: true,
|
||||
zoomOnTouch: true,
|
||||
zoomOnWheel: true,
|
||||
cropBoxMovable: true,
|
||||
cropBoxResizable: true,
|
||||
toggleDragModeOnDblclick: true,
|
||||
autoCrop: true,
|
||||
background: true,
|
||||
highlight: true,
|
||||
center: true,
|
||||
responsive: true,
|
||||
restore: true,
|
||||
checkCrossOrigin: true,
|
||||
checkOrientation: true,
|
||||
scalable: true,
|
||||
modal: true,
|
||||
guides: true,
|
||||
movable: true,
|
||||
rotatable: true
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
src: propTypes.string.def(''),
|
||||
alt: propTypes.string.def(''),
|
||||
|
|
@ -56,35 +23,21 @@ const props = defineProps({
|
|||
default: undefined
|
||||
},
|
||||
imageStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
|
||||
options: { type: Object as PropType<Options>, default: () => ({}) }
|
||||
options: { type: Object as PropType<Record<string, any>>, default: () => ({}) }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['cropend', 'ready', 'cropendError'])
|
||||
const attrs = useAttrs()
|
||||
const imgElRef = ref<ElRef<HTMLImageElement>>()
|
||||
const cropper = ref<Nullable<Cropper>>()
|
||||
const isReady = ref(false)
|
||||
const imgElRef = ref<HTMLImageElement>()
|
||||
const containerRef = ref<HTMLElement>()
|
||||
const cropper = ref<Cropper>()
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
const prefixCls = getPrefixCls('cropper-image')
|
||||
const debounceRealTimeCroppered = useDebounceFn(realTimeCroppered, 80)
|
||||
|
||||
const getImageStyle = computed((): CSSProperties => {
|
||||
return {
|
||||
height: props.height,
|
||||
maxWidth: '100%',
|
||||
...props.imageStyle
|
||||
}
|
||||
})
|
||||
|
||||
const getClass = computed(() => {
|
||||
return [
|
||||
prefixCls,
|
||||
attrs.class,
|
||||
{
|
||||
[`${prefixCls}--circled`]: props.circled
|
||||
}
|
||||
]
|
||||
return [prefixCls, attrs.class]
|
||||
})
|
||||
const getWrapperStyle = computed((): CSSProperties => {
|
||||
return { height: `${props.height}`.replace(/px/, '') + 'px' }
|
||||
|
|
@ -98,27 +51,39 @@ onUnmounted(() => {
|
|||
|
||||
async function init() {
|
||||
const imgEl = unref(imgElRef)
|
||||
if (!imgEl) {
|
||||
return
|
||||
}
|
||||
const containerEl = unref(containerRef)
|
||||
if (!imgEl || !containerEl) return
|
||||
|
||||
cropper.value = new Cropper(imgEl, {
|
||||
...defaultOptions,
|
||||
ready: () => {
|
||||
isReady.value = true
|
||||
realTimeCroppered()
|
||||
emit('ready', cropper.value)
|
||||
},
|
||||
crop() {
|
||||
debounceRealTimeCroppered()
|
||||
},
|
||||
zoom() {
|
||||
debounceRealTimeCroppered()
|
||||
},
|
||||
cropmove() {
|
||||
debounceRealTimeCroppered()
|
||||
},
|
||||
container: containerEl,
|
||||
...props.options
|
||||
})
|
||||
|
||||
// Wait for custom elements to be ready, then configure
|
||||
await nextTick()
|
||||
const cropperSelection = cropper.value.getCropperSelection()
|
||||
const cropperImage = cropper.value.getCropperImage()
|
||||
|
||||
if (cropperSelection) {
|
||||
cropperSelection.initialCoverage = 0.5
|
||||
cropperSelection.aspectRatio = 1
|
||||
cropperSelection.movable = true
|
||||
cropperSelection.resizable = true
|
||||
cropperSelection.addEventListener('change', () => {
|
||||
debounceRealTimeCroppered()
|
||||
})
|
||||
}
|
||||
|
||||
if (cropperImage) {
|
||||
cropperImage.addEventListener('transform', () => {
|
||||
debounceRealTimeCroppered()
|
||||
})
|
||||
// Emit ready once image loads
|
||||
cropperImage.addEventListener('load', () => {
|
||||
emit('ready', cropper.value)
|
||||
realTimeCroppered()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Real-time display preview
|
||||
|
|
@ -127,33 +92,45 @@ function realTimeCroppered() {
|
|||
}
|
||||
|
||||
// event: return base64 and width and height information after cropping
|
||||
function croppered() {
|
||||
if (!cropper.value) {
|
||||
return
|
||||
async function croppered() {
|
||||
if (!cropper.value) return
|
||||
|
||||
const selection = cropper.value.getCropperSelection()
|
||||
if (!selection) return
|
||||
|
||||
const imgInfo = {
|
||||
x: selection.x,
|
||||
y: selection.y,
|
||||
width: selection.width,
|
||||
height: selection.height
|
||||
}
|
||||
let imgInfo = cropper.value.getData()
|
||||
const canvas = props.circled ? getRoundedCanvas() : cropper.value.getCroppedCanvas()
|
||||
canvas.toBlob((blob) => {
|
||||
if (!blob) {
|
||||
return
|
||||
|
||||
try {
|
||||
let canvas = await selection.$toCanvas()
|
||||
if (props.circled) {
|
||||
canvas = getRoundedCanvas(canvas)
|
||||
}
|
||||
let fileReader: FileReader = new FileReader()
|
||||
fileReader.readAsDataURL(blob)
|
||||
fileReader.onloadend = (e) => {
|
||||
emit('cropend', {
|
||||
imgBase64: e.target?.result ?? '',
|
||||
imgInfo
|
||||
})
|
||||
}
|
||||
fileReader.onerror = () => {
|
||||
emit('cropendError')
|
||||
}
|
||||
}, 'image/png')
|
||||
canvas.toBlob((blob) => {
|
||||
if (!blob) return
|
||||
const fileReader = new FileReader()
|
||||
fileReader.readAsDataURL(blob)
|
||||
fileReader.onloadend = (e) => {
|
||||
emit('cropend', {
|
||||
imgBase64: e.target?.result ?? '',
|
||||
imgInfo
|
||||
})
|
||||
}
|
||||
fileReader.onerror = () => {
|
||||
emit('cropendError')
|
||||
}
|
||||
}, 'image/png')
|
||||
} catch {
|
||||
// Selection may not be ready yet
|
||||
}
|
||||
}
|
||||
|
||||
// Get a circular picture canvas
|
||||
function getRoundedCanvas() {
|
||||
const sourceCanvas = cropper.value!.getCroppedCanvas()
|
||||
function getRoundedCanvas(sourceCanvas: HTMLCanvasElement) {
|
||||
const canvas = document.createElement('canvas')
|
||||
const context = canvas.getContext('2d')!
|
||||
const width = sourceCanvas.width
|
||||
|
|
@ -169,15 +146,3 @@ function getRoundedCanvas() {
|
|||
return canvas
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
$prefix-cls: #{$namespace}-cropper-image;
|
||||
|
||||
.#{$prefix-cls} {
|
||||
&--circled {
|
||||
.cropper-view-box,
|
||||
.cropper-face {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type Cropper from 'cropperjs'
|
|||
|
||||
export interface CropendResult {
|
||||
imgBase64: string
|
||||
imgInfo: Cropper.Data
|
||||
imgInfo: { x: number; y: number; width: number; height: number }
|
||||
}
|
||||
|
||||
export type { Cropper }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import Iconify from '@purge-icons/generated'
|
||||
import { Icon as IconifyIcon } from '@iconify/vue'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
|
||||
defineOptions({ name: 'Icon' })
|
||||
|
|
@ -20,57 +20,16 @@ const props = defineProps({
|
|||
svgClass: propTypes.string.def('')
|
||||
})
|
||||
|
||||
const elRef = ref<ElRef>(null)
|
||||
|
||||
const isLocal = computed(() => props.icon?.startsWith('svg-icon:'))
|
||||
|
||||
const symbolId = computed(() => {
|
||||
return unref(isLocal) ? `#icon-${props.icon.split('svg-icon:')[1]}` : props.icon
|
||||
})
|
||||
|
||||
const getIconifyStyle = computed(() => {
|
||||
const { color, size } = props
|
||||
return {
|
||||
fontSize: `${size}px`,
|
||||
height: '1em',
|
||||
color
|
||||
}
|
||||
})
|
||||
|
||||
const getSvgClass = computed(() => {
|
||||
const { svgClass } = props
|
||||
return `iconify ${svgClass}`
|
||||
})
|
||||
|
||||
const updateIcon = async (icon: string) => {
|
||||
if (unref(isLocal)) return
|
||||
|
||||
const el = unref(elRef)
|
||||
if (!el) return
|
||||
|
||||
await nextTick()
|
||||
|
||||
if (!icon) return
|
||||
|
||||
const svg = Iconify.renderSVG(icon, {})
|
||||
if (svg) {
|
||||
el.textContent = ''
|
||||
el.appendChild(svg)
|
||||
} else {
|
||||
const span = document.createElement('span')
|
||||
span.className = 'iconify'
|
||||
span.dataset.icon = icon
|
||||
el.textContent = ''
|
||||
el.appendChild(span)
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.icon,
|
||||
(icon: string) => {
|
||||
updateIcon(icon)
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -79,8 +38,11 @@ watch(
|
|||
<use :xlink:href="symbolId" />
|
||||
</svg>
|
||||
|
||||
<span v-else ref="elRef" :class="$attrs.class" :style="getIconifyStyle">
|
||||
<span :class="getSvgClass" :data-icon="symbolId"></span>
|
||||
</span>
|
||||
<IconifyIcon
|
||||
v-else
|
||||
:icon="symbolId"
|
||||
:class="getSvgClass"
|
||||
:style="{ fontSize: `${size}px`, color }"
|
||||
/>
|
||||
</ElIcon>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export const useUpload = (directory?: string) => {
|
|||
const uploadProgressHandler = (evt: AxiosProgressEvent) => {
|
||||
const upEvt: UploadProgressEvent = Object.assign(evt.event)
|
||||
upEvt.percent = evt.progress ? evt.progress * 100 : 0
|
||||
options.onProgress(upEvt) // 触发 el-upload 的 on-progress
|
||||
options.onProgress?.(upEvt) // 触发 el-upload 的 on-progress
|
||||
}
|
||||
|
||||
// 模式一:前端上传
|
||||
|
|
|
|||
|
|
@ -47,6 +47,12 @@ import { setupWangEditorPlugin } from '@/views/bpm/model/form/PrintTemplate'
|
|||
|
||||
import print from 'vue3-print-nb' // 打印插件
|
||||
|
||||
// 处理 Vite 预加载模块失败(如重新构建后 chunk 哈希变化),自动刷新页面
|
||||
window.addEventListener('vite:preloadError', (event) => {
|
||||
event.preventDefault()
|
||||
window.location.reload()
|
||||
})
|
||||
|
||||
// 创建实例
|
||||
const setupAll = async () => {
|
||||
const app = createApp(App)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,10 @@
|
|||
import 'virtual:svg-icons-register'
|
||||
|
||||
import '@purge-icons/generated'
|
||||
import { addCollection } from '@iconify/vue'
|
||||
import epIcons from '@iconify/json/json/ep.json'
|
||||
import faIcons from '@iconify/json/json/fa.json'
|
||||
import faSolidIcons from '@iconify/json/json/fa-solid.json'
|
||||
|
||||
addCollection(epIcons)
|
||||
addCollection(faIcons)
|
||||
addCollection(faSolidIcons)
|
||||
|
|
|
|||
|
|
@ -19,8 +19,18 @@ const router = createRouter({
|
|||
}
|
||||
})
|
||||
|
||||
// 处理动态导入失败(如重新构建后 chunk 哈希变化),自动跳转到目标页面
|
||||
router.onError((error, to) => {
|
||||
if (
|
||||
error.message.includes('Failed to fetch dynamically imported module') ||
|
||||
error.message.includes('Importing a module script failed')
|
||||
) {
|
||||
window.location.assign(to.fullPath)
|
||||
}
|
||||
})
|
||||
|
||||
export const resetRouter = (): void => {
|
||||
const resetWhiteNameList = ['Redirect', 'Login', 'NoFound', 'Home']
|
||||
const resetWhiteNameList = ['Redirect', 'RedirectRoot', 'Login', 'NoFound', 'Home']
|
||||
router.getRoutes().forEach((route) => {
|
||||
const { name } = route
|
||||
if (name && !resetWhiteNameList.includes(name as string)) {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||
{
|
||||
path: '/redirect',
|
||||
component: Layout,
|
||||
name: 'Redirect',
|
||||
name: 'RedirectRoot',
|
||||
children: [
|
||||
{
|
||||
path: '/redirect/:path(.*)',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { RouteLocationNormalized, Router, RouteRecordNormalized } from 'vue-router'
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw, RouterView } from 'vue-router'
|
||||
import { isUrl } from '@/utils/is'
|
||||
import { cloneDeep, omit } from 'lodash-es'
|
||||
import qs from 'qs'
|
||||
|
|
@ -21,14 +22,16 @@ export const registerComponent = (componentPath: string) => {
|
|||
/* Layout */
|
||||
export const Layout = () => import('@/layout/Layout.vue')
|
||||
|
||||
export const getParentLayout = () => {
|
||||
return () =>
|
||||
new Promise((resolve) => {
|
||||
resolve({
|
||||
name: 'ParentLayout'
|
||||
})
|
||||
})
|
||||
}
|
||||
// 嵌套动态目录只需要透传子路由;不能再次渲染完整 Layout,否则内容区会重复出现顶部栏和侧边栏。
|
||||
// vue-router 5 对 component 更严格,这里必须返回带 render 的真实组件,不能只返回 { name: 'ParentLayout' }。
|
||||
const ParentLayout = defineComponent({
|
||||
name: 'ParentLayout',
|
||||
setup() {
|
||||
return () => h(RouterView)
|
||||
}
|
||||
})
|
||||
|
||||
export const getParentLayout = () => ParentLayout
|
||||
|
||||
// 按照路由中meta下的rank等级升序来排序路由
|
||||
export const ascending = (arr: any[]) => {
|
||||
|
|
@ -98,7 +101,7 @@ export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecord
|
|||
meta: meta
|
||||
}
|
||||
//处理顶级非目录路由
|
||||
if (!route.children && route.parentId == 0 && route.component) {
|
||||
if (!route.children && Number(route.parentId) === 0 && route.component) {
|
||||
data.component = Layout
|
||||
data.meta = {
|
||||
hidden: meta.hidden
|
||||
|
|
@ -123,7 +126,8 @@ export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecord
|
|||
} else {
|
||||
// 目录
|
||||
if (route.children?.length) {
|
||||
data.component = Layout
|
||||
// 顶级目录承载后台整体框架;非顶级目录只作为 router-view 占位,避免多级菜单嵌套 Layout。
|
||||
data.component = Number(route.parentId) === 0 ? Layout : getParentLayout()
|
||||
data.redirect = getRedirect(route.path, route.children)
|
||||
// 外链
|
||||
} else if (isUrl(route.path)) {
|
||||
|
|
@ -145,12 +149,77 @@ export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecord
|
|||
}
|
||||
if (route.children) {
|
||||
data.children = generateRoute(route.children)
|
||||
// vue-router 5 要求路由 name 全局唯一;后端菜单可能生成父子同名,例如 /mall/trade/delivery/express。
|
||||
// 父路由改名后,如果同名子是叶子页面,则把页面 component 折叠到父路由,保持原 URL 可访问。
|
||||
const sameNameChild = findDescendantRouteByName(data.children, data.name)
|
||||
if (sameNameChild) {
|
||||
data.name = `${data.name}Parent`
|
||||
if (!sameNameChild.children?.length && sameNameChild.component) {
|
||||
// 只移除被折叠的同名子,保留其它兄弟节点,避免后续菜单扩展时丢路由。
|
||||
const remainingChildren = removeDescendantRoute(data.children, sameNameChild)
|
||||
data.component = sameNameChild.component
|
||||
data.redirect = sameNameChild.redirect
|
||||
data.children = remainingChildren
|
||||
data.meta = {
|
||||
...data.meta,
|
||||
alwaysShow: remainingChildren.length > 0 ? data.meta.alwaysShow : false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
res.push(data as AppRouteRecordRaw)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* 在已生成的子路由树里查找第一个同名后代路由。
|
||||
*
|
||||
* vue-router 5 要求 name 全局唯一;后端菜单可能生成“父目录”和“默认子页面”同名的结构。
|
||||
* 调用方会用返回的同名叶子节点做折叠处理,所以这里返回原始节点引用,供 removeDescendantRoute 精确移除。
|
||||
*/
|
||||
const findDescendantRouteByName = (
|
||||
routes: AppRouteRecordRaw[] | undefined,
|
||||
name: string
|
||||
): AppRouteRecordRaw | undefined => {
|
||||
for (const route of routes || []) {
|
||||
if (route.name === name) {
|
||||
return route
|
||||
}
|
||||
const child = findDescendantRouteByName(route.children, name)
|
||||
if (child) {
|
||||
return child
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从路由树中移除指定节点,并保留其它兄弟节点和子树。
|
||||
*
|
||||
* target 来自 findDescendantRouteByName,和 routes 属于同一棵对象树,因此可以用引用相等精确定位。
|
||||
* 不按 name 删除,是为了避免误删其它层级里可能同名但不应该折叠的节点。
|
||||
*/
|
||||
const removeDescendantRoute = (
|
||||
routes: AppRouteRecordRaw[] | undefined,
|
||||
target: AppRouteRecordRaw
|
||||
): AppRouteRecordRaw[] => {
|
||||
return (routes || []).flatMap((route) => {
|
||||
if (route === target) {
|
||||
return []
|
||||
}
|
||||
if (!route.children?.length) {
|
||||
return [route]
|
||||
}
|
||||
return [
|
||||
{
|
||||
...route,
|
||||
children: removeDescendantRoute(route.children, target)
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
export const getRedirect = (parentPath: string, children: AppCustomRouteRecordRaw[]) => {
|
||||
if (!children || children.length == 0) {
|
||||
return parentPath
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"ignoreDeprecations": "6.0",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
declare module 'virtual:svg-icons-register'
|
||||
|
|
@ -41,21 +41,20 @@ export default ({command, mode}: ConfigEnv): UserConfig => {
|
|||
// 项目使用的vite插件。 单独提取到build/vite/plugin中管理
|
||||
plugins: createVitePlugins(),
|
||||
css: {
|
||||
lightningcss: {
|
||||
// Preserve legacy star-hack declarations by stripping invalid syntax during minification.
|
||||
errorRecovery: true
|
||||
},
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: '@use "@/styles/variables.scss" as *;',
|
||||
javascriptEnabled: true,
|
||||
silenceDeprecations: ["legacy-js-api"], // 参考自 https://stackoverflow.com/questions/78997907/the-legacy-js-api-is-deprecated-and-will-be-removed-in-dart-sass-2-0-0
|
||||
additionalData: `@use "${pathResolve('src/styles/variables.scss')}" as *;`,
|
||||
api: 'modern-compiler'
|
||||
}
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.scss', '.css'],
|
||||
alias: [
|
||||
{
|
||||
find: 'vue-i18n',
|
||||
replacement: 'vue-i18n/dist/vue-i18n.cjs.js'
|
||||
},
|
||||
{
|
||||
find: /\@\//,
|
||||
replacement: `${pathResolve('src')}/`
|
||||
|
|
@ -75,10 +74,12 @@ export default ({command, mode}: ConfigEnv): UserConfig => {
|
|||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
echarts: ['echarts'], // 将 echarts 单独打包,参考 https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues/IAB1SX 讨论
|
||||
'form-create': ['@form-create/element-ui'], // 参考 https://github.com/yudaocode/yudao-ui-admin-vue3/issues/148 讨论
|
||||
'form-designer': ['@form-create/designer'],
|
||||
codeSplitting: {
|
||||
groups: [
|
||||
{ name: 'echarts', test: /node_modules[\\/]echarts[\\/]/ }, // 将 echarts 单独打包,参考 https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues/IAB1SX 讨论
|
||||
{ name: 'form-create', test: /node_modules[\\/]@form-create[\\/]element-ui[\\/]/ }, // 参考 https://github.com/yudaocode/yudao-ui-admin-vue3/issues/148 讨论
|
||||
{ name: 'form-designer', test: /node_modules[\\/]@form-create[\\/]designer[\\/]/ }
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue