diff --git a/.gitignore b/.gitignore index 3399f39c0..df1f37a8a 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,10 @@ vite.config.ts.* *.sw? .history .cursor + +# AI +.agent +.agents +.claude +.codex +skills-lock.json diff --git a/.npmrc b/.npmrc index aeac1ae91..6d28fabf4 100644 --- a/.npmrc +++ b/.npmrc @@ -1,8 +1,8 @@ registry=https://registry.npmmirror.com public-hoist-pattern[]=lefthook public-hoist-pattern[]=eslint -public-hoist-pattern[]=prettier -public-hoist-pattern[]=prettier-plugin-tailwindcss +public-hoist-pattern[]=oxfmt +public-hoist-pattern[]=oxlint public-hoist-pattern[]=stylelint public-hoist-pattern[]=*postcss* public-hoist-pattern[]=@commitlint/* diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index d0b0ca133..000000000 --- a/.prettierignore +++ /dev/null @@ -1,18 +0,0 @@ -dist -dev-dist -.local -.output.js -node_modules -.nvmrc -coverage -CODEOWNERS -.nitro -.output - - -**/*.svg -**/*.sh - -public -.npmrc -*-lock.yaml diff --git a/.prettierrc.mjs b/.prettierrc.mjs deleted file mode 100644 index 3e25d2cfa..000000000 --- a/.prettierrc.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/prettier-config'; diff --git a/.stylelintignore b/.stylelintignore index f4b2db2c1..3adb33b22 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -2,3 +2,7 @@ dist public __tests__ coverage +.codex +.claude +.agent +.agents diff --git a/.vscode/extensions.json b/.vscode/extensions.json index e8dc9ed9b..d4e28662b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,14 +2,18 @@ "recommendations": [ // Vue 3 的语言支持 "Vue.volar", - // 将 ESLint JavaScript 集成到 VS Code 中。 + // 将 eslint 集成到 VS Code 中。 "dbaeumer.vscode-eslint", + // 将 oxlint 集成到 VS Code 中。 + "oxc.oxc-vscode", // Visual Studio Code 的官方 Stylelint 扩展 "stylelint.vscode-stylelint", - // 使用 Prettier 的代码格式化程序 - "esbenp.prettier-vscode", + // 使用 oxfmt 的代码格式化程序 + "oxc.oxc-vscode", // 支持 dotenv 文件语法 "mikestead.dotenv", + // YAML 语言支持,供 ESLint 校验 pnpm-workspace.yaml 等文件 + "redhat.vscode-yaml", // 源代码的拼写检查器 "streetsidesoftware.code-spell-checker", // Tailwind CSS 的官方 VS Code 插件 diff --git a/.vscode/settings.json b/.vscode/settings.json index 588357bba..ae1f5c929 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { - "tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts", + "tailwindCSS.experimental.configFile": "internal/tailwind-config/src/theme.css", + "tailwindCSS.lint.suggestCanonicalClasses": "ignore", // workbench "workbench.list.smoothScrolling": true, "workbench.startupEditor": "newUntitledFile", @@ -31,39 +32,51 @@ "editor.autoClosingOvertype": "always", "editor.autoClosingQuotes": "beforeWhitespace", "editor.wordSeparators": "`~!@#%^&*()=+[{]}\\|;:'\",.<>/?", + "editor.quickSuggestions": { + "strings": "on" + }, + + // lint && format + "oxc.enable": true, + "oxc.typeAware": true, + "oxc.configPath": "oxlint.config.ts", + "oxc.fmt.configPath": "oxfmt.config.ts", + "eslint.useFlatConfig": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit", + "source.fixAll.oxc": "explicit", "source.fixAll.stylelint": "explicit", "source.organizeImports": "never" }, - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "oxc.oxc-vscode", "[html]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[css]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[scss]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[json]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[markdown]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[jsonc]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, "[vue]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "oxc.oxc-vscode" }, + // extensions "extensions.ignoreRecommendations": true, @@ -79,6 +92,7 @@ "files.insertFinalNewline": true, "files.simpleDialog.enable": true, "files.associations": { + "*.css": "tailwindcss", "*.ejs": "html", "*.art": "html", "**/tsconfig.json": "jsonc", @@ -118,7 +132,7 @@ // search "search.searchEditor.singleClickBehaviour": "peekDefinition", "search.followSymlinks": false, - // 在使用搜索功能时,将这些文件夹/文件排除在外 + // 使用搜索功能时,将这些文件和文件夹排除在外 "search.exclude": { "**/node_modules": true, "**/*.log": true, @@ -159,7 +173,7 @@ "emmet.triggerExpansionOnTab": false, "errorLens.enabledDiagnosticLevels": ["warning", "error"], - "errorLens.excludeBySource": ["cSpell", "Grammarly", "eslint"], + "errorLens.excludeBySource": ["cSpell", "Grammarly"], "stylelint.enable": true, "stylelint.packageManager": "pnpm", @@ -193,7 +207,7 @@ "*": false }, - "cssVariables.lookupFiles": ["packages/core/base/design/src/**/*.css"], + "cssVariables.lookupFiles": ["packages/@core/base/design/src/**/*.css"], "i18n-ally.localesPaths": [ "packages/locales/src/langs", @@ -218,12 +232,10 @@ "*.env": "$(capture).env.*", "README.md": "README*,CHANGELOG*,LICENSE,CNAME", "package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json", - "eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml", - "tailwind.config.mjs": "postcss.*" + "oxlint.config.ts": ".eslintignore,.stylelintignore,.commitlintrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml,oxfmt.config.*,eslint.config.*" }, "commentTranslate.hover.enabled": false, "commentTranslate.multiLineMerge": true, "vue.server.hybridMode": true, - "typescript.tsdk": "node_modules/typescript/lib", - "oxc.enable": false + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/README.md b/README.md index a6939ad52..3e8f10586 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## 🐶 新手必读 -- nodejs > v20.19.0 | v22 | v24 && pnpm > 10.20.0 (强制使用pnpm) +- nodejs >= v20.19.0(推荐 v22 / v24) && pnpm >= 10.32.1(强制使用 pnpm) - 演示地址【Vue3 + element-plus】: - 演示地址【Vue3 + vben5(ant-design-vue)】: - 演示地址【Vue2 + element-ui】: @@ -20,12 +20,12 @@ **芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。 -- 采用最新 [vue-vben-admin](https://github.com/vbenjs/vue-vben-admin) v5 实现 +- 采用最新 [vue-vben-admin](https://github.com/vbenjs/vue-vben-admin) v5.7.0 实现 - 支持 [Ant Design Vue](https://www.antdv.com/) | [Element Plus](https://element-plus.org/zh-CN/) | [Naive UI](https://www.naiveui.com/) | [TDesign](https://tdesign.tencent.com/) 多种免费开源的中后台模版,具备如下特性: ![首页](.gitee/image/demo/vben.png) -- **最新技术栈**:使用 Vue3、Vite7 等前端前沿技术开发 +- **最新技术栈**:使用 Vue3、Vite8 等前端前沿技术开发 - **TypeScript**: 应用程序级 JavaScript 的语言 - **主题**: 提供多套主题色彩,可配置自定义主题 - **国际化**:内置完善的国际化方案 @@ -41,24 +41,24 @@ | 框架 | 说明 | 版本 | | --- | --- | --- | -| [Vue](https://staging-cn.vuejs.org/) | vue框架 | 3.5.27 | -| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 7.3.1 | +| [Vue](https://staging-cn.vuejs.org/) | vue框架 | 3.5.30 | +| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 8.0.0 | | [Ant Design Vue](https://www.antdv.com/) | Ant Design Vue | 4.2.6 | -| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.13.1 | -| [Naive UI](https://www.naiveui.com/) | Naive UI | 2.43.2 | -| [TDesign](https://tdesign.tencent.com/) | TDesign | 1.18.0 | +| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.13.5 | +| [Naive UI](https://www.naiveui.com/) | Naive UI | 2.44.1 | +| [TDesign](https://tdesign.tencent.com/) | TDesign | 1.18.5 | | [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 超集 | 5.9.3 | | [pinia](https://pinia.vuejs.org/) | Vue 存储库替代 vuex5 | 3.0.4 | -| [vueuse](https://vueuse.org/) | 常用工具集 | 14.1.0 | -| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 11.2.8 | -| [vue-router](https://router.vuejs.org/) | Vue 路由 | 4.6.4 | -| [Tailwind CSS](https://tailwindcss.com/) | 原子 CSS | 3.4.19 | +| [vueuse](https://vueuse.org/) | 常用工具集 | 14.2.1 | +| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 11.3.0 | +| [vue-router](https://router.vuejs.org/) | Vue 路由 | 5.0.3 | +| [Tailwind CSS](https://tailwindcss.com/) | 原子 CSS | 4.2.1 | | [Iconify](https://iconify.design/) | 图标组件 | 5.0.0 | -| [Iconify](https://icon-sets.iconify.design/) | 在线图标库 | 2.2.431 | +| [Iconify](https://icon-sets.iconify.design/) | 在线图标库 | 2.2.449 | | [TinyMCE](https://www.tiny.cloud/) | 富文本编辑器 | 7.3.0 | | [Echarts](https://echarts.apache.org/) | 图表库 | 6.0.0 | -| [axios](https://axios-http.com/) | http客户端 | 1.13.2 | -| [dayjs](https://day.js.org/) | 日期处理库 | 1.11.19 | +| [axios](https://axios-http.com/) | http客户端 | 1.13.6 | +| [dayjs](https://day.js.org/) | 日期处理库 | 1.11.20 | | [vee-validate](https://vee-validate.logaretm.com/) | 表单验证 | 4.15.1 | | [zod](https://zod.dev/) | 数据验证 | 3.25.76 | diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index 75506ad31..904f69a77 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-antd", - "version": "5.6.0", + "version": "5.7.0", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/apps/web-antd/postcss.config.mjs b/apps/web-antd/postcss.config.mjs deleted file mode 100644 index 3d8070455..000000000 --- a/apps/web-antd/postcss.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config/postcss'; diff --git a/apps/web-antd/public/tinymce/tinymce.d.ts b/apps/web-antd/public/tinymce/tinymce.d.ts index 20f888b56..a87b1629a 100644 --- a/apps/web-antd/public/tinymce/tinymce.d.ts +++ b/apps/web-antd/public/tinymce/tinymce.d.ts @@ -2537,12 +2537,12 @@ interface EditorSelection { normalize: () => Range; selectorChanged: (selector: string, callback: (active: boolean, args: { node: Node; - selector: String; + selector: string; parents: Node[]; }) => void) => EditorSelection; selectorChangedWithUnbind: (selector: string, callback: (active: boolean, args: { node: Node; - selector: String; + selector: string; parents: Node[]; }) => void) => { unbind: () => void; @@ -3217,9 +3217,9 @@ interface Tools { (arr: ArrayLike | null | undefined, cb: ArrayCallback): R[]; (obj: Record | null | undefined, cb: ObjCallback): R[]; }; - extend: (obj: Object, ext: Object, ...objs: Object[]) => any; + extend: (obj: object, ext: object, ...objs: object[]) => any; walk: >(obj: T, f: WalkCallback, n?: keyof T, scope?: any) => void; - resolve: (path: string, o?: Object) => any; + resolve: (path: string, o?: object) => any; explode: (s: string | string[], d?: string | RegExp) => string[]; _addCacheSuffix: (url: string) => string; } diff --git a/apps/web-antd/src/adapter/component/index.ts b/apps/web-antd/src/adapter/component/index.ts index 5bac14d67..def7ff2f9 100644 --- a/apps/web-antd/src/adapter/component/index.ts +++ b/apps/web-antd/src/adapter/component/index.ts @@ -14,6 +14,7 @@ import type { import type { Component, Ref } from 'vue'; import type { BaseFormComponentType } from '@vben/common-ui'; +import type { Sortable } from '@vben/hooks'; import type { Recordable } from '@vben/types'; import { @@ -21,6 +22,9 @@ import { defineAsyncComponent, defineComponent, h, + nextTick, + onMounted, + onUnmounted, ref, render, unref, @@ -33,6 +37,7 @@ import { IconPicker, VCropper, } from '@vben/common-ui'; +import { useSortable } from '@vben/hooks'; import { IconifyIcon } from '@vben/icons'; import { $t } from '@vben/locales'; import { isEmpty } from '@vben/utils'; @@ -132,260 +137,261 @@ const withDefaultPlaceholder = ( }); }; -const withPreviewUpload = () => { - // 检查是否为图片文件的辅助函数 - const isImageFile = (file: UploadFile): boolean => { - const imageExtensions = new Set([ - 'bmp', - 'gif', - 'jpeg', - 'jpg', - 'png', - 'svg', - 'webp', - ]); - if (file.url) { - try { - const pathname = new URL(file.url, 'http://localhost').pathname; - const ext = pathname.split('.').pop()?.toLowerCase(); - return ext ? imageExtensions.has(ext) : false; - } catch { - const ext = file.url?.split('.').pop()?.toLowerCase(); - return ext ? imageExtensions.has(ext) : false; - } +const IMAGE_EXTENSIONS = new Set([ + 'bmp', + 'gif', + 'jpeg', + 'jpg', + 'png', + 'svg', + 'webp', +]); + +/** + * 检查是否为图片文件 + */ +function isImageFile(file: UploadFile): boolean { + if (file.url) { + try { + const pathname = new URL(file.url, 'http://localhost').pathname; + const ext = pathname.split('.').pop()?.toLowerCase(); + return ext ? IMAGE_EXTENSIONS.has(ext) : false; + } catch { + const ext = file.url?.split('.').pop()?.toLowerCase(); + return ext ? IMAGE_EXTENSIONS.has(ext) : false; } - if (!file.type) { - const ext = file.name?.split('.').pop()?.toLowerCase(); - return ext ? imageExtensions.has(ext) : false; - } - return file.type.startsWith('image/'); + } + if (!file.type) { + const ext = file.name?.split('.').pop()?.toLowerCase(); + return ext ? IMAGE_EXTENSIONS.has(ext) : false; + } + return file.type.startsWith('image/'); +} + +/** + * 创建默认的上传按钮插槽 + */ +function createDefaultUploadSlots(listType: string, placeholder: string) { + if (listType === 'picture-card') { + return { default: () => placeholder }; + } + return { + default: () => + h( + Button, + { + icon: h(IconifyIcon, { + icon: 'ant-design:upload-outlined', + class: 'mb-1 size-4', + }), + }, + () => placeholder, + ), }; - // 创建默认的上传按钮插槽 - const createDefaultSlotsWithUpload = ( - listType: string, - placeholder: string, - ) => { - switch (listType) { - case 'picture-card': { - return { - default: () => placeholder, - }; - } - default: { - return { - default: () => - h( - Button, - { - icon: h(IconifyIcon, { - icon: 'ant-design:upload-outlined', - class: 'mb-1 size-4', - }), +} + +/** + * 获取文件的 Base64 + */ +function getBase64(file: File): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.addEventListener('load', () => resolve(reader.result as string)); + reader.addEventListener('error', reject); + }); +} + +/** + * 预览图片 + */ +async function previewImage( + file: UploadFile, + visible: Ref, + fileList: Ref, +) { + // 非图片文件直接打开链接 + if (!isImageFile(file)) { + const url = file.url || file.preview; + if (url) { + window.open(url, '_blank'); + } else { + message.error($t('ui.formRules.previewWarning')); + } + return; + } + + const [ImageComponent, PreviewGroupComponent] = await Promise.all([ + Image, + PreviewGroup, + ]); + + // 过滤图片文件并生成预览 + const imageFiles = (unref(fileList) || []).filter((f) => isImageFile(f)); + + for (const imgFile of imageFiles) { + if (!imgFile.url && !imgFile.preview && imgFile.originFileObj) { + imgFile.preview = await getBase64(imgFile.originFileObj); + } + } + + const container = document.createElement('div'); + document.body.append(container); + let isUnmounted = false; + + const currentIndex = imageFiles.findIndex((f) => f.uid === file.uid); + + const PreviewWrapper = { + setup() { + return () => { + if (isUnmounted) return null; + return h( + PreviewGroupComponent, + { + class: 'hidden', + preview: { + visible: visible.value, + current: currentIndex, + onVisibleChange: (value: boolean) => { + visible.value = value; + if (!value) { + setTimeout(() => { + if (!isUnmounted && container) { + isUnmounted = true; + render(null, container); + container.remove(); + } + }, 300); + } }, - () => placeholder, + }, + }, + () => + imageFiles.map((imgFile) => + h(ImageComponent, { + key: imgFile.uid, + src: imgFile.url || imgFile.preview, + }), ), - }; - } - } + ); + }; + }, }; - // 构建预览图片组 - const previewImage = async ( - file: UploadFile, - visible: Ref, - fileList: Ref, - ) => { - // 如果当前文件不是图片,直接打开 - if (!isImageFile(file)) { - if (file.url) { - window.open(file.url, '_blank'); - } else if (file.preview) { - window.open(file.preview, '_blank'); - } else { - message.error($t('ui.formRules.previewWarning')); - } - return; - } - // 对于图片文件,继续使用预览组 - const [ImageComponent, PreviewGroupComponent] = await Promise.all([ - Image, - PreviewGroup, - ]); + render(h(PreviewWrapper), container); +} - const getBase64 = (file: File) => { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.readAsDataURL(file); - reader.addEventListener('load', () => resolve(reader.result)); - reader.addEventListener('error', (error) => reject(error)); - }); - }; - // 从fileList中过滤出所有图片文件 - const imageFiles = (unref(fileList) || []).filter((element) => - isImageFile(element), - ); - - // 为所有没有预览地址的图片生成预览 - for (const imgFile of imageFiles) { - if (!imgFile.url && !imgFile.preview && imgFile.originFileObj) { - imgFile.preview = (await getBase64(imgFile.originFileObj)) as string; - } - } - const container: HTMLElement | null = document.createElement('div'); +/** + * 图片裁剪操作 + */ +function cropImage(file: File, aspectRatio: string | undefined) { + return new Promise((resolve, reject) => { + const container = document.createElement('div'); document.body.append(container); - // 用于追踪组件是否已卸载 let isUnmounted = false; + let objectUrl: null | string = null; - const PreviewWrapper = { + const open = ref(true); + const cropperRef = ref | null>(null); + + const closeModal = () => { + open.value = false; + setTimeout(() => { + if (!isUnmounted && container) { + if (objectUrl) { + URL.revokeObjectURL(objectUrl); + } + isUnmounted = true; + render(null, container); + container.remove(); + } + }, 300); + }; + + const CropperWrapper = { setup() { return () => { if (isUnmounted) return null; + if (!objectUrl) { + objectUrl = URL.createObjectURL(file); + } return h( - PreviewGroupComponent, + Modal, { - class: 'hidden', - preview: { - visible: visible.value, - // 设置初始显示的图片索引 - current: imageFiles.findIndex((f) => f.uid === file.uid), - onVisibleChange: (value: boolean) => { - visible.value = value; - if (!value) { - // 延迟清理,确保动画完成 - setTimeout(() => { - if (!isUnmounted && container) { - isUnmounted = true; - render(null, container); - container.remove(); - } - }, 300); + open: open.value, + title: h('div', {}, [ + $t('ui.crop.title'), + h( + 'span', + { + class: `${aspectRatio ? '' : 'hidden'} ml-2 text-sm text-gray-400 font-normal`, + }, + $t('ui.crop.titleTip', [aspectRatio]), + ), + ]), + centered: true, + width: 548, + keyboard: false, + maskClosable: false, + closable: false, + cancelText: $t('common.cancel'), + okText: $t('ui.crop.confirm'), + destroyOnClose: true, + onOk: async () => { + const cropper = cropperRef.value; + if (!cropper) { + reject(new Error('Cropper not found')); + closeModal(); + return; + } + try { + const dataUrl = await cropper.getCropImage(); + if (dataUrl) { + resolve(dataUrl); + } else { + reject(new Error($t('ui.crop.errorTip'))); } - }, + } catch { + reject(new Error($t('ui.crop.errorTip'))); + } finally { + closeModal(); + } + }, + onCancel() { + resolve(''); + closeModal(); }, }, () => - // 渲染所有图片文件 - imageFiles.map((imgFile) => - h(ImageComponent, { - key: imgFile.uid, - src: imgFile.url || imgFile.preview, - }), - ), + h(VCropper, { + ref: (ref: any) => (cropperRef.value = ref), + img: objectUrl as string, + aspectRatio, + }), ); }; }, }; - render(h(PreviewWrapper), container); - }; - - // 图片裁剪操作 - const cropImage = (file: File, aspectRatio: string | undefined) => { - return new Promise((resolve, reject) => { - const container: HTMLElement | null = document.createElement('div'); - document.body.append(container); - - // 用于追踪组件是否已卸载 - let isUnmounted = false; - let objectUrl: null | string = null; - - const open = ref(true); - const cropperRef = ref | null>(null); - - const closeModal = () => { - open.value = false; - // 延迟清理,确保动画完成 - setTimeout(() => { - if (!isUnmounted && container) { - if (objectUrl) { - URL.revokeObjectURL(objectUrl); - } - isUnmounted = true; - render(null, container); - container.remove(); - } - }, 300); - }; - - const CropperWrapper = { - setup() { - return () => { - if (isUnmounted) return null; - if (!objectUrl) { - objectUrl = URL.createObjectURL(file); - } - return h( - Modal, - { - open: open.value, - title: h('div', {}, [ - $t('ui.crop.title'), - h( - 'span', - { - class: `${aspectRatio ? '' : 'hidden'} ml-2 text-sm text-gray-400 font-normal`, - }, - $t('ui.crop.titleTip', [aspectRatio]), - ), - ]), - centered: true, - width: 548, - keyboard: false, - maskClosable: false, - closable: false, - cancelText: $t('common.cancel'), - okText: $t('ui.crop.confirm'), - destroyOnClose: true, - onOk: async () => { - const cropper = cropperRef.value; - if (!cropper) { - reject(new Error('Cropper not found')); - closeModal(); - return; - } - try { - const dataUrl = await cropper.getCropImage(); - resolve(dataUrl); - } catch { - reject(new Error($t('ui.crop.errorTip'))); - } finally { - closeModal(); - } - }, - onCancel() { - resolve(''); - closeModal(); - }, - }, - () => - h(VCropper, { - ref: (ref: any) => (cropperRef.value = ref), - img: objectUrl as string, - aspectRatio, - }), - ); - }; - }, - }; - - render(h(CropperWrapper), container); - }); - }; + render(h(CropperWrapper), container); + }); +} +/** + * 带预览功能的上传组件 + */ +const withPreviewUpload = () => { return defineComponent({ name: Upload.name, emits: ['update:modelValue'], - setup: ( + setup( props: any, { attrs, slots, emit }: { attrs: any; emit: any; slots: any }, - ) => { + ) { const previewVisible = ref(false); - - const placeholder = attrs?.placeholder || $t(`ui.placeholder.upload`); - + const placeholder = attrs?.placeholder || $t('ui.placeholder.upload'); const listType = attrs?.listType || attrs?.['list-type'] || 'text'; - const fileList = ref( attrs?.fileList || attrs?.['file-list'] || [], ); @@ -399,12 +405,14 @@ const withPreviewUpload = () => { file: UploadFile, originFileList: Array, ) => { + // 文件大小限制 if (maxSize.value && (file.size || 0) / 1024 / 1024 > maxSize.value) { message.error($t('ui.formRules.sizeLimit', [maxSize.value])); file.status = 'removed'; return false; } - // 多选或者非图片不唤起裁剪框 + + // 图片裁剪处理 if ( attrs.crop && !attrs.multiple && @@ -412,14 +420,11 @@ const withPreviewUpload = () => { isImageFile(file) ) { file.status = 'removed'; - // antd Upload组件问题 file参数获取的是UploadFile类型对象无法取到File类型 所以通过originFileList[0]获取 const blob = await cropImage(originFileList[0], aspectRatio.value); - return new Promise((resolve, reject) => { - if (!blob) { - return reject(new Error($t('ui.crop.errorTip'))); - } - resolve(blob); - }); + if (!blob) { + throw new Error($t('ui.crop.errorTip')); + } + return blob; } return attrs.beforeUpload?.(file) ?? true; @@ -427,12 +432,9 @@ const withPreviewUpload = () => { const handleChange = (event: UploadChangeParam) => { try { - // 行内写法 handleChange: (event) => {} attrs.handleChange?.(event); - // template写法 @handle-change="(event) => {}" attrs.onHandleChange?.(event); } catch (error) { - // Avoid breaking internal v-model sync on user handler errors console.error(error); } fileList.value = event.fileList.filter( @@ -449,21 +451,88 @@ const withPreviewUpload = () => { await previewImage(file, previewVisible, fileList); }; - const renderUploadButton = (): any => { - const isDisabled = attrs.disabled; - - // 如果禁用,不渲染上传按钮 - if (isDisabled) { - return null; - } - - // 否则渲染默认上传按钮 + const renderUploadButton = () => { + if (attrs.disabled) return null; return isEmpty(slots) - ? createDefaultSlotsWithUpload(listType, placeholder) + ? createDefaultUploadSlots(listType, placeholder) : slots; }; - // 可以监听到表单API设置的值 + // 拖拽排序 + const draggable = computed( + () => (attrs.draggable ?? false) && !attrs.disabled, + ); + const uploadId = `upload-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; + const sortableInstance = ref(null); + + const styleId = `upload-drag-style-${uploadId}`; + + function injectDragStyle() { + if (!document.querySelector(`[id="${styleId}"]`)) { + const style = document.createElement('style'); + style.id = styleId; + style.textContent = ` + [data-upload-id="${uploadId}"] .ant-upload-list-item { cursor: move; } + [data-upload-id="${uploadId}"] .ant-upload-list-item:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.15); } + `; + document.head.append(style); + } + } + + function removeDragStyle() { + document.querySelector(`[id="${styleId}"]`)?.remove(); + } + + async function initSortable(retryCount = 0) { + if (!draggable.value) return; + + injectDragStyle(); + await nextTick(); + await new Promise((resolve) => setTimeout(resolve, 100)); + + const container = document.querySelector( + `[data-upload-id="${uploadId}"] .ant-upload-list`, + ) as HTMLElement; + + if (!container) { + if (retryCount < 5) { + setTimeout(() => initSortable(retryCount + 1), 200); + } + return; + } + + const { initializeSortable } = useSortable(container, { + animation: 300, + delay: 400, + delayOnTouchOnly: true, + filter: + '.ant-upload-select, .ant-upload-list-item-error, .ant-upload-list-item-uploading', + onEnd: (evt) => { + const { oldIndex, newIndex } = evt; + if ( + oldIndex === undefined || + newIndex === undefined || + oldIndex === newIndex + ) { + return; + } + + const list = [...(fileList.value || [])]; + const [movedItem] = list.splice(oldIndex, 1); + if (movedItem) { + list.splice(newIndex, 0, movedItem); + fileList.value = list; + } + + attrs.onDragSort?.(oldIndex, newIndex); + emit('update:modelValue', fileList.value); + }, + }); + + sortableInstance.value = await initializeSortable(); + } + + // 监听表单值变化 watch( () => attrs.modelValue, (res) => { @@ -471,18 +540,28 @@ const withPreviewUpload = () => { }, ); + onMounted(initSortable); + onUnmounted(() => { + sortableInstance.value?.destroy(); + removeDragStyle(); + }); + return () => h( - Upload, - { - ...props, - ...attrs, - fileList: fileList.value, - beforeUpload: handleBeforeUpload, - onChange: handleChange, - onPreview: handlePreview, - }, - renderUploadButton(), + 'div', + { 'data-upload-id': uploadId, class: 'w-full' }, + h( + Upload, + { + ...props, + ...attrs, + fileList: fileList.value, + beforeUpload: handleBeforeUpload, + onChange: handleChange, + onPreview: handlePreview, + }, + renderUploadButton() as any, + ), ); }, }); diff --git a/apps/web-antd/src/adapter/vxe-table.ts b/apps/web-antd/src/adapter/vxe-table.ts index 15ab9212a..9bf5d0d04 100644 --- a/apps/web-antd/src/adapter/vxe-table.ts +++ b/apps/web-antd/src/adapter/vxe-table.ts @@ -199,7 +199,7 @@ setupVbenVxeTable({ vxeUI.renderer.add('CellOperation', { renderTableDefault({ attrs, options, props }, { column, row }) { const defaultProps = { size: 'small', type: 'link', ...props }; - let align = 'end'; + let align: string; switch (column.align) { case 'center': { align = 'center'; diff --git a/apps/web-antd/src/api/crm/customer/limitConfig/index.ts b/apps/web-antd/src/api/crm/customer/limitConfig/index.ts index f99a35f31..102e98db3 100644 --- a/apps/web-antd/src/api/crm/customer/limitConfig/index.ts +++ b/apps/web-antd/src/api/crm/customer/limitConfig/index.ts @@ -16,10 +16,10 @@ export namespace CrmCustomerLimitConfigApi { /** 客户限制配置类型 */ export enum LimitConfType { - /** 锁定客户数限制 */ - CUSTOMER_LOCK_LIMIT = 2, /** 拥有客户数限制 */ CUSTOMER_QUANTITY_LIMIT = 1, + /** 锁定客户数限制 */ + CUSTOMER_LOCK_LIMIT = 2, } /** 查询客户限制配置列表 */ diff --git a/apps/web-antd/src/api/crm/permission/index.ts b/apps/web-antd/src/api/crm/permission/index.ts index 296e442f0..b95e142b1 100644 --- a/apps/web-antd/src/api/crm/permission/index.ts +++ b/apps/web-antd/src/api/crm/permission/index.ts @@ -35,11 +35,11 @@ export namespace CrmPermissionApi { * CRM 业务类型枚举 */ export enum BizTypeEnum { - CRM_BUSINESS = 4, // 商机 CRM_CLUE = 1, // 线索 - CRM_CONTACT = 3, // 联系人 - CRM_CONTRACT = 5, // 合同 CRM_CUSTOMER = 2, // 客户 + CRM_CONTACT = 3, // 联系人 + CRM_BUSINESS = 4, // 商机 + CRM_CONTRACT = 5, // 合同 CRM_PRODUCT = 6, // 产品 CRM_RECEIVABLE = 7, // 回款 CRM_RECEIVABLE_PLAN = 8, // 回款计划 diff --git a/apps/web-antd/src/api/iot/device/modbus/point/index.ts b/apps/web-antd/src/api/iot/device/modbus/point/index.ts index fa5c6767d..11b440fae 100644 --- a/apps/web-antd/src/api/iot/device/modbus/point/index.ts +++ b/apps/web-antd/src/api/iot/device/modbus/point/index.ts @@ -37,16 +37,12 @@ export function getModbusPoint(id: number) { } /** 创建 Modbus 点位配置 */ -export function createModbusPoint( - data: IotDeviceModbusPointApi.ModbusPoint, -) { +export function createModbusPoint(data: IotDeviceModbusPointApi.ModbusPoint) { return requestClient.post('/iot/device-modbus-point/create', data); } /** 更新 Modbus 点位配置 */ -export function updateModbusPoint( - data: IotDeviceModbusPointApi.ModbusPoint, -) { +export function updateModbusPoint(data: IotDeviceModbusPointApi.ModbusPoint) { return requestClient.put('/iot/device-modbus-point/update', data); } diff --git a/apps/web-antd/src/api/request.ts b/apps/web-antd/src/api/request.ts index 2568def87..15539ab93 100644 --- a/apps/web-antd/src/api/request.ts +++ b/apps/web-antd/src/api/request.ts @@ -119,7 +119,9 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { response.data = apiEncrypt.decryptResponse(response.data); } catch (error) { console.error('响应数据解密失败:', error); - throw new Error(`响应数据解密失败: ${(error as Error).message}`); + throw new Error(`响应数据解密失败: ${(error as Error).message}`, { + cause: error, + }); } } return response; diff --git a/apps/web-antd/src/components/description/use-description.ts b/apps/web-antd/src/components/description/use-description.ts index fd24920f0..e3b676af2 100644 --- a/apps/web-antd/src/components/description/use-description.ts +++ b/apps/web-antd/src/components/description/use-description.ts @@ -21,7 +21,7 @@ export function useDescription(options?: Partial) { inheritAttrs: false, setup(_props, { attrs, slots }) { return () => { - // @ts-ignore - 避免类型实例化过深 + // @ts-expect-error - 避免类型实例化过深 return h(Description, { ...propsState, ...attrs }, slots); }; }, diff --git a/apps/web-antd/src/components/form-create/components/area-select.vue b/apps/web-antd/src/components/form-create/components/area-select.vue index 46141f385..9fa8d5b05 100644 --- a/apps/web-antd/src/components/form-create/components/area-select.vue +++ b/apps/web-antd/src/components/form-create/components/area-select.vue @@ -47,6 +47,7 @@ interface Props { clearable?: boolean; showAllLevels?: boolean; separator?: string; + // eslint-disable-next-line vue/require-default-prop formCreateInject?: any; } @@ -71,7 +72,7 @@ async function loadAreaTree(): Promise { const data = await getAreaTree(); // 根据 level 限制层级 - areaTree.value = filterTreeByLevel(data || [], props.level); + areaTree.value = filterTreeByLevel((data || []) as AreaVO[], props.level); } catch (error) { console.warn('[AreaSelect] 加载地区数据失败:', error); areaTree.value = []; @@ -100,7 +101,7 @@ function filterTreeByLevel(tree: AreaVO[], maxLevel: number): AreaVO[] { } // 处理选中值变化 -function handleChange(value: number[] | undefined): void { +function handleChange(value: any): void { if (value === undefined || value === null) { emit('update:modelValue', undefined); emit('update:value', undefined); diff --git a/apps/web-antd/src/components/form-create/components/dept-select.vue b/apps/web-antd/src/components/form-create/components/dept-select.vue index 98fa25490..45c153f85 100644 --- a/apps/web-antd/src/components/form-create/components/dept-select.vue +++ b/apps/web-antd/src/components/form-create/components/dept-select.vue @@ -39,15 +39,16 @@ interface DeptVO { status?: number; } -// TODO @puhui999:linter 报错; /** 接受父组件参数 */ interface Props { + // eslint-disable-next-line vue/require-default-prop modelValue?: number | number[] | string | string[]; multiple?: boolean; returnType?: 'id' | 'name'; defaultCurrentDept?: boolean; disabled?: boolean; placeholder?: string; + // eslint-disable-next-line vue/require-default-prop formCreateInject?: any; } diff --git a/apps/web-antd/src/components/form-create/components/iframe.vue b/apps/web-antd/src/components/form-create/components/iframe.vue index 7fd4ff9c0..04c1acc20 100644 --- a/apps/web-antd/src/components/form-create/components/iframe.vue +++ b/apps/web-antd/src/components/form-create/components/iframe.vue @@ -29,6 +29,7 @@ interface Props { allowfullscreen?: boolean; loading?: 'eager' | 'lazy'; sandbox?: string; + // eslint-disable-next-line vue/require-default-prop formCreateInject?: any; } diff --git a/apps/web-antd/src/components/form-create/components/use-api-select.tsx b/apps/web-antd/src/components/form-create/components/use-api-select.tsx index dba12f326..023869350 100644 --- a/apps/web-antd/src/components/form-create/components/use-api-select.tsx +++ b/apps/web-antd/src/components/form-create/components/use-api-select.tsx @@ -193,7 +193,8 @@ export function useApiSelect(option: ApiSelectProps) { let parse: any = null; if (props.parseFunc) { // 解析字符串函数 - // eslint-disable-next-line no-new-func + // oxlint-disable-next-line typescript/no-implied-eval + // oxlint-disable-next-line no-new-func, typescript/no-implied-eval parse = new Function(`return ${props.parseFunc}`)(); } return parse; diff --git a/apps/web-antd/src/components/form-create/helpers.ts b/apps/web-antd/src/components/form-create/helpers.ts index af08c6617..12d07a68a 100644 --- a/apps/web-antd/src/components/form-create/helpers.ts +++ b/apps/web-antd/src/components/form-create/helpers.ts @@ -11,14 +11,14 @@ import formCreate from '@form-create/ant-design-vue'; import { apiSelectRule } from '#/components/form-create/rules/data'; import { + useAreaSelectRule, useDictSelectRule, useEditorRule, + useIframeRule, useSelectRule, useUploadFileRule, useUploadImageRule, useUploadImagesRule, - useIframeRule, - useAreaSelectRule, } from './rules'; /** 编码表单 Conf */ diff --git a/apps/web-antd/src/components/form-create/rules/data.ts b/apps/web-antd/src/components/form-create/rules/data.ts index edef44508..e6f659c7d 100644 --- a/apps/web-antd/src/components/form-create/rules/data.ts +++ b/apps/web-antd/src/components/form-create/rules/data.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-template-curly-in-string */ const selectRule = [ { type: 'select', @@ -134,7 +133,7 @@ const apiSelectRule = [ type: 'input', field: 'labelField', title: 'label 属性', - info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}', + info: `可以使用 el 表达式:\${属性},来实现复杂数据组合。如:\${nickname}-\${id}`, props: { placeholder: 'nickname', }, @@ -143,7 +142,7 @@ const apiSelectRule = [ type: 'input', field: 'valueField', title: 'value 属性', - info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}', + info: `可以使用 el 表达式:\${属性},来实现复杂数据组合。如:\${nickname}-\${id}`, props: { placeholder: 'id', }, diff --git a/apps/web-antd/src/components/markdown-view/markdown-view.vue b/apps/web-antd/src/components/markdown-view/markdown-view.vue index 373957a69..a0e527f1b 100644 --- a/apps/web-antd/src/components/markdown-view/markdown-view.vue +++ b/apps/web-antd/src/components/markdown-view/markdown-view.vue @@ -47,6 +47,7 @@ onMounted(async () => { diff --git a/apps/web-antd/src/components/upload/image-upload.vue b/apps/web-antd/src/components/upload/image-upload.vue index b78d8f15d..7f0a62c1e 100644 --- a/apps/web-antd/src/components/upload/image-upload.vue +++ b/apps/web-antd/src/components/upload/image-upload.vue @@ -146,6 +146,7 @@ async function handlePreview(file: UploadFile) { async function handleRemove(file: UploadFile) { if (fileList.value) { const index = fileList.value.findIndex((item) => item.uid === file.uid); + // oxlint-disable-next-line no-unused-expressions index !== -1 && fileList.value.splice(index, 1); const value = getValue(); isInnerOperate.value = true; @@ -350,6 +351,8 @@ function getValue() { diff --git a/apps/web-antd/src/store/auth.ts b/apps/web-antd/src/store/auth.ts index dcef71175..7d09f0ec4 100644 --- a/apps/web-antd/src/store/auth.ts +++ b/apps/web-antd/src/store/auth.ts @@ -83,6 +83,7 @@ export const useAuthStore = defineStore('auth', () => { if (accessStore.loginExpired) { accessStore.setLoginExpired(false); } else { + // oxlint-disable-next-line no-unused-expressions onSuccess ? await onSuccess?.() : await router.push( @@ -132,6 +133,7 @@ export const useAuthStore = defineStore('auth', () => { async function fetchUserInfo() { // 加载 + // eslint-disable-next-line no-useless-assignment let authPermissionInfo: AuthPermissionInfo | null = null; authPermissionInfo = await getAuthPermissionInfoApi(); // userStore diff --git a/apps/web-antd/src/views/ai/chat/index/index.vue b/apps/web-antd/src/views/ai/chat/index/index.vue index 14440a3aa..4a42447c0 100644 --- a/apps/web-antd/src/views/ai/chat/index/index.vue +++ b/apps/web-antd/src/views/ai/chat/index/index.vue @@ -571,6 +571,7 @@ onMounted(async () => { size="small" @click="openChatConversationUpdateForm" > + diff --git a/apps/web-antd/src/views/ai/image/index/index.vue b/apps/web-antd/src/views/ai/image/index/index.vue index 16fdb5ba6..5ddc5c8bf 100644 --- a/apps/web-antd/src/views/ai/image/index/index.vue +++ b/apps/web-antd/src/views/ai/image/index/index.vue @@ -21,6 +21,7 @@ const imageListRef = ref(); // image 列表 ref const dall3Ref = ref(); // dall3(openai) ref const midjourneyRef = ref(); // midjourney ref const stableDiffusionRef = ref(); // stable diffusion ref +// @ts-expect-error: template ref is retained for future provider expansion const commonRef = ref(); // stable diffusion ref const selectPlatform = ref('common'); // 选中的平台 @@ -45,7 +46,9 @@ const platformOptions = [ const models = ref([]); // 模型列表 /** 绘画 start */ -async function handleDrawStart() {} +function handleDrawStart() { + // drawing state is handled by child components +} /** 绘画 complete */ async function handleDrawComplete() { diff --git a/apps/web-antd/src/views/ai/mindmap/index/modules/right.vue b/apps/web-antd/src/views/ai/mindmap/index/modules/right.vue index a2537f393..3e03e3735 100644 --- a/apps/web-antd/src/views/ai/mindmap/index/modules/right.vue +++ b/apps/web-antd/src/views/ai/mindmap/index/modules/right.vue @@ -150,10 +150,12 @@ defineExpose({ ref="mdContainerRef" class="wh-full overflow-y-auto" > +
+
('currentSong', {}); {{ currentSong.date }}
+
diff --git a/apps/web-antd/src/views/ai/workflow/form/modules/workflow-design.vue b/apps/web-antd/src/views/ai/workflow/form/modules/workflow-design.vue index 962fec60e..176b81555 100644 --- a/apps/web-antd/src/views/ai/workflow/form/modules/workflow-design.vue +++ b/apps/web-antd/src/views/ai/workflow/form/modules/workflow-design.vue @@ -106,7 +106,9 @@ async function goRun() { try { convertedParams[paramKey] = convertParamValue(value, dataType); } catch (error: any) { - throw new Error(`参数 ${paramKey} 转换失败: ${error.message}`); + throw new Error(`参数 ${paramKey} 转换失败: ${error.message}`, { + cause: error, + }); } } @@ -175,7 +177,7 @@ function convertParamValue(value: string, dataType: string) { try { return JSON.parse(value); } catch (error: any) { - throw new Error(`JSON格式错误: ${error.message}`); + throw new Error(`JSON格式错误: ${error.message}`, { cause: error }); } } default: { diff --git a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessDesigner.vue b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessDesigner.vue index 14fdf8539..add772d32 100644 --- a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessDesigner.vue +++ b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessDesigner.vue @@ -22,7 +22,7 @@ import { import { Button, ButtonGroup, message, Modal, Tooltip } from 'ant-design-vue'; // 模拟流转流程 -// @ts-ignore +// @ts-expect-error: token simulation package does not ship compatible types import tokenSimulation from 'bpmn-js-token-simulation'; import BpmnModeler from 'bpmn-js/lib/Modeler'; // 代码高亮插件 @@ -132,6 +132,7 @@ const emit = defineEmits([ 'element-click', ]); +// @ts-expect-error: file input ref is set imperatively by the template const bpmnCanvas = ref(); const refFile = ref(); @@ -178,6 +179,7 @@ const additionalModules = computed(() => { ) { Modules.push(...(props.additionalModel as any[])); } else { + // oxlint-disable-next-line no-unused-expressions props.additionalModel && Modules.push(props.additionalModel); } @@ -417,6 +419,7 @@ const processSimulation = () => { // bpmnModeler.get('toggleMode', 'strict'), // "bpmnModeler.get('toggleMode')", // ); + // oxlint-disable-next-line no-unused-expressions props.simulation && bpmnModeler.get('toggleMode', 'strict').toggleMode(); }; const processRedo = () => { diff --git a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/content-pad/contentPadProvider.js b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/content-pad/contentPadProvider.js index 638317fb8..b8f1dfef7 100644 --- a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/content-pad/contentPadProvider.js +++ b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/content-pad/contentPadProvider.js @@ -7,7 +7,7 @@ import { hasPrimaryModifier } from 'diagram-js/lib/util/Mouse'; /** * A provider for BPMN 2.0 elements context pad */ -export default function ContextPadProvider( +function ContextPadProvider( config, injector, eventBus, @@ -57,6 +57,8 @@ export default function ContextPadProvider( }); } +export default ContextPadProvider; + ContextPadProvider.$inject = [ 'config.contextPad', 'injector', diff --git a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/CustomPalette.js b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/CustomPalette.js index 75b491ccb..aa682e7db 100644 --- a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/CustomPalette.js +++ b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/CustomPalette.js @@ -1,6 +1,6 @@ import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider'; -export default function CustomPalette( +function CustomPalette( palette, create, elementFactory, @@ -24,11 +24,21 @@ export default function CustomPalette( ); } -const F = function () {}; // 核心,利用空对象作为中介; -F.prototype = PaletteProvider.prototype; // 核心,将父类的原型赋值给空对象F; +CustomPalette.$inject = [ + 'palette', + 'create', + 'elementFactory', + 'spaceTool', + 'lassoTool', + 'handTool', + 'globalConnect', + 'translate', +]; -// 利用中介函数重写原型链方法 -F.prototype.getPaletteEntries = function () { +CustomPalette.prototype = Object.create(PaletteProvider.prototype); +CustomPalette.prototype.constructor = CustomPalette; + +CustomPalette.prototype.getPaletteEntries = function () { const actions = {}; const create = this._create; const elementFactory = this._elementFactory; @@ -94,8 +104,7 @@ F.prototype.getPaletteEntries = function () { 'hand-tool': { group: 'tools', className: 'bpmn-icon-hand-tool', - title: '激活抓手工具', - // title: translate("Activate the hand tool"), + title: translate('Activate the hand tool'), action: { click(event) { handTool.activateHand(event); @@ -219,16 +228,4 @@ F.prototype.getPaletteEntries = function () { return actions; }; -CustomPalette.$inject = [ - 'palette', - 'create', - 'elementFactory', - 'spaceTool', - 'lassoTool', - 'handTool', - 'globalConnect', - 'translate', -]; - -CustomPalette.prototype = new F(); // 核心,将 F的实例赋值给子类; -CustomPalette.prototype.constructor = CustomPalette; // 修复子类CustomPalette的构造器指向,防止原型链的混乱; +export default CustomPalette; diff --git a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/paletteProvider.js b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/paletteProvider.js index 70a27a9a6..9faa09737 100644 --- a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/paletteProvider.js +++ b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/paletteProvider.js @@ -1,7 +1,7 @@ /** * A palette provider for BPMN 2.0 elements. */ -export default function PaletteProvider( +function PaletteProvider( palette, create, elementFactory, @@ -23,6 +23,8 @@ export default function PaletteProvider( palette.registerProvider(this); } +export default PaletteProvider; + PaletteProvider.$inject = [ 'palette', 'create', diff --git a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/translate/zh.js b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/translate/zh.js index 4d25da7f5..a573e5f2b 100644 --- a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/translate/zh.js +++ b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/translate/zh.js @@ -1,4 +1,3 @@ -/* eslint-disable no-template-curly-in-string */ /** * This is a sample file that should be replaced with the actual translation. * @@ -238,10 +237,8 @@ export default { 'Due Date': '到期时间', 'Follow Up Date': '跟踪日期', Priority: '优先级', - 'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': - '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00', - 'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': - '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00', + [`The follow up date as an EL expression (e.g. \${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)`]: `跟踪日期必须符合EL表达式,如: \${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00`, + [`The due date as an EL expression (e.g. \${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)`]: `跟踪日期必须符合EL表达式,如: \${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00`, Variables: '变量', 'Candidate Starter Configuration': '候选人起动器配置', 'Candidate Starter Groups': '候选人起动器组', diff --git a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue index 8c8cc6f11..656249b25 100644 --- a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue +++ b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue @@ -39,7 +39,7 @@ watch( val += props.businessObject.eventDefinitions[0]?.$type.split(':')[1] || ''; } - // @ts-ignore + // @ts-expect-error: async component registry is indexed dynamically customConfigComponent.value = ( CustomConfigMap as Record )[val]?.component; diff --git a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/penal/multi-instance/ElementMultiInstance.vue b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/penal/multi-instance/ElementMultiInstance.vue index 6caf5c0ab..2883834d3 100644 --- a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/penal/multi-instance/ElementMultiInstance.vue +++ b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/penal/multi-instance/ElementMultiInstance.vue @@ -1,4 +1,3 @@ - diff --git a/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue b/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue index fef0d4279..10444d0ae 100644 --- a/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue +++ b/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue @@ -29,8 +29,7 @@ const formSchema = computed((): VbenFormSchema[] => { }); function handleSubmit(value: Recordable) { - // eslint-disable-next-line no-console - console.log('reset email:', value); + void value; } diff --git a/apps/web-antdv-next/src/views/_core/authentication/register.vue b/apps/web-antdv-next/src/views/_core/authentication/register.vue index b1a5de726..8c4295310 100644 --- a/apps/web-antdv-next/src/views/_core/authentication/register.vue +++ b/apps/web-antdv-next/src/views/_core/authentication/register.vue @@ -82,8 +82,7 @@ const formSchema = computed((): VbenFormSchema[] => { }); function handleSubmit(value: Recordable) { - // eslint-disable-next-line no-console - console.log('register submit:', value); + void value; } diff --git a/apps/web-antdv-next/src/views/dashboard/analytics/index.vue b/apps/web-antdv-next/src/views/dashboard/analytics/index.vue index 5e3d6d285..e794c99a9 100644 --- a/apps/web-antdv-next/src/views/dashboard/analytics/index.vue +++ b/apps/web-antdv-next/src/views/dashboard/analytics/index.vue @@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
- + - + diff --git a/apps/web-antdv-next/tailwind.config.mjs b/apps/web-antdv-next/tailwind.config.mjs deleted file mode 100644 index f17f556fa..000000000 --- a/apps/web-antdv-next/tailwind.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config'; diff --git a/apps/web-antdv-next/tsconfig.json b/apps/web-antdv-next/tsconfig.json index 02c287fe6..858a0ec08 100644 --- a/apps/web-antdv-next/tsconfig.json +++ b/apps/web-antdv-next/tsconfig.json @@ -2,7 +2,6 @@ "$schema": "https://json.schemastore.org/tsconfig", "extends": "@vben/tsconfig/web-app.json", "compilerOptions": { - "baseUrl": ".", "paths": { "#/*": ["./src/*"] } diff --git a/apps/web-antdv-next/tsconfig.node.json b/apps/web-antdv-next/tsconfig.node.json index c2f0d86cc..36e9fb5fb 100644 --- a/apps/web-antdv-next/tsconfig.node.json +++ b/apps/web-antdv-next/tsconfig.node.json @@ -6,5 +6,5 @@ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "noEmit": false }, - "include": ["vite.config.mts"] + "include": ["vite.config.ts"] } diff --git a/apps/web-antdv-next/vite.config.mts b/apps/web-antdv-next/vite.config.ts similarity index 100% rename from apps/web-antdv-next/vite.config.mts rename to apps/web-antdv-next/vite.config.ts diff --git a/apps/web-ele/package.json b/apps/web-ele/package.json index f04fa87b8..d842d8261 100644 --- a/apps/web-ele/package.json +++ b/apps/web-ele/package.json @@ -1,6 +1,6 @@ { "name": "@vben/web-ele", - "version": "5.6.0", + "version": "5.7.0", "homepage": "https://vben.pro", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "repository": { diff --git a/apps/web-ele/postcss.config.mjs b/apps/web-ele/postcss.config.mjs deleted file mode 100644 index 3d8070455..000000000 --- a/apps/web-ele/postcss.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config/postcss'; diff --git a/apps/web-ele/public/tinymce/tinymce.d.ts b/apps/web-ele/public/tinymce/tinymce.d.ts index 20f888b56..a87b1629a 100644 --- a/apps/web-ele/public/tinymce/tinymce.d.ts +++ b/apps/web-ele/public/tinymce/tinymce.d.ts @@ -2537,12 +2537,12 @@ interface EditorSelection { normalize: () => Range; selectorChanged: (selector: string, callback: (active: boolean, args: { node: Node; - selector: String; + selector: string; parents: Node[]; }) => void) => EditorSelection; selectorChangedWithUnbind: (selector: string, callback: (active: boolean, args: { node: Node; - selector: String; + selector: string; parents: Node[]; }) => void) => { unbind: () => void; @@ -3217,9 +3217,9 @@ interface Tools { (arr: ArrayLike | null | undefined, cb: ArrayCallback): R[]; (obj: Record | null | undefined, cb: ObjCallback): R[]; }; - extend: (obj: Object, ext: Object, ...objs: Object[]) => any; + extend: (obj: object, ext: object, ...objs: object[]) => any; walk: >(obj: T, f: WalkCallback, n?: keyof T, scope?: any) => void; - resolve: (path: string, o?: Object) => any; + resolve: (path: string, o?: object) => any; explode: (s: string | string[], d?: string | RegExp) => string[]; _addCacheSuffix: (url: string) => string; } diff --git a/apps/web-ele/src/adapter/vxe-table.ts b/apps/web-ele/src/adapter/vxe-table.ts index 9f46883c2..724bd0df6 100644 --- a/apps/web-ele/src/adapter/vxe-table.ts +++ b/apps/web-ele/src/adapter/vxe-table.ts @@ -194,6 +194,7 @@ setupVbenVxeTable({ class: '!p-0', ...props, }; + // eslint-disable-next-line no-useless-assignment let align = 'end'; switch (column.align) { case 'center': { diff --git a/apps/web-ele/src/api/crm/customer/limitConfig/index.ts b/apps/web-ele/src/api/crm/customer/limitConfig/index.ts index f99a35f31..102e98db3 100644 --- a/apps/web-ele/src/api/crm/customer/limitConfig/index.ts +++ b/apps/web-ele/src/api/crm/customer/limitConfig/index.ts @@ -16,10 +16,10 @@ export namespace CrmCustomerLimitConfigApi { /** 客户限制配置类型 */ export enum LimitConfType { - /** 锁定客户数限制 */ - CUSTOMER_LOCK_LIMIT = 2, /** 拥有客户数限制 */ CUSTOMER_QUANTITY_LIMIT = 1, + /** 锁定客户数限制 */ + CUSTOMER_LOCK_LIMIT = 2, } /** 查询客户限制配置列表 */ diff --git a/apps/web-ele/src/api/crm/permission/index.ts b/apps/web-ele/src/api/crm/permission/index.ts index 296e442f0..b95e142b1 100644 --- a/apps/web-ele/src/api/crm/permission/index.ts +++ b/apps/web-ele/src/api/crm/permission/index.ts @@ -35,11 +35,11 @@ export namespace CrmPermissionApi { * CRM 业务类型枚举 */ export enum BizTypeEnum { - CRM_BUSINESS = 4, // 商机 CRM_CLUE = 1, // 线索 - CRM_CONTACT = 3, // 联系人 - CRM_CONTRACT = 5, // 合同 CRM_CUSTOMER = 2, // 客户 + CRM_CONTACT = 3, // 联系人 + CRM_BUSINESS = 4, // 商机 + CRM_CONTRACT = 5, // 合同 CRM_PRODUCT = 6, // 产品 CRM_RECEIVABLE = 7, // 回款 CRM_RECEIVABLE_PLAN = 8, // 回款计划 diff --git a/apps/web-ele/src/api/request.ts b/apps/web-ele/src/api/request.ts index 97462d84a..0675f912c 100644 --- a/apps/web-ele/src/api/request.ts +++ b/apps/web-ele/src/api/request.ts @@ -119,7 +119,9 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) { response.data = apiEncrypt.decryptResponse(response.data); } catch (error) { console.error('响应数据解密失败:', error); - throw new Error(`响应数据解密失败: ${(error as Error).message}`); + throw new Error(`响应数据解密失败: ${(error as Error).message}`, { + cause: error, + }); } } return response; diff --git a/apps/web-ele/src/components/cropper/cropper.vue b/apps/web-ele/src/components/cropper/cropper.vue index d14f75351..0db99c17b 100644 --- a/apps/web-ele/src/components/cropper/cropper.vue +++ b/apps/web-ele/src/components/cropper/cropper.vue @@ -89,6 +89,7 @@ async function init() { // Real-time display preview function realTimeCropped() { + // oxlint-disable-next-line no-unused-expressions props.realTimePreview && cropped(); } @@ -113,10 +114,9 @@ function cropped() { imgInfo, }); }; - // eslint-disable-next-line unicorn/prefer-add-event-listener - fileReader.onerror = () => { + fileReader.addEventListener('error', () => { emit('cropendError'); - }; + }); }, 'image/png'); } diff --git a/apps/web-ele/src/components/description/use-description.ts b/apps/web-ele/src/components/description/use-description.ts index fd24920f0..9559cb648 100644 --- a/apps/web-ele/src/components/description/use-description.ts +++ b/apps/web-ele/src/components/description/use-description.ts @@ -21,7 +21,6 @@ export function useDescription(options?: Partial) { inheritAttrs: false, setup(_props, { attrs, slots }) { return () => { - // @ts-ignore - 避免类型实例化过深 return h(Description, { ...propsState, ...attrs }, slots); }; }, diff --git a/apps/web-ele/src/components/form-create/components/area-select.vue b/apps/web-ele/src/components/form-create/components/area-select.vue index 6eb3b1990..288101024 100644 --- a/apps/web-ele/src/components/form-create/components/area-select.vue +++ b/apps/web-ele/src/components/form-create/components/area-select.vue @@ -44,6 +44,7 @@ interface Props { clearable?: boolean; showAllLevels?: boolean; separator?: string; + // eslint-disable-next-line vue/require-default-prop formCreateInject?: any; } @@ -70,7 +71,7 @@ async function loadAreaTree(): Promise { const data = await getAreaTree(); // 根据 level 限制层级 - areaTree.value = filterTreeByLevel(data || [], props.level); + areaTree.value = filterTreeByLevel((data || []) as AreaVO[], props.level); } catch (error) { console.warn('[AreaSelect] 加载地区数据失败:', error); areaTree.value = []; @@ -99,7 +100,7 @@ function filterTreeByLevel(tree: AreaVO[], maxLevel: number): AreaVO[] { } // 处理选中值变化 -function handleChange(value: number[] | undefined): void { +function handleChange(value: any): void { if (value === undefined || value === null) { emit('update:modelValue', undefined); return; diff --git a/apps/web-ele/src/components/form-create/components/dept-select.vue b/apps/web-ele/src/components/form-create/components/dept-select.vue index 073d30767..9a9febc53 100644 --- a/apps/web-ele/src/components/form-create/components/dept-select.vue +++ b/apps/web-ele/src/components/form-create/components/dept-select.vue @@ -39,15 +39,16 @@ interface DeptVO { status?: number; } -// TODO @puhui999:linter 报错; /** 接受父组件参数 */ interface Props { + // eslint-disable-next-line vue/require-default-prop modelValue?: number | number[] | string | string[]; multiple?: boolean; returnType?: 'id' | 'name'; defaultCurrentDept?: boolean; disabled?: boolean; placeholder?: string; + // eslint-disable-next-line vue/require-default-prop formCreateInject?: any; } diff --git a/apps/web-ele/src/components/form-create/components/iframe.vue b/apps/web-ele/src/components/form-create/components/iframe.vue index 6fb762c11..6037b2a78 100644 --- a/apps/web-ele/src/components/form-create/components/iframe.vue +++ b/apps/web-ele/src/components/form-create/components/iframe.vue @@ -27,6 +27,7 @@ interface Props { allowfullscreen?: boolean; loading?: 'eager' | 'lazy'; sandbox?: string; + // eslint-disable-next-line vue/require-default-prop formCreateInject?: any; } diff --git a/apps/web-ele/src/components/form-create/components/use-api-select.tsx b/apps/web-ele/src/components/form-create/components/use-api-select.tsx index accd8190b..6df1ae49e 100644 --- a/apps/web-ele/src/components/form-create/components/use-api-select.tsx +++ b/apps/web-ele/src/components/form-create/components/use-api-select.tsx @@ -193,7 +193,8 @@ export function useApiSelect(option: ApiSelectProps) { let parse: any = null; if (props.parseFunc) { // 解析字符串函数 - // eslint-disable-next-line no-new-func + // oxlint-disable-next-line typescript/no-implied-eval + // oxlint-disable-next-line no-new-func, typescript/no-implied-eval parse = new Function(`return ${props.parseFunc}`)(); } return parse; diff --git a/apps/web-ele/src/components/form-create/helpers.ts b/apps/web-ele/src/components/form-create/helpers.ts index 53d51e85e..a254186d2 100644 --- a/apps/web-ele/src/components/form-create/helpers.ts +++ b/apps/web-ele/src/components/form-create/helpers.ts @@ -11,14 +11,14 @@ import formCreate from '@form-create/element-ui'; import { apiSelectRule } from '#/components/form-create/rules/data'; import { + useAreaSelectRule, useDictSelectRule, useEditorRule, + useIframeRule, useSelectRule, useUploadFileRule, useUploadImageRule, useUploadImagesRule, - useIframeRule, - useAreaSelectRule, } from './rules'; /** 编码表单 Conf */ diff --git a/apps/web-ele/src/components/form-create/rules/data.ts b/apps/web-ele/src/components/form-create/rules/data.ts index 9c1d3d654..e754031e0 100644 --- a/apps/web-ele/src/components/form-create/rules/data.ts +++ b/apps/web-ele/src/components/form-create/rules/data.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-template-curly-in-string */ const selectRule = [ { type: 'select', @@ -134,7 +133,7 @@ const apiSelectRule = [ type: 'input', field: 'labelField', title: 'label 属性', - info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}', + info: `可以使用 el 表达式:\${属性},来实现复杂数据组合。如:\${nickname}-\${id}`, props: { placeholder: 'nickname', }, @@ -143,7 +142,7 @@ const apiSelectRule = [ type: 'input', field: 'valueField', title: 'value 属性', - info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}', + info: `可以使用 el 表达式:\${属性},来实现复杂数据组合。如:\${nickname}-\${id}`, props: { placeholder: 'id', }, diff --git a/apps/web-ele/src/components/markdown-view/markdown-view.vue b/apps/web-ele/src/components/markdown-view/markdown-view.vue index e3af56379..bb8bb0594 100644 --- a/apps/web-ele/src/components/markdown-view/markdown-view.vue +++ b/apps/web-ele/src/components/markdown-view/markdown-view.vue @@ -47,6 +47,7 @@ onMounted(async () => { diff --git a/apps/web-ele/src/types/element-plus-style-css.d.ts b/apps/web-ele/src/types/element-plus-style-css.d.ts new file mode 100644 index 000000000..ba7a76672 --- /dev/null +++ b/apps/web-ele/src/types/element-plus-style-css.d.ts @@ -0,0 +1,4 @@ +declare module 'element-plus/es/components/*/style/css' { + const sideEffect: undefined; + export default sideEffect; +} diff --git a/apps/web-ele/src/views/ai/chat/index/index.vue b/apps/web-ele/src/views/ai/chat/index/index.vue index 693403c4a..72e38fef1 100644 --- a/apps/web-ele/src/views/ai/chat/index/index.vue +++ b/apps/web-ele/src/views/ai/chat/index/index.vue @@ -582,7 +582,7 @@ onMounted(async () => { size="small" @click="openChatConversationUpdateForm" > - + {{ activeConversation?.modelName }} (); // image 列表 ref const dall3Ref = ref(); // dall3(openai) ref const midjourneyRef = ref(); // midjourney ref const stableDiffusionRef = ref(); // stable diffusion ref +// @ts-expect-error: template ref is retained for future provider expansion const commonRef = ref(); // stable diffusion ref const selectPlatform = ref('common'); // 选中的平台 @@ -45,7 +46,9 @@ const platformOptions = [ const models = ref([]); // 模型列表 /** 绘画 start */ -async function handleDrawStart() {} +function handleDrawStart() { + // drawing state is handled by child components +} /** 绘画 complete */ async function handleDrawComplete() { diff --git a/apps/web-ele/src/views/ai/mindmap/index/modules/right.vue b/apps/web-ele/src/views/ai/mindmap/index/modules/right.vue index 5adc66aaf..8da862585 100644 --- a/apps/web-ele/src/views/ai/mindmap/index/modules/right.vue +++ b/apps/web-ele/src/views/ai/mindmap/index/modules/right.vue @@ -155,10 +155,12 @@ defineExpose({ ref="mdContainerRef" class="wh-full overflow-y-auto" > +
+
('currentSong', {}); {{ currentSong.date }}
信息复用 +
diff --git a/apps/web-ele/src/views/ai/workflow/form/modules/workflow-design.vue b/apps/web-ele/src/views/ai/workflow/form/modules/workflow-design.vue index 3acb5ad5b..9648fc710 100644 --- a/apps/web-ele/src/views/ai/workflow/form/modules/workflow-design.vue +++ b/apps/web-ele/src/views/ai/workflow/form/modules/workflow-design.vue @@ -106,7 +106,9 @@ async function goRun() { try { convertedParams[paramKey] = convertParamValue(value, dataType); } catch (error: any) { - throw new Error(`参数 ${paramKey} 转换失败: ${error.message}`); + throw new Error(`参数 ${paramKey} 转换失败: ${error.message}`, { + cause: error, + }); } } @@ -175,7 +177,7 @@ function convertParamValue(value: string, dataType: string) { try { return JSON.parse(value); } catch (error: any) { - throw new Error(`JSON格式错误: ${error.message}`); + throw new Error(`JSON格式错误: ${error.message}`, { cause: error }); } } default: { diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessDesigner.vue b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessDesigner.vue index e77301f20..732b34a03 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessDesigner.vue +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessDesigner.vue @@ -21,7 +21,7 @@ import { } from '@vben/icons'; // 模拟流转流程 -// @ts-ignore +// @ts-expect-error: token simulation package does not ship compatible types import tokenSimulation from 'bpmn-js-token-simulation'; import BpmnModeler from 'bpmn-js/lib/Modeler'; import { @@ -139,6 +139,7 @@ const emit = defineEmits([ 'element-click', ]); +// @ts-expect-error: file input ref is set imperatively by the template const bpmnCanvas = ref(); const refFile = ref(); @@ -185,6 +186,7 @@ const additionalModules = computed(() => { ) { Modules.push(...(props.additionalModel as any[])); } else { + // oxlint-disable-next-line no-unused-expressions props.additionalModel && Modules.push(props.additionalModel); } @@ -424,6 +426,7 @@ const processSimulation = () => { // bpmnModeler.get('toggleMode', 'strict'), // "bpmnModeler.get('toggleMode')", // ); + // oxlint-disable-next-line no-unused-expressions props.simulation && bpmnModeler.get('toggleMode', 'strict').toggleMode(); }; const processRedo = () => { diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/content-pad/contentPadProvider.js b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/content-pad/contentPadProvider.js index 638317fb8..b8f1dfef7 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/content-pad/contentPadProvider.js +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/content-pad/contentPadProvider.js @@ -7,7 +7,7 @@ import { hasPrimaryModifier } from 'diagram-js/lib/util/Mouse'; /** * A provider for BPMN 2.0 elements context pad */ -export default function ContextPadProvider( +function ContextPadProvider( config, injector, eventBus, @@ -57,6 +57,8 @@ export default function ContextPadProvider( }); } +export default ContextPadProvider; + ContextPadProvider.$inject = [ 'config.contextPad', 'injector', diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/CustomPalette.js b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/CustomPalette.js index 75b491ccb..aa682e7db 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/CustomPalette.js +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/CustomPalette.js @@ -1,6 +1,6 @@ import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider'; -export default function CustomPalette( +function CustomPalette( palette, create, elementFactory, @@ -24,11 +24,21 @@ export default function CustomPalette( ); } -const F = function () {}; // 核心,利用空对象作为中介; -F.prototype = PaletteProvider.prototype; // 核心,将父类的原型赋值给空对象F; +CustomPalette.$inject = [ + 'palette', + 'create', + 'elementFactory', + 'spaceTool', + 'lassoTool', + 'handTool', + 'globalConnect', + 'translate', +]; -// 利用中介函数重写原型链方法 -F.prototype.getPaletteEntries = function () { +CustomPalette.prototype = Object.create(PaletteProvider.prototype); +CustomPalette.prototype.constructor = CustomPalette; + +CustomPalette.prototype.getPaletteEntries = function () { const actions = {}; const create = this._create; const elementFactory = this._elementFactory; @@ -94,8 +104,7 @@ F.prototype.getPaletteEntries = function () { 'hand-tool': { group: 'tools', className: 'bpmn-icon-hand-tool', - title: '激活抓手工具', - // title: translate("Activate the hand tool"), + title: translate('Activate the hand tool'), action: { click(event) { handTool.activateHand(event); @@ -219,16 +228,4 @@ F.prototype.getPaletteEntries = function () { return actions; }; -CustomPalette.$inject = [ - 'palette', - 'create', - 'elementFactory', - 'spaceTool', - 'lassoTool', - 'handTool', - 'globalConnect', - 'translate', -]; - -CustomPalette.prototype = new F(); // 核心,将 F的实例赋值给子类; -CustomPalette.prototype.constructor = CustomPalette; // 修复子类CustomPalette的构造器指向,防止原型链的混乱; +export default CustomPalette; diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/paletteProvider.js b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/paletteProvider.js index 70a27a9a6..9faa09737 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/paletteProvider.js +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/palette/paletteProvider.js @@ -1,7 +1,7 @@ /** * A palette provider for BPMN 2.0 elements. */ -export default function PaletteProvider( +function PaletteProvider( palette, create, elementFactory, @@ -23,6 +23,8 @@ export default function PaletteProvider( palette.registerProvider(this); } +export default PaletteProvider; + PaletteProvider.$inject = [ 'palette', 'create', diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/translate/zh.js b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/translate/zh.js index 4d25da7f5..a573e5f2b 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/translate/zh.js +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/designer/plugins/translate/zh.js @@ -1,4 +1,3 @@ -/* eslint-disable no-template-curly-in-string */ /** * This is a sample file that should be replaced with the actual translation. * @@ -238,10 +237,8 @@ export default { 'Due Date': '到期时间', 'Follow Up Date': '跟踪日期', Priority: '优先级', - 'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': - '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00', - 'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': - '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00', + [`The follow up date as an EL expression (e.g. \${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)`]: `跟踪日期必须符合EL表达式,如: \${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00`, + [`The due date as an EL expression (e.g. \${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)`]: `跟踪日期必须符合EL表达式,如: \${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00`, Variables: '变量', 'Candidate Starter Configuration': '候选人起动器配置', 'Candidate Starter Groups': '候选人起动器组', diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue index 8c8cc6f11..656249b25 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/custom-config/ElementCustomConfig.vue @@ -39,7 +39,7 @@ watch( val += props.businessObject.eventDefinitions[0]?.$type.split(':')[1] || ''; } - // @ts-ignore + // @ts-expect-error: async component registry is indexed dynamically customConfigComponent.value = ( CustomConfigMap as Record )[val]?.component; diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/multi-instance/ElementMultiInstance.vue b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/multi-instance/ElementMultiInstance.vue index f7ca14286..a9b2198e5 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/multi-instance/ElementMultiInstance.vue +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/multi-instance/ElementMultiInstance.vue @@ -67,13 +67,13 @@ const bpmnElement = ref(null); const multiLoopInstance = ref(null); declare global { interface Window { - // @ts-ignore bpmnInstances?: () => any; } } const bpmnInstances = () => (window as any)?.bpmnInstances; +// @ts-expect-error: retained for legacy multi-instance mode compatibility // eslint-disable-next-line unused-imports/no-unused-vars const getElementLoop = (businessObject: any): void => { if (!businessObject.loopCharacteristics) { @@ -142,8 +142,7 @@ const changeLoopCharacteristicsType = (type: any): void => { isSequential: true, }) : bpmnInstances().moddle.create('bpmn:MultiInstanceLoopCharacteristics', { - // eslint-disable-next-line no-template-curly-in-string - collection: '${coll_userList}', + collection: `\${coll_userList}`, }); bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { loopCharacteristics: toRaw(multiLoopInstance.value), @@ -234,7 +233,7 @@ const updateLoopAsync = (key: any): void => { extensionElements: null, }; } else { - // @ts-ignore + // @ts-expect-error: dynamic async flags are assigned by runtime key asyncAttr[key] = loopInstanceForm.value[key]; } bpmnInstances().modeling.updateModdleProperties( @@ -248,23 +247,20 @@ const changeConfig = (config: string): void => { switch (config) { case '会签': { changeLoopCharacteristicsType('ParallelMultiInstance'); - // eslint-disable-next-line no-template-curly-in-string - updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }'); + updateLoopCondition(`\${ nrOfCompletedInstances >= nrOfInstances }`); break; } case '依次审批': { changeLoopCharacteristicsType('SequentialMultiInstance'); updateLoopCardinality('1'); - // eslint-disable-next-line no-template-curly-in-string - updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }'); + updateLoopCondition(`\${ nrOfCompletedInstances >= nrOfInstances }`); break; } case '或签': { changeLoopCharacteristicsType('ParallelMultiInstance'); - // eslint-disable-next-line no-template-curly-in-string - updateLoopCondition('${ nrOfCompletedInstances > 0 }'); + updateLoopCondition(`\${ nrOfCompletedInstances > 0 }`); break; } @@ -332,8 +328,7 @@ const updateLoopCharacteristics = (): void => { if (approveMethod.value === ApproveMethodType.APPROVE_BY_RATIO) { multiLoopInstance.value = bpmnInstances().moddle.create( 'bpmn:MultiInstanceLoopCharacteristics', - // eslint-disable-next-line no-template-curly-in-string - { isSequential: false, collection: '${coll_userList}' }, + { isSequential: false, collection: `\${coll_userList}` }, ); multiLoopInstance.value.completionCondition = bpmnInstances().moddle.create('bpmn:FormalExpression', { @@ -345,20 +340,17 @@ const updateLoopCharacteristics = (): void => { if (approveMethod.value === ApproveMethodType.ANY_APPROVE) { multiLoopInstance.value = bpmnInstances().moddle.create( 'bpmn:MultiInstanceLoopCharacteristics', - // eslint-disable-next-line no-template-curly-in-string - { isSequential: false, collection: '${coll_userList}' }, + { isSequential: false, collection: `\${coll_userList}` }, ); multiLoopInstance.value.completionCondition = bpmnInstances().moddle.create('bpmn:FormalExpression', { - // eslint-disable-next-line no-template-curly-in-string - body: '${ nrOfCompletedInstances > 0 }', + body: `\${ nrOfCompletedInstances > 0 }`, }); } if (approveMethod.value === ApproveMethodType.SEQUENTIAL_APPROVE) { multiLoopInstance.value = bpmnInstances().moddle.create( 'bpmn:MultiInstanceLoopCharacteristics', - // eslint-disable-next-line no-template-curly-in-string - { isSequential: true, collection: '${coll_userList}' }, + { isSequential: true, collection: `\${coll_userList}` }, ); multiLoopInstance.value.loopCardinality = bpmnInstances().moddle.create( 'bpmn:FormalExpression', @@ -368,8 +360,7 @@ const updateLoopCharacteristics = (): void => { ); multiLoopInstance.value.completionCondition = bpmnInstances().moddle.create('bpmn:FormalExpression', { - // eslint-disable-next-line no-template-curly-in-string - body: '${ nrOfCompletedInstances >= nrOfInstances }', + body: `\${ nrOfCompletedInstances >= nrOfInstances }`, }); } bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), { diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/ElementTask.vue b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/ElementTask.vue index 8775cb942..8d160c233 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/ElementTask.vue +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/ElementTask.vue @@ -53,7 +53,7 @@ watch( () => props.type, () => { if (props.type) { - // @ts-ignore + // @ts-expect-error: installed task component map is indexed dynamically witchTaskComponent.value = installedComponent[props.type].component; } }, diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/CallActivity.vue b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/CallActivity.vue index dfdf17647..37fd976c9 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/CallActivity.vue +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/CallActivity.vue @@ -65,7 +65,7 @@ const initCallActivity = () => { // 初始化所有配置项 Object.keys(formData.value).forEach((key: string) => { - // @ts-ignore + // @ts-expect-error: form state is updated through dynamic schema keys formData.value[key] = bpmnElement.value.businessObject[key] ?? formData.value[key as keyof FormData]; @@ -183,6 +183,7 @@ const updateElementExtensions = () => { watch( () => props.id, (val) => { + // oxlint-disable-next-line no-unused-expressions val && val.length > 0 && nextTick(() => { diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ReceiveTask.vue b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ReceiveTask.vue index affd1cd0a..0d5ea00db 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ReceiveTask.vue +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ReceiveTask.vue @@ -82,7 +82,6 @@ onMounted(() => { bpmnRootElements.value .filter((el: any) => el.$type === 'bpmn:Message') .forEach((m: any) => { - // @ts-ignore if (bpmnMessageRefsMap.value) { bpmnMessageRefsMap.value[m.id] = m; } diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ScriptTask.vue b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ScriptTask.vue index c15dad114..bff3cfe28 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ScriptTask.vue +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ScriptTask.vue @@ -27,7 +27,7 @@ const bpmnInstances = () => (window as any)?.bpmnInstances; const resetTaskForm = () => { for (const key in defaultTaskForm.value) { - // @ts-ignore + // @ts-expect-error: form state is updated through dynamic schema keys scriptTaskForm.value[key] = bpmnElement.value?.businessObject[ key as keyof typeof defaultTaskForm.value diff --git a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ServiceTask.vue b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ServiceTask.vue index 2994f1eb9..ab8fddc0b 100644 --- a/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ServiceTask.vue +++ b/apps/web-ele/src/views/bpm/components/bpmn-process-designer/package/penal/task/task-components/ServiceTask.vue @@ -1,3 +1,4 @@ +