merge: 合并 master 分支,解决 isUrl 冲突(保留从 @vben/utils 导出的方式)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pull/339/head
YunaiV 2026-04-12 21:27:05 +08:00
commit 76f9d3d9fc
643 changed files with 12914 additions and 10977 deletions

7
.gitignore vendored
View File

@ -50,3 +50,10 @@ vite.config.ts.*
*.sw? *.sw?
.history .history
.cursor .cursor
# AI
.agent
.agents
.claude
.codex
skills-lock.json

4
.npmrc
View File

@ -1,8 +1,8 @@
registry=https://registry.npmmirror.com registry=https://registry.npmmirror.com
public-hoist-pattern[]=lefthook public-hoist-pattern[]=lefthook
public-hoist-pattern[]=eslint public-hoist-pattern[]=eslint
public-hoist-pattern[]=prettier public-hoist-pattern[]=oxfmt
public-hoist-pattern[]=prettier-plugin-tailwindcss public-hoist-pattern[]=oxlint
public-hoist-pattern[]=stylelint public-hoist-pattern[]=stylelint
public-hoist-pattern[]=*postcss* public-hoist-pattern[]=*postcss*
public-hoist-pattern[]=@commitlint/* public-hoist-pattern[]=@commitlint/*

View File

@ -1,18 +0,0 @@
dist
dev-dist
.local
.output.js
node_modules
.nvmrc
coverage
CODEOWNERS
.nitro
.output
**/*.svg
**/*.sh
public
.npmrc
*-lock.yaml

View File

@ -1 +0,0 @@
export { default } from '@vben/prettier-config';

View File

@ -2,3 +2,7 @@ dist
public public
__tests__ __tests__
coverage coverage
.codex
.claude
.agent
.agents

View File

@ -2,14 +2,18 @@
"recommendations": [ "recommendations": [
// Vue 3 // Vue 3
"Vue.volar", "Vue.volar",
// ESLint JavaScript VS Code // eslint VS Code
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
// oxlint VS Code
"oxc.oxc-vscode",
// Visual Studio Code Stylelint // Visual Studio Code Stylelint
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
// 使 Prettier // 使 oxfmt
"esbenp.prettier-vscode", "oxc.oxc-vscode",
// dotenv // dotenv
"mikestead.dotenv", "mikestead.dotenv",
// YAML ESLint pnpm-workspace.yaml
"redhat.vscode-yaml",
// //
"streetsidesoftware.code-spell-checker", "streetsidesoftware.code-spell-checker",
// Tailwind CSS VS Code // Tailwind CSS VS Code

48
.vscode/settings.json vendored
View File

@ -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
"workbench.list.smoothScrolling": true, "workbench.list.smoothScrolling": true,
"workbench.startupEditor": "newUntitledFile", "workbench.startupEditor": "newUntitledFile",
@ -31,39 +32,51 @@
"editor.autoClosingOvertype": "always", "editor.autoClosingOvertype": "always",
"editor.autoClosingQuotes": "beforeWhitespace", "editor.autoClosingQuotes": "beforeWhitespace",
"editor.wordSeparators": "`~!@#%^&*()=+[{]}\\|;:'\",.<>/?", "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": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit", "source.fixAll.eslint": "explicit",
"source.fixAll.oxc": "explicit",
"source.fixAll.stylelint": "explicit", "source.fixAll.stylelint": "explicit",
"source.organizeImports": "never" "source.organizeImports": "never"
}, },
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "oxc.oxc-vscode",
"[html]": { "[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[css]": { "[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[scss]": { "[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[javascript]": { "[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[json]": { "[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[markdown]": { "[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[jsonc]": { "[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
"[vue]": { "[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "oxc.oxc-vscode"
}, },
// extensions // extensions
"extensions.ignoreRecommendations": true, "extensions.ignoreRecommendations": true,
@ -79,6 +92,7 @@
"files.insertFinalNewline": true, "files.insertFinalNewline": true,
"files.simpleDialog.enable": true, "files.simpleDialog.enable": true,
"files.associations": { "files.associations": {
"*.css": "tailwindcss",
"*.ejs": "html", "*.ejs": "html",
"*.art": "html", "*.art": "html",
"**/tsconfig.json": "jsonc", "**/tsconfig.json": "jsonc",
@ -118,7 +132,7 @@
// search // search
"search.searchEditor.singleClickBehaviour": "peekDefinition", "search.searchEditor.singleClickBehaviour": "peekDefinition",
"search.followSymlinks": false, "search.followSymlinks": false,
// 使/ // 使
"search.exclude": { "search.exclude": {
"**/node_modules": true, "**/node_modules": true,
"**/*.log": true, "**/*.log": true,
@ -159,7 +173,7 @@
"emmet.triggerExpansionOnTab": false, "emmet.triggerExpansionOnTab": false,
"errorLens.enabledDiagnosticLevels": ["warning", "error"], "errorLens.enabledDiagnosticLevels": ["warning", "error"],
"errorLens.excludeBySource": ["cSpell", "Grammarly", "eslint"], "errorLens.excludeBySource": ["cSpell", "Grammarly"],
"stylelint.enable": true, "stylelint.enable": true,
"stylelint.packageManager": "pnpm", "stylelint.packageManager": "pnpm",
@ -193,7 +207,7 @@
"*": false "*": false
}, },
"cssVariables.lookupFiles": ["packages/core/base/design/src/**/*.css"], "cssVariables.lookupFiles": ["packages/@core/base/design/src/**/*.css"],
"i18n-ally.localesPaths": [ "i18n-ally.localesPaths": [
"packages/locales/src/langs", "packages/locales/src/langs",
@ -218,12 +232,10 @@
"*.env": "$(capture).env.*", "*.env": "$(capture).env.*",
"README.md": "README*,CHANGELOG*,LICENSE,CNAME", "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", "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", "oxlint.config.ts": ".eslintignore,.stylelintignore,.commitlintrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml,oxfmt.config.*,eslint.config.*"
"tailwind.config.mjs": "postcss.*"
}, },
"commentTranslate.hover.enabled": false, "commentTranslate.hover.enabled": false,
"commentTranslate.multiLineMerge": true, "commentTranslate.multiLineMerge": true,
"vue.server.hybridMode": true, "vue.server.hybridMode": true,
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib"
"oxc.enable": false
} }

View File

@ -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】<http://dashboard-vue3.yudao.iocoder.cn> - 演示地址【Vue3 + element-plus】<http://dashboard-vue3.yudao.iocoder.cn>
- 演示地址【Vue3 + vben5(ant-design-vue)】:<http://dashboard-vben.yudao.iocoder.cn> - 演示地址【Vue3 + vben5(ant-design-vue)】:<http://dashboard-vben.yudao.iocoder.cn>
- 演示地址【Vue2 + element-ui】<http://dashboard.yudao.iocoder.cn> - 演示地址【Vue2 + element-ui】<http://dashboard.yudao.iocoder.cn>
@ -20,12 +20,12 @@
**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。 **芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 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/) 多种免费开源的中后台模版,具备如下特性: - 支持 [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) ![首页](.gitee/image/demo/vben.png)
- **最新技术栈**:使用 Vue3、Vite7 等前端前沿技术开发 - **最新技术栈**:使用 Vue3、Vite8 等前端前沿技术开发
- **TypeScript**: 应用程序级 JavaScript 的语言 - **TypeScript**: 应用程序级 JavaScript 的语言
- **主题**: 提供多套主题色彩,可配置自定义主题 - **主题**: 提供多套主题色彩,可配置自定义主题
- **国际化**:内置完善的国际化方案 - **国际化**:内置完善的国际化方案
@ -41,24 +41,24 @@
| 框架 | 说明 | 版本 | | 框架 | 说明 | 版本 |
| --- | --- | --- | | --- | --- | --- |
| [Vue](https://staging-cn.vuejs.org/) | vue框架 | 3.5.27 | | [Vue](https://staging-cn.vuejs.org/) | vue框架 | 3.5.30 |
| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 7.3.1 | | [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 8.0.0 |
| [Ant Design Vue](https://www.antdv.com/) | Ant Design Vue | 4.2.6 | | [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 | | [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.13.5 |
| [Naive UI](https://www.naiveui.com/) | Naive UI | 2.43.2 | | [Naive UI](https://www.naiveui.com/) | Naive UI | 2.44.1 |
| [TDesign](https://tdesign.tencent.com/) | TDesign | 1.18.0 | | [TDesign](https://tdesign.tencent.com/) | TDesign | 1.18.5 |
| [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 超集 | 5.9.3 | | [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 超集 | 5.9.3 |
| [pinia](https://pinia.vuejs.org/) | Vue 存储库替代 vuex5 | 3.0.4 | | [pinia](https://pinia.vuejs.org/) | Vue 存储库替代 vuex5 | 3.0.4 |
| [vueuse](https://vueuse.org/) | 常用工具集 | 14.1.0 | | [vueuse](https://vueuse.org/) | 常用工具集 | 14.2.1 |
| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 11.2.8 | | [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 11.3.0 |
| [vue-router](https://router.vuejs.org/) | Vue 路由 | 4.6.4 | | [vue-router](https://router.vuejs.org/) | Vue 路由 | 5.0.3 |
| [Tailwind CSS](https://tailwindcss.com/) | 原子 CSS | 3.4.19 | | [Tailwind CSS](https://tailwindcss.com/) | 原子 CSS | 4.2.1 |
| [Iconify](https://iconify.design/) | 图标组件 | 5.0.0 | | [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 | | [TinyMCE](https://www.tiny.cloud/) | 富文本编辑器 | 7.3.0 |
| [Echarts](https://echarts.apache.org/) | 图表库 | 6.0.0 | | [Echarts](https://echarts.apache.org/) | 图表库 | 6.0.0 |
| [axios](https://axios-http.com/) | http客户端 | 1.13.2 | | [axios](https://axios-http.com/) | http客户端 | 1.13.6 |
| [dayjs](https://day.js.org/) | 日期处理库 | 1.11.19 | | [dayjs](https://day.js.org/) | 日期处理库 | 1.11.20 |
| [vee-validate](https://vee-validate.logaretm.com/) | 表单验证 | 4.15.1 | | [vee-validate](https://vee-validate.logaretm.com/) | 表单验证 | 4.15.1 |
| [zod](https://zod.dev/) | 数据验证 | 3.25.76 | | [zod](https://zod.dev/) | 数据验证 | 3.25.76 |

View File

@ -1,6 +1,6 @@
{ {
"name": "@vben/web-antd", "name": "@vben/web-antd",
"version": "5.6.0", "version": "5.7.0",
"homepage": "https://vben.pro", "homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {

View File

@ -1 +0,0 @@
export { default } from '@vben/tailwind-config/postcss';

View File

@ -2537,12 +2537,12 @@ interface EditorSelection {
normalize: () => Range; normalize: () => Range;
selectorChanged: (selector: string, callback: (active: boolean, args: { selectorChanged: (selector: string, callback: (active: boolean, args: {
node: Node; node: Node;
selector: String; selector: string;
parents: Node[]; parents: Node[];
}) => void) => EditorSelection; }) => void) => EditorSelection;
selectorChangedWithUnbind: (selector: string, callback: (active: boolean, args: { selectorChangedWithUnbind: (selector: string, callback: (active: boolean, args: {
node: Node; node: Node;
selector: String; selector: string;
parents: Node[]; parents: Node[];
}) => void) => { }) => void) => {
unbind: () => void; unbind: () => void;
@ -3217,9 +3217,9 @@ interface Tools {
<T, R>(arr: ArrayLike<T> | null | undefined, cb: ArrayCallback<T, R>): R[]; <T, R>(arr: ArrayLike<T> | null | undefined, cb: ArrayCallback<T, R>): R[];
<T, R>(obj: Record<string, T> | null | undefined, cb: ObjCallback<T, R>): R[]; <T, R>(obj: Record<string, T> | null | undefined, cb: ObjCallback<T, R>): R[];
}; };
extend: (obj: Object, ext: Object, ...objs: Object[]) => any; extend: (obj: object, ext: object, ...objs: object[]) => any;
walk: <T extends Record<string, any>>(obj: T, f: WalkCallback<T>, n?: keyof T, scope?: any) => void; walk: <T extends Record<string, any>>(obj: T, f: WalkCallback<T>, 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[]; explode: (s: string | string[], d?: string | RegExp) => string[];
_addCacheSuffix: (url: string) => string; _addCacheSuffix: (url: string) => string;
} }

View File

@ -14,6 +14,7 @@ import type {
import type { Component, Ref } from 'vue'; import type { Component, Ref } from 'vue';
import type { BaseFormComponentType } from '@vben/common-ui'; import type { BaseFormComponentType } from '@vben/common-ui';
import type { Sortable } from '@vben/hooks';
import type { Recordable } from '@vben/types'; import type { Recordable } from '@vben/types';
import { import {
@ -21,6 +22,9 @@ import {
defineAsyncComponent, defineAsyncComponent,
defineComponent, defineComponent,
h, h,
nextTick,
onMounted,
onUnmounted,
ref, ref,
render, render,
unref, unref,
@ -33,6 +37,7 @@ import {
IconPicker, IconPicker,
VCropper, VCropper,
} from '@vben/common-ui'; } from '@vben/common-ui';
import { useSortable } from '@vben/hooks';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
@ -132,260 +137,261 @@ const withDefaultPlaceholder = <T extends Component>(
}); });
}; };
const withPreviewUpload = () => { const IMAGE_EXTENSIONS = new Set([
// 检查是否为图片文件的辅助函数 'bmp',
const isImageFile = (file: UploadFile): boolean => { 'gif',
const imageExtensions = new Set([ 'jpeg',
'bmp', 'jpg',
'gif', 'png',
'jpeg', 'svg',
'jpg', 'webp',
'png', ]);
'svg',
'webp', /**
]); *
if (file.url) { */
try { function isImageFile(file: UploadFile): boolean {
const pathname = new URL(file.url, 'http://localhost').pathname; if (file.url) {
const ext = pathname.split('.').pop()?.toLowerCase(); try {
return ext ? imageExtensions.has(ext) : false; const pathname = new URL(file.url, 'http://localhost').pathname;
} catch { const ext = pathname.split('.').pop()?.toLowerCase();
const ext = file.url?.split('.').pop()?.toLowerCase(); return ext ? IMAGE_EXTENSIONS.has(ext) : false;
return ext ? imageExtensions.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(); if (!file.type) {
return ext ? imageExtensions.has(ext) : false; const ext = file.name?.split('.').pop()?.toLowerCase();
} return ext ? IMAGE_EXTENSIONS.has(ext) : false;
return file.type.startsWith('image/'); }
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, * Base64
) => { */
switch (listType) { function getBase64(file: File): Promise<string> {
case 'picture-card': { return new Promise((resolve, reject) => {
return { const reader = new FileReader();
default: () => placeholder, reader.readAsDataURL(file);
}; reader.addEventListener('load', () => resolve(reader.result as string));
} reader.addEventListener('error', reject);
default: { });
return { }
default: () =>
h( /**
Button, *
{ */
icon: h(IconifyIcon, { async function previewImage(
icon: 'ant-design:upload-outlined', file: UploadFile,
class: 'mb-1 size-4', visible: Ref<boolean>,
}), fileList: Ref<UploadProps['fileList']>,
) {
// 非图片文件直接打开链接
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<boolean>,
fileList: Ref<UploadProps['fileList']>,
) => {
// 如果当前文件不是图片,直接打开
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;
}
// 对于图片文件,继续使用预览组 render(h(PreviewWrapper), container);
const [ImageComponent, PreviewGroupComponent] = await Promise.all([ }
Image,
PreviewGroup,
]);
const getBase64 = (file: File) => { /**
return new Promise((resolve, reject) => { *
const reader = new FileReader(); */
reader.readAsDataURL(file); function cropImage(file: File, aspectRatio: string | undefined) {
reader.addEventListener('load', () => resolve(reader.result)); return new Promise<Blob | string | undefined>((resolve, reject) => {
reader.addEventListener('error', (error) => reject(error)); const container = document.createElement('div');
});
};
// 从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');
document.body.append(container); document.body.append(container);
// 用于追踪组件是否已卸载
let isUnmounted = false; let isUnmounted = false;
let objectUrl: null | string = null;
const PreviewWrapper = { const open = ref<boolean>(true);
const cropperRef = ref<InstanceType<typeof VCropper> | 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() { setup() {
return () => { return () => {
if (isUnmounted) return null; if (isUnmounted) return null;
if (!objectUrl) {
objectUrl = URL.createObjectURL(file);
}
return h( return h(
PreviewGroupComponent, Modal,
{ {
class: 'hidden', open: open.value,
preview: { title: h('div', {}, [
visible: visible.value, $t('ui.crop.title'),
// 设置初始显示的图片索引 h(
current: imageFiles.findIndex((f) => f.uid === file.uid), 'span',
onVisibleChange: (value: boolean) => { {
visible.value = value; class: `${aspectRatio ? '' : 'hidden'} ml-2 text-sm text-gray-400 font-normal`,
if (!value) { },
// 延迟清理,确保动画完成 $t('ui.crop.titleTip', [aspectRatio]),
setTimeout(() => { ),
if (!isUnmounted && container) { ]),
isUnmounted = true; centered: true,
render(null, container); width: 548,
container.remove(); keyboard: false,
} maskClosable: false,
}, 300); 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();
}, },
}, },
() => () =>
// 渲染所有图片文件 h(VCropper, {
imageFiles.map((imgFile) => ref: (ref: any) => (cropperRef.value = ref),
h(ImageComponent, { img: objectUrl as string,
key: imgFile.uid, aspectRatio,
src: imgFile.url || imgFile.preview, }),
}),
),
); );
}; };
}, },
}; };
render(h(PreviewWrapper), container); render(h(CropperWrapper), 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<boolean>(true);
const cropperRef = ref<InstanceType<typeof VCropper> | 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);
});
};
/**
*
*/
const withPreviewUpload = () => {
return defineComponent({ return defineComponent({
name: Upload.name, name: Upload.name,
emits: ['update:modelValue'], emits: ['update:modelValue'],
setup: ( setup(
props: any, props: any,
{ attrs, slots, emit }: { attrs: any; emit: any; slots: any }, { attrs, slots, emit }: { attrs: any; emit: any; slots: any },
) => { ) {
const previewVisible = ref<boolean>(false); const previewVisible = ref<boolean>(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 listType = attrs?.listType || attrs?.['list-type'] || 'text';
const fileList = ref<UploadProps['fileList']>( const fileList = ref<UploadProps['fileList']>(
attrs?.fileList || attrs?.['file-list'] || [], attrs?.fileList || attrs?.['file-list'] || [],
); );
@ -399,12 +405,14 @@ const withPreviewUpload = () => {
file: UploadFile, file: UploadFile,
originFileList: Array<File>, originFileList: Array<File>,
) => { ) => {
// 文件大小限制
if (maxSize.value && (file.size || 0) / 1024 / 1024 > maxSize.value) { if (maxSize.value && (file.size || 0) / 1024 / 1024 > maxSize.value) {
message.error($t('ui.formRules.sizeLimit', [maxSize.value])); message.error($t('ui.formRules.sizeLimit', [maxSize.value]));
file.status = 'removed'; file.status = 'removed';
return false; return false;
} }
// 多选或者非图片不唤起裁剪框
// 图片裁剪处理
if ( if (
attrs.crop && attrs.crop &&
!attrs.multiple && !attrs.multiple &&
@ -412,14 +420,11 @@ const withPreviewUpload = () => {
isImageFile(file) isImageFile(file)
) { ) {
file.status = 'removed'; file.status = 'removed';
// antd Upload组件问题 file参数获取的是UploadFile类型对象无法取到File类型 所以通过originFileList[0]获取
const blob = await cropImage(originFileList[0], aspectRatio.value); const blob = await cropImage(originFileList[0], aspectRatio.value);
return new Promise((resolve, reject) => { if (!blob) {
if (!blob) { throw new Error($t('ui.crop.errorTip'));
return reject(new Error($t('ui.crop.errorTip'))); }
} return blob;
resolve(blob);
});
} }
return attrs.beforeUpload?.(file) ?? true; return attrs.beforeUpload?.(file) ?? true;
@ -427,12 +432,9 @@ const withPreviewUpload = () => {
const handleChange = (event: UploadChangeParam) => { const handleChange = (event: UploadChangeParam) => {
try { try {
// 行内写法 handleChange: (event) => {}
attrs.handleChange?.(event); attrs.handleChange?.(event);
// template写法 @handle-change="(event) => {}"
attrs.onHandleChange?.(event); attrs.onHandleChange?.(event);
} catch (error) { } catch (error) {
// Avoid breaking internal v-model sync on user handler errors
console.error(error); console.error(error);
} }
fileList.value = event.fileList.filter( fileList.value = event.fileList.filter(
@ -449,21 +451,88 @@ const withPreviewUpload = () => {
await previewImage(file, previewVisible, fileList); await previewImage(file, previewVisible, fileList);
}; };
const renderUploadButton = (): any => { const renderUploadButton = () => {
const isDisabled = attrs.disabled; if (attrs.disabled) return null;
// 如果禁用,不渲染上传按钮
if (isDisabled) {
return null;
}
// 否则渲染默认上传按钮
return isEmpty(slots) return isEmpty(slots)
? createDefaultSlotsWithUpload(listType, placeholder) ? createDefaultUploadSlots(listType, placeholder)
: slots; : 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 | Sortable>(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( watch(
() => attrs.modelValue, () => attrs.modelValue,
(res) => { (res) => {
@ -471,18 +540,28 @@ const withPreviewUpload = () => {
}, },
); );
onMounted(initSortable);
onUnmounted(() => {
sortableInstance.value?.destroy();
removeDragStyle();
});
return () => return () =>
h( h(
Upload, 'div',
{ { 'data-upload-id': uploadId, class: 'w-full' },
...props, h(
...attrs, Upload,
fileList: fileList.value, {
beforeUpload: handleBeforeUpload, ...props,
onChange: handleChange, ...attrs,
onPreview: handlePreview, fileList: fileList.value,
}, beforeUpload: handleBeforeUpload,
renderUploadButton(), onChange: handleChange,
onPreview: handlePreview,
},
renderUploadButton() as any,
),
); );
}, },
}); });

View File

@ -199,7 +199,7 @@ setupVbenVxeTable({
vxeUI.renderer.add('CellOperation', { vxeUI.renderer.add('CellOperation', {
renderTableDefault({ attrs, options, props }, { column, row }) { renderTableDefault({ attrs, options, props }, { column, row }) {
const defaultProps = { size: 'small', type: 'link', ...props }; const defaultProps = { size: 'small', type: 'link', ...props };
let align = 'end'; let align: string;
switch (column.align) { switch (column.align) {
case 'center': { case 'center': {
align = 'center'; align = 'center';

View File

@ -16,10 +16,10 @@ export namespace CrmCustomerLimitConfigApi {
/** 客户限制配置类型 */ /** 客户限制配置类型 */
export enum LimitConfType { export enum LimitConfType {
/** 锁定客户数限制 */
CUSTOMER_LOCK_LIMIT = 2,
/** 拥有客户数限制 */ /** 拥有客户数限制 */
CUSTOMER_QUANTITY_LIMIT = 1, CUSTOMER_QUANTITY_LIMIT = 1,
/** 锁定客户数限制 */
CUSTOMER_LOCK_LIMIT = 2,
} }
/** 查询客户限制配置列表 */ /** 查询客户限制配置列表 */

View File

@ -35,11 +35,11 @@ export namespace CrmPermissionApi {
* CRM * CRM
*/ */
export enum BizTypeEnum { export enum BizTypeEnum {
CRM_BUSINESS = 4, // 商机
CRM_CLUE = 1, // 线索 CRM_CLUE = 1, // 线索
CRM_CONTACT = 3, // 联系人
CRM_CONTRACT = 5, // 合同
CRM_CUSTOMER = 2, // 客户 CRM_CUSTOMER = 2, // 客户
CRM_CONTACT = 3, // 联系人
CRM_BUSINESS = 4, // 商机
CRM_CONTRACT = 5, // 合同
CRM_PRODUCT = 6, // 产品 CRM_PRODUCT = 6, // 产品
CRM_RECEIVABLE = 7, // 回款 CRM_RECEIVABLE = 7, // 回款
CRM_RECEIVABLE_PLAN = 8, // 回款计划 CRM_RECEIVABLE_PLAN = 8, // 回款计划

View File

@ -37,16 +37,12 @@ export function getModbusPoint(id: number) {
} }
/** 创建 Modbus 点位配置 */ /** 创建 Modbus 点位配置 */
export function createModbusPoint( export function createModbusPoint(data: IotDeviceModbusPointApi.ModbusPoint) {
data: IotDeviceModbusPointApi.ModbusPoint,
) {
return requestClient.post('/iot/device-modbus-point/create', data); return requestClient.post('/iot/device-modbus-point/create', data);
} }
/** 更新 Modbus 点位配置 */ /** 更新 Modbus 点位配置 */
export function updateModbusPoint( export function updateModbusPoint(data: IotDeviceModbusPointApi.ModbusPoint) {
data: IotDeviceModbusPointApi.ModbusPoint,
) {
return requestClient.put('/iot/device-modbus-point/update', data); return requestClient.put('/iot/device-modbus-point/update', data);
} }

View File

@ -119,7 +119,9 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
response.data = apiEncrypt.decryptResponse(response.data); response.data = apiEncrypt.decryptResponse(response.data);
} catch (error) { } catch (error) {
console.error('响应数据解密失败:', error); console.error('响应数据解密失败:', error);
throw new Error(`响应数据解密失败: ${(error as Error).message}`); throw new Error(`响应数据解密失败: ${(error as Error).message}`, {
cause: error,
});
} }
} }
return response; return response;

View File

@ -21,7 +21,7 @@ export function useDescription(options?: Partial<DescriptionProps>) {
inheritAttrs: false, inheritAttrs: false,
setup(_props, { attrs, slots }) { setup(_props, { attrs, slots }) {
return () => { return () => {
// @ts-ignore - 避免类型实例化过深 // @ts-expect-error - 避免类型实例化过深
return h(Description, { ...propsState, ...attrs }, slots); return h(Description, { ...propsState, ...attrs }, slots);
}; };
}, },

View File

@ -47,6 +47,7 @@ interface Props {
clearable?: boolean; clearable?: boolean;
showAllLevels?: boolean; showAllLevels?: boolean;
separator?: string; separator?: string;
// eslint-disable-next-line vue/require-default-prop
formCreateInject?: any; formCreateInject?: any;
} }
@ -71,7 +72,7 @@ async function loadAreaTree(): Promise<void> {
const data = await getAreaTree(); const data = await getAreaTree();
// level // level
areaTree.value = filterTreeByLevel(data || [], props.level); areaTree.value = filterTreeByLevel((data || []) as AreaVO[], props.level);
} catch (error) { } catch (error) {
console.warn('[AreaSelect] 加载地区数据失败:', error); console.warn('[AreaSelect] 加载地区数据失败:', error);
areaTree.value = []; 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) { if (value === undefined || value === null) {
emit('update:modelValue', undefined); emit('update:modelValue', undefined);
emit('update:value', undefined); emit('update:value', undefined);

View File

@ -39,15 +39,16 @@ interface DeptVO {
status?: number; status?: number;
} }
// TODO @puhui999linter
/** 接受父组件参数 */ /** 接受父组件参数 */
interface Props { interface Props {
// eslint-disable-next-line vue/require-default-prop
modelValue?: number | number[] | string | string[]; modelValue?: number | number[] | string | string[];
multiple?: boolean; multiple?: boolean;
returnType?: 'id' | 'name'; returnType?: 'id' | 'name';
defaultCurrentDept?: boolean; defaultCurrentDept?: boolean;
disabled?: boolean; disabled?: boolean;
placeholder?: string; placeholder?: string;
// eslint-disable-next-line vue/require-default-prop
formCreateInject?: any; formCreateInject?: any;
} }

View File

@ -29,6 +29,7 @@ interface Props {
allowfullscreen?: boolean; allowfullscreen?: boolean;
loading?: 'eager' | 'lazy'; loading?: 'eager' | 'lazy';
sandbox?: string; sandbox?: string;
// eslint-disable-next-line vue/require-default-prop
formCreateInject?: any; formCreateInject?: any;
} }

View File

@ -193,7 +193,8 @@ export function useApiSelect(option: ApiSelectProps) {
let parse: any = null; let parse: any = null;
if (props.parseFunc) { 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}`)(); parse = new Function(`return ${props.parseFunc}`)();
} }
return parse; return parse;

View File

@ -11,14 +11,14 @@ import formCreate from '@form-create/ant-design-vue';
import { apiSelectRule } from '#/components/form-create/rules/data'; import { apiSelectRule } from '#/components/form-create/rules/data';
import { import {
useAreaSelectRule,
useDictSelectRule, useDictSelectRule,
useEditorRule, useEditorRule,
useIframeRule,
useSelectRule, useSelectRule,
useUploadFileRule, useUploadFileRule,
useUploadImageRule, useUploadImageRule,
useUploadImagesRule, useUploadImagesRule,
useIframeRule,
useAreaSelectRule,
} from './rules'; } from './rules';
/** 编码表单 Conf */ /** 编码表单 Conf */

View File

@ -1,4 +1,3 @@
/* eslint-disable no-template-curly-in-string */
const selectRule = [ const selectRule = [
{ {
type: 'select', type: 'select',
@ -134,7 +133,7 @@ const apiSelectRule = [
type: 'input', type: 'input',
field: 'labelField', field: 'labelField',
title: 'label 属性', title: 'label 属性',
info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}', info: `可以使用 el 表达式:\${},来实现复杂数据组合。如:\${nickname}-\${id}`,
props: { props: {
placeholder: 'nickname', placeholder: 'nickname',
}, },
@ -143,7 +142,7 @@ const apiSelectRule = [
type: 'input', type: 'input',
field: 'valueField', field: 'valueField',
title: 'value 属性', title: 'value 属性',
info: '可以使用 el 表达式:${属性},来实现复杂数据组合。如:${nickname}-${id}', info: `可以使用 el 表达式:\${},来实现复杂数据组合。如:\${nickname}-\${id}`,
props: { props: {
placeholder: 'id', placeholder: 'id',
}, },

View File

@ -47,6 +47,7 @@ onMounted(async () => {
</script> </script>
<template> <template>
<!-- eslint-disable-next-line vue/no-v-html -->
<div ref="contentRef" class="markdown-view" v-html="renderedMarkdown"></div> <div ref="contentRef" class="markdown-view" v-html="renderedMarkdown"></div>
</template> </template>

View File

@ -146,6 +146,7 @@ async function handlePreview(file: UploadFile) {
async function handleRemove(file: UploadFile) { async function handleRemove(file: UploadFile) {
if (fileList.value) { if (fileList.value) {
const index = fileList.value.findIndex((item) => item.uid === file.uid); const index = fileList.value.findIndex((item) => item.uid === file.uid);
// oxlint-disable-next-line no-unused-expressions
index !== -1 && fileList.value.splice(index, 1); index !== -1 && fileList.value.splice(index, 1);
const value = getValue(); const value = getValue();
isInnerOperate.value = true; isInnerOperate.value = true;
@ -350,6 +351,8 @@ function getValue() {
<style> <style>
.ant-upload-select-picture-card { .ant-upload-select-picture-card {
@apply flex items-center justify-center; display: flex;
align-items: center;
justify-content: center;
} }
</style> </style>

View File

@ -83,6 +83,7 @@ export const useAuthStore = defineStore('auth', () => {
if (accessStore.loginExpired) { if (accessStore.loginExpired) {
accessStore.setLoginExpired(false); accessStore.setLoginExpired(false);
} else { } else {
// oxlint-disable-next-line no-unused-expressions
onSuccess onSuccess
? await onSuccess?.() ? await onSuccess?.()
: await router.push( : await router.push(
@ -132,6 +133,7 @@ export const useAuthStore = defineStore('auth', () => {
async function fetchUserInfo() { async function fetchUserInfo() {
// 加载 // 加载
// eslint-disable-next-line no-useless-assignment
let authPermissionInfo: AuthPermissionInfo | null = null; let authPermissionInfo: AuthPermissionInfo | null = null;
authPermissionInfo = await getAuthPermissionInfoApi(); authPermissionInfo = await getAuthPermissionInfoApi();
// userStore // userStore

View File

@ -571,6 +571,7 @@ onMounted(async () => {
size="small" size="small"
@click="openChatConversationUpdateForm" @click="openChatConversationUpdateForm"
> >
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-html="activeConversation?.modelName"></span> <span v-html="activeConversation?.modelName"></span>
<IconifyIcon icon="lucide:settings" class="ml-2 size-4" /> <IconifyIcon icon="lucide:settings" class="ml-2 size-4" />
</Button> </Button>

View File

@ -21,6 +21,7 @@ const imageListRef = ref<any>(); // image 列表 ref
const dall3Ref = ref<any>(); // dall3(openai) ref const dall3Ref = ref<any>(); // dall3(openai) ref
const midjourneyRef = ref<any>(); // midjourney ref const midjourneyRef = ref<any>(); // midjourney ref
const stableDiffusionRef = ref<any>(); // stable diffusion ref const stableDiffusionRef = ref<any>(); // stable diffusion ref
// @ts-expect-error: template ref is retained for future provider expansion
const commonRef = ref<any>(); // stable diffusion ref const commonRef = ref<any>(); // stable diffusion ref
const selectPlatform = ref('common'); // const selectPlatform = ref('common'); //
@ -45,7 +46,9 @@ const platformOptions = [
const models = ref<AiModelModelApi.Model[]>([]); // const models = ref<AiModelModelApi.Model[]>([]); //
/** 绘画 start */ /** 绘画 start */
async function handleDrawStart() {} function handleDrawStart() {
// drawing state is handled by child components
}
/** 绘画 complete */ /** 绘画 complete */
async function handleDrawComplete() { async function handleDrawComplete() {

View File

@ -150,10 +150,12 @@ defineExpose({
ref="mdContainerRef" ref="mdContainerRef"
class="wh-full overflow-y-auto" class="wh-full overflow-y-auto"
> >
<!-- eslint-disable vue/no-v-html -->
<div <div
class="flex flex-col items-center justify-center" class="flex flex-col items-center justify-center"
v-html="html" v-html="html"
></div> ></div>
<!-- eslint-enable vue/no-v-html -->
</div> </div>
<div ref="mindMapRef" class="wh-full"> <div ref="mindMapRef" class="wh-full">
<svg <svg

View File

@ -20,6 +20,7 @@ const currentSong = inject<any>('currentSong', {});
{{ currentSong.date }} {{ currentSong.date }}
</div> </div>
<Button size="small" shape="round" class="my-2">信息复用</Button> <Button size="small" shape="round" class="my-2">信息复用</Button>
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="text-xs" v-html="currentSong.lyric"></div> <div class="text-xs" v-html="currentSong.lyric"></div>
</Card> </Card>
</template> </template>

View File

@ -106,7 +106,9 @@ async function goRun() {
try { try {
convertedParams[paramKey] = convertParamValue(value, dataType); convertedParams[paramKey] = convertParamValue(value, dataType);
} catch (error: any) { } 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 { try {
return JSON.parse(value); return JSON.parse(value);
} catch (error: any) { } catch (error: any) {
throw new Error(`JSON格式错误: ${error.message}`); throw new Error(`JSON格式错误: ${error.message}`, { cause: error });
} }
} }
default: { default: {

View File

@ -22,7 +22,7 @@ import {
import { Button, ButtonGroup, message, Modal, Tooltip } from 'ant-design-vue'; 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 tokenSimulation from 'bpmn-js-token-simulation';
import BpmnModeler from 'bpmn-js/lib/Modeler'; import BpmnModeler from 'bpmn-js/lib/Modeler';
// //
@ -132,6 +132,7 @@ const emit = defineEmits([
'element-click', 'element-click',
]); ]);
// @ts-expect-error: file input ref is set imperatively by the template
const bpmnCanvas = ref(); const bpmnCanvas = ref();
const refFile = ref(); const refFile = ref();
@ -178,6 +179,7 @@ const additionalModules = computed(() => {
) { ) {
Modules.push(...(props.additionalModel as any[])); Modules.push(...(props.additionalModel as any[]));
} else { } else {
// oxlint-disable-next-line no-unused-expressions
props.additionalModel && Modules.push(props.additionalModel); props.additionalModel && Modules.push(props.additionalModel);
} }
@ -417,6 +419,7 @@ const processSimulation = () => {
// bpmnModeler.get('toggleMode', 'strict'), // bpmnModeler.get('toggleMode', 'strict'),
// "bpmnModeler.get('toggleMode')", // "bpmnModeler.get('toggleMode')",
// ); // );
// oxlint-disable-next-line no-unused-expressions
props.simulation && bpmnModeler.get('toggleMode', 'strict').toggleMode(); props.simulation && bpmnModeler.get('toggleMode', 'strict').toggleMode();
}; };
const processRedo = () => { const processRedo = () => {

View File

@ -7,7 +7,7 @@ import { hasPrimaryModifier } from 'diagram-js/lib/util/Mouse';
/** /**
* A provider for BPMN 2.0 elements context pad * A provider for BPMN 2.0 elements context pad
*/ */
export default function ContextPadProvider( function ContextPadProvider(
config, config,
injector, injector,
eventBus, eventBus,
@ -57,6 +57,8 @@ export default function ContextPadProvider(
}); });
} }
export default ContextPadProvider;
ContextPadProvider.$inject = [ ContextPadProvider.$inject = [
'config.contextPad', 'config.contextPad',
'injector', 'injector',

View File

@ -1,6 +1,6 @@
import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider'; import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider';
export default function CustomPalette( function CustomPalette(
palette, palette,
create, create,
elementFactory, elementFactory,
@ -24,11 +24,21 @@ export default function CustomPalette(
); );
} }
const F = function () {}; // 核心,利用空对象作为中介; CustomPalette.$inject = [
F.prototype = PaletteProvider.prototype; // 核心将父类的原型赋值给空对象F 'palette',
'create',
'elementFactory',
'spaceTool',
'lassoTool',
'handTool',
'globalConnect',
'translate',
];
// 利用中介函数重写原型链方法 CustomPalette.prototype = Object.create(PaletteProvider.prototype);
F.prototype.getPaletteEntries = function () { CustomPalette.prototype.constructor = CustomPalette;
CustomPalette.prototype.getPaletteEntries = function () {
const actions = {}; const actions = {};
const create = this._create; const create = this._create;
const elementFactory = this._elementFactory; const elementFactory = this._elementFactory;
@ -94,8 +104,7 @@ F.prototype.getPaletteEntries = function () {
'hand-tool': { 'hand-tool': {
group: 'tools', group: 'tools',
className: 'bpmn-icon-hand-tool', className: 'bpmn-icon-hand-tool',
title: '激活抓手工具', title: translate('Activate the hand tool'),
// title: translate("Activate the hand tool"),
action: { action: {
click(event) { click(event) {
handTool.activateHand(event); handTool.activateHand(event);
@ -219,16 +228,4 @@ F.prototype.getPaletteEntries = function () {
return actions; return actions;
}; };
CustomPalette.$inject = [ export default CustomPalette;
'palette',
'create',
'elementFactory',
'spaceTool',
'lassoTool',
'handTool',
'globalConnect',
'translate',
];
CustomPalette.prototype = new F(); // 核心,将 F的实例赋值给子类
CustomPalette.prototype.constructor = CustomPalette; // 修复子类CustomPalette的构造器指向防止原型链的混乱

View File

@ -1,7 +1,7 @@
/** /**
* A palette provider for BPMN 2.0 elements. * A palette provider for BPMN 2.0 elements.
*/ */
export default function PaletteProvider( function PaletteProvider(
palette, palette,
create, create,
elementFactory, elementFactory,
@ -23,6 +23,8 @@ export default function PaletteProvider(
palette.registerProvider(this); palette.registerProvider(this);
} }
export default PaletteProvider;
PaletteProvider.$inject = [ PaletteProvider.$inject = [
'palette', 'palette',
'create', 'create',

View File

@ -1,4 +1,3 @@
/* eslint-disable no-template-curly-in-string */
/** /**
* This is a sample file that should be replaced with the actual translation. * This is a sample file that should be replaced with the actual translation.
* *
@ -238,10 +237,8 @@ export default {
'Due Date': '到期时间', 'Due Date': '到期时间',
'Follow Up Date': '跟踪日期', 'Follow Up Date': '跟踪日期',
Priority: '优先级', Priority: '优先级',
'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 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`,
'跟踪日期必须符合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 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: '变量', Variables: '变量',
'Candidate Starter Configuration': '候选人起动器配置', 'Candidate Starter Configuration': '候选人起动器配置',
'Candidate Starter Groups': '候选人起动器组', 'Candidate Starter Groups': '候选人起动器组',

View File

@ -39,7 +39,7 @@ watch(
val += val +=
props.businessObject.eventDefinitions[0]?.$type.split(':')[1] || ''; props.businessObject.eventDefinitions[0]?.$type.split(':')[1] || '';
} }
// @ts-ignore // @ts-expect-error: async component registry is indexed dynamically
customConfigComponent.value = ( customConfigComponent.value = (
CustomConfigMap as Record<string, { component: Component }> CustomConfigMap as Record<string, { component: Component }>
)[val]?.component; )[val]?.component;

View File

@ -1,4 +1,3 @@
<!-- eslint-disable no-unused-vars -->
<script lang="ts" setup> <script lang="ts" setup>
import { inject, nextTick, onBeforeUnmount, ref, toRaw, watch } from 'vue'; import { inject, nextTick, onBeforeUnmount, ref, toRaw, watch } from 'vue';
@ -66,13 +65,13 @@ const bpmnElement = ref<any>(null);
const multiLoopInstance = ref<any>(null); const multiLoopInstance = ref<any>(null);
declare global { declare global {
interface Window { interface Window {
// @ts-ignore
bpmnInstances?: () => any; bpmnInstances?: () => any;
} }
} }
const bpmnInstances = () => (window as any)?.bpmnInstances; 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 // eslint-disable-next-line unused-imports/no-unused-vars
const getElementLoop = (businessObject: any): void => { const getElementLoop = (businessObject: any): void => {
if (!businessObject.loopCharacteristics) { if (!businessObject.loopCharacteristics) {
@ -141,8 +140,7 @@ const changeLoopCharacteristicsType = (type: any): void => {
isSequential: true, isSequential: true,
}) })
: bpmnInstances().moddle.create('bpmn:MultiInstanceLoopCharacteristics', { : 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), { bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
loopCharacteristics: toRaw(multiLoopInstance.value), loopCharacteristics: toRaw(multiLoopInstance.value),
@ -233,7 +231,7 @@ const updateLoopAsync = (key: any): void => {
extensionElements: null, extensionElements: null,
}; };
} else { } else {
// @ts-ignore // @ts-expect-error: dynamic async flags are assigned by runtime key
asyncAttr[key] = loopInstanceForm.value[key]; asyncAttr[key] = loopInstanceForm.value[key];
} }
bpmnInstances().modeling.updateModdleProperties( bpmnInstances().modeling.updateModdleProperties(
@ -247,23 +245,23 @@ const changeConfig = (config: string): void => {
switch (config) { switch (config) {
case '会签': { case '会签': {
changeLoopCharacteristicsType('ParallelMultiInstance'); changeLoopCharacteristicsType('ParallelMultiInstance');
// eslint-disable-next-line no-template-curly-in-string
updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }'); updateLoopCondition(`\${ nrOfCompletedInstances >= nrOfInstances }`);
break; break;
} }
case '依次审批': { case '依次审批': {
changeLoopCharacteristicsType('SequentialMultiInstance'); changeLoopCharacteristicsType('SequentialMultiInstance');
updateLoopCardinality('1'); updateLoopCardinality('1');
// eslint-disable-next-line no-template-curly-in-string
updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }'); updateLoopCondition(`\${ nrOfCompletedInstances >= nrOfInstances }`);
break; break;
} }
case '或签': { case '或签': {
changeLoopCharacteristicsType('ParallelMultiInstance'); changeLoopCharacteristicsType('ParallelMultiInstance');
// eslint-disable-next-line no-template-curly-in-string
updateLoopCondition('${ nrOfCompletedInstances > 0 }'); updateLoopCondition(`\${ nrOfCompletedInstances > 0 }`);
break; break;
} }
@ -331,8 +329,8 @@ const updateLoopCharacteristics = (): void => {
if (approveMethod.value === ApproveMethodType.APPROVE_BY_RATIO) { if (approveMethod.value === ApproveMethodType.APPROVE_BY_RATIO) {
multiLoopInstance.value = bpmnInstances().moddle.create( multiLoopInstance.value = bpmnInstances().moddle.create(
'bpmn:MultiInstanceLoopCharacteristics', 'bpmn:MultiInstanceLoopCharacteristics',
// eslint-disable-next-line no-template-curly-in-string
{ isSequential: false, collection: '${coll_userList}' }, { isSequential: false, collection: `\${coll_userList}` },
); );
multiLoopInstance.value.completionCondition = multiLoopInstance.value.completionCondition =
bpmnInstances().moddle.create('bpmn:FormalExpression', { bpmnInstances().moddle.create('bpmn:FormalExpression', {
@ -344,20 +342,19 @@ const updateLoopCharacteristics = (): void => {
if (approveMethod.value === ApproveMethodType.ANY_APPROVE) { if (approveMethod.value === ApproveMethodType.ANY_APPROVE) {
multiLoopInstance.value = bpmnInstances().moddle.create( multiLoopInstance.value = bpmnInstances().moddle.create(
'bpmn:MultiInstanceLoopCharacteristics', 'bpmn:MultiInstanceLoopCharacteristics',
// eslint-disable-next-line no-template-curly-in-string
{ isSequential: false, collection: '${coll_userList}' }, { isSequential: false, collection: `\${coll_userList}` },
); );
multiLoopInstance.value.completionCondition = multiLoopInstance.value.completionCondition =
bpmnInstances().moddle.create('bpmn:FormalExpression', { 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) { if (approveMethod.value === ApproveMethodType.SEQUENTIAL_APPROVE) {
multiLoopInstance.value = bpmnInstances().moddle.create( multiLoopInstance.value = bpmnInstances().moddle.create(
'bpmn:MultiInstanceLoopCharacteristics', '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( multiLoopInstance.value.loopCardinality = bpmnInstances().moddle.create(
'bpmn:FormalExpression', 'bpmn:FormalExpression',
@ -367,8 +364,7 @@ const updateLoopCharacteristics = (): void => {
); );
multiLoopInstance.value.completionCondition = multiLoopInstance.value.completionCondition =
bpmnInstances().moddle.create('bpmn:FormalExpression', { 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), { bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {

View File

@ -53,7 +53,7 @@ watch(
() => props.type, () => props.type,
() => { () => {
if (props.type) { if (props.type) {
// @ts-ignore // @ts-expect-error: installed task component map is indexed dynamically
witchTaskComponent.value = installedComponent[props.type].component; witchTaskComponent.value = installedComponent[props.type].component;
} }
}, },

View File

@ -65,7 +65,7 @@ const initCallActivity = () => {
// //
Object.keys(formData.value).forEach((key: string) => { Object.keys(formData.value).forEach((key: string) => {
// @ts-ignore // @ts-expect-error: form state is updated through dynamic schema keys
formData.value[key] = formData.value[key] =
bpmnElement.value.businessObject[key] ?? bpmnElement.value.businessObject[key] ??
formData.value[key as keyof FormData]; formData.value[key as keyof FormData];
@ -183,6 +183,7 @@ const updateElementExtensions = () => {
watch( watch(
() => props.id, () => props.id,
(val) => { (val) => {
// oxlint-disable-next-line no-unused-expressions
val && val &&
val.length > 0 && val.length > 0 &&
nextTick(() => { nextTick(() => {

View File

@ -82,7 +82,6 @@ onMounted(() => {
bpmnRootElements.value bpmnRootElements.value
.filter((el: any) => el.$type === 'bpmn:Message') .filter((el: any) => el.$type === 'bpmn:Message')
.forEach((m: any) => { .forEach((m: any) => {
// @ts-ignore
if (bpmnMessageRefsMap.value) { if (bpmnMessageRefsMap.value) {
bpmnMessageRefsMap.value[m.id] = m; bpmnMessageRefsMap.value[m.id] = m;
} }

View File

@ -34,7 +34,6 @@ const bpmnInstances = () => (window as any)?.bpmnInstances;
const resetTaskForm = () => { const resetTaskForm = () => {
for (const key in defaultTaskForm.value) { for (const key in defaultTaskForm.value) {
// @ts-ignore
scriptTaskForm.value[key] = scriptTaskForm.value[key] =
bpmnElement.value?.businessObject[ bpmnElement.value?.businessObject[
key as keyof typeof defaultTaskForm.value key as keyof typeof defaultTaskForm.value

View File

@ -1,3 +1,4 @@
<!-- eslint-disable unicorn/no-nested-ternary -->
<!-- eslint-disable prettier/prettier --> <!-- eslint-disable prettier/prettier -->
<script lang="ts" setup> <script lang="ts" setup>
import { inject, nextTick, onBeforeUnmount, ref, watch } from 'vue'; import { inject, nextTick, onBeforeUnmount, ref, watch } from 'vue';
@ -206,9 +207,9 @@ const updateHttpExtensions = (force = false) => {
const persisted = HTTP_BOOLEAN_FIELDS.has(name) const persisted = HTTP_BOOLEAN_FIELDS.has(name)
? String(!!rawValue) ? String(!!rawValue)
: (rawValue === undefined : rawValue === undefined
? '' ? ''
: rawValue.toString()); : rawValue.toString();
desiredEntries.push([name, persisted]); desiredEntries.push([name, persisted]);
}); });

View File

@ -70,6 +70,7 @@ const deptTreeOptions = ref<any>(); // 部门树
const postOptions = ref<SystemPostApi.Post[]>([]); // const postOptions = ref<SystemPostApi.Post[]>([]); //
const userOptions = ref<SystemUserApi.User[]>([]); // const userOptions = ref<SystemUserApi.User[]>([]); //
const userGroupOptions = ref<BpmUserGroupApi.UserGroup[]>([]); // const userGroupOptions = ref<BpmUserGroupApi.UserGroup[]>([]); //
// @ts-expect-error: tree ref instance type is provided by the UI library at runtime
const treeRef = ref<any>(); const treeRef = ref<any>();
const { formFieldOptions } = useFormFieldsPermission(FieldPermissionType.READ); const { formFieldOptions } = useFormFieldsPermission(FieldPermissionType.READ);
@ -128,7 +129,7 @@ const resetTaskForm = () => {
// eslint-disable-next-line unicorn/prefer-switch // eslint-disable-next-line unicorn/prefer-switch
if (userTaskForm.value.candidateStrategy === CandidateStrategy.EXPRESSION) { if (userTaskForm.value.candidateStrategy === CandidateStrategy.EXPRESSION) {
// input // input
// @ts-ignore // @ts-expect-error: expression strategy stores a scalar in an array-shaped field
userTaskForm.value.candidateParam = [candidateParamStr]; userTaskForm.value.candidateParam = [candidateParamStr];
} else if ( } else if (
userTaskForm.value.candidateStrategy === userTaskForm.value.candidateStrategy ===
@ -152,7 +153,7 @@ const resetTaskForm = () => {
userTaskForm.value.candidateStrategy === userTaskForm.value.candidateStrategy ===
CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER
) { ) {
// @ts-ignore // @ts-expect-error: dynamic candidate param shape varies by strategy
userTaskForm.value.candidateParam = +candidateParamStr; userTaskForm.value.candidateParam = +candidateParamStr;
deptLevel.value = +candidateParamStr; deptLevel.value = +candidateParamStr;
} else if ( } else if (
@ -303,7 +304,7 @@ const openProcessExpressionDialog = async () => {
const selectProcessExpression = ( const selectProcessExpression = (
expression: BpmProcessExpressionApi.ProcessExpression, expression: BpmProcessExpressionApi.ProcessExpression,
) => { ) => {
// @ts-ignore // @ts-expect-error: modal helper exposes runtime methods outside static typing
userTaskForm.value.candidateParam = [expression.expression]; userTaskForm.value.candidateParam = [expression.expression];
updateElementTask(); updateElementTask();
}; };
@ -311,7 +312,7 @@ const selectProcessExpression = (
const handleFormUserChange = (e: any) => { const handleFormUserChange = (e: any) => {
if (e === 'PROCESS_START_USER_ID') { if (e === 'PROCESS_START_USER_ID') {
userTaskForm.value.candidateParam = []; userTaskForm.value.candidateParam = [];
// @ts-ignore // @ts-expect-error: modal helper exposes runtime methods outside static typing
userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER; userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER;
} }
updateElementTask(); updateElementTask();

View File

@ -1,6 +1,6 @@
import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer'; import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer';
export default function CustomRenderer( function CustomRenderer(
config, config,
eventBus, eventBus,
styles, styles,
@ -19,12 +19,10 @@ export default function CustomRenderer(
2000, 2000,
); );
this.handlers.label = function () { this.handlers.label = () => null;
return null;
};
} }
const F = function () {}; // 核心,利用空对象作为中介; CustomRenderer.prototype = Object.create(BpmnRenderer.prototype);
F.prototype = BpmnRenderer.prototype; // 核心将父类的原型赋值给空对象F CustomRenderer.prototype.constructor = CustomRenderer;
CustomRenderer.prototype = new F(); // 核心,将 F的实例赋值给子类
CustomRenderer.prototype.constructor = CustomRenderer; // 修复子类CustomRenderer的构造器指向防止原型链的混乱 export default CustomRenderer;

View File

@ -1,7 +1,8 @@
import BpmnRules from 'bpmn-js/lib/features/rules/BpmnRules'; import BpmnRules from 'bpmn-js/lib/features/rules/BpmnRules';
// eslint-disable-next-line n/no-extraneous-import
import inherits from 'inherits'; import inherits from 'inherits';
export default function CustomRules(eventBus) { function CustomRules(eventBus) {
BpmnRules.call(this, eventBus); BpmnRules.call(this, eventBus);
} }
@ -14,3 +15,5 @@ CustomRules.prototype.canDrop = function () {
CustomRules.prototype.canMove = function () { CustomRules.prototype.canMove = function () {
return false; return false;
}; };
export default CustomRules;

View File

@ -1,4 +1,5 @@
function xmlStr2XmlObj(xmlStr) { function xmlStr2XmlObj(xmlStr) {
// eslint-disable-next-line no-useless-assignment
let xmlObj = {}; let xmlObj = {};
if (document.all) { if (document.all) {
const xmlDom = new window.ActiveXObject('Microsoft.XMLDOM'); const xmlDom = new window.ActiveXObject('Microsoft.XMLDOM');

View File

@ -71,6 +71,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
// @ts-expect-error: composable typing does not preserve this node schema exactly
const { nodeName, showInput, clickIcon, changeNodeName, inputRef } = const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
useNodeName(BpmNodeTypeEnum.TRIGGER_NODE); useNodeName(BpmNodeTypeEnum.TRIGGER_NODE);
// //

View File

@ -25,12 +25,13 @@ const emits = defineEmits<{
}>(); }>();
// //
const readonly = inject<Boolean>('readonly'); const readonly = inject<boolean>('readonly');
/** 监控节点的变化 */ /** 监控节点的变化 */
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
/** 节点名称编辑 */ /** 节点名称编辑 */
// @ts-expect-error: composable typing does not preserve this node schema exactly
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.CHILD_PROCESS_NODE, BpmNodeTypeEnum.CHILD_PROCESS_NODE,

View File

@ -27,10 +27,11 @@ const emits = defineEmits<{
'update:flowNode': [node: SimpleFlowNode | undefined]; 'update:flowNode': [node: SimpleFlowNode | undefined];
}>(); }>();
// //
const readonly = inject<Boolean>('readonly'); const readonly = inject<boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
// @ts-expect-error: composable typing does not preserve this node schema exactly
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.COPY_TASK_NODE, BpmNodeTypeEnum.COPY_TASK_NODE,

View File

@ -25,10 +25,11 @@ const emits = defineEmits<{
'update:flowNode': [node: SimpleFlowNode | undefined]; 'update:flowNode': [node: SimpleFlowNode | undefined];
}>(); }>();
// //
const readonly = inject<Boolean>('readonly'); const readonly = inject<boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
// @ts-expect-error: composable typing does not preserve this node schema exactly
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.DELAY_TIMER_NODE, BpmNodeTypeEnum.DELAY_TIMER_NODE,

View File

@ -20,7 +20,7 @@ const props = defineProps({
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const readonly = inject<Boolean>('readonly'); const readonly = inject<boolean>('readonly');
const processInstance = inject<Ref<any>>('processInstance', ref({})); const processInstance = inject<Ref<any>>('processInstance', ref({}));
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({

View File

@ -41,7 +41,7 @@ const emits = defineEmits<{
const { proxy } = getCurrentInstance() as any; const { proxy } = getCurrentInstance() as any;
// //
const readonly = inject<Boolean>('readonly'); const readonly = inject<boolean>('readonly');
const currentNode = ref<SimpleFlowNode>(props.flowNode); const currentNode = ref<SimpleFlowNode>(props.flowNode);
watch( watch(

View File

@ -46,7 +46,7 @@ const emits = defineEmits<{
const { proxy } = getCurrentInstance() as any; const { proxy } = getCurrentInstance() as any;
// //
const readonly = inject<Boolean>('readonly'); const readonly = inject<boolean>('readonly');
const currentNode = ref<SimpleFlowNode>(props.flowNode); const currentNode = ref<SimpleFlowNode>(props.flowNode);

View File

@ -36,7 +36,7 @@ const props = defineProps({
const emits = defineEmits(['update:childNode']); const emits = defineEmits(['update:childNode']);
const popoverShow = ref(false); const popoverShow = ref(false);
const readonly = inject<Boolean>('readonly'); // const readonly = inject<boolean>('readonly'); //
function addNode(type: number) { function addNode(type: number) {
// //

View File

@ -36,7 +36,7 @@ const emits = defineEmits<{
const currentNode = ref<SimpleFlowNode>(props.flowNode); const currentNode = ref<SimpleFlowNode>(props.flowNode);
// //
const readonly = inject<Boolean>('readonly'); const readonly = inject<boolean>('readonly');
watch( watch(
() => props.flowNode, () => props.flowNode,

View File

@ -28,10 +28,11 @@ const emits = defineEmits<{
}>(); }>();
// //
const readonly = inject<Boolean>('readonly'); const readonly = inject<boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
// @ts-expect-error: composable typing does not preserve this node schema exactly
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.ROUTER_BRANCH_NODE, BpmNodeTypeEnum.ROUTER_BRANCH_NODE,

View File

@ -32,11 +32,12 @@ defineEmits<{
'update:modelValue': [node: SimpleFlowNode | undefined]; 'update:modelValue': [node: SimpleFlowNode | undefined];
}>(); }>();
const readonly = inject<Boolean>('readonly'); // const readonly = inject<boolean>('readonly'); //
const tasks = inject<Ref<any[]>>('tasks', ref([])); const tasks = inject<Ref<any[]>>('tasks', ref([]));
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
// @ts-expect-error: composable typing does not preserve this node schema exactly
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.START_USER_NODE, BpmNodeTypeEnum.START_USER_NODE,

View File

@ -30,7 +30,7 @@ const emits = defineEmits<{
}>(); }>();
// //
const readonly = inject<Boolean>('readonly'); const readonly = inject<boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //

View File

@ -32,11 +32,12 @@ const emits = defineEmits<{
}>(); }>();
// //
const readonly = inject<Boolean>('readonly'); const readonly = inject<boolean>('readonly');
const tasks = inject<Ref<any[]>>('tasks', ref([])); const tasks = inject<Ref<any[]>>('tasks', ref([]));
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
// @ts-expect-error: composable typing does not preserve this node schema exactly
const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.USER_TASK_NODE, BpmNodeTypeEnum.USER_TASK_NODE,

View File

@ -7,6 +7,10 @@ interface DictDataType {
// 用户任务的审批类型。 【参考飞书】 // 用户任务的审批类型。 【参考飞书】
export enum ApproveType { export enum ApproveType {
/**
*
*/
USER = 1,
/** /**
* *
*/ */
@ -15,18 +19,14 @@ export enum ApproveType {
* *
*/ */
AUTO_REJECT = 3, AUTO_REJECT = 3,
/**
*
*/
USER = 1,
} }
// 多人审批方式类型枚举 用于审批节点 // 多人审批方式类型枚举 用于审批节点
export enum ApproveMethodType { export enum ApproveMethodType {
/** /**
* () *
*/ */
ANY_APPROVE = 3, RANDOM_SELECT_ONE_APPROVE = 1,
/** /**
* () * ()
@ -34,9 +34,9 @@ export enum ApproveMethodType {
APPROVE_BY_RATIO = 2, APPROVE_BY_RATIO = 2,
/** /**
* * ()
*/ */
RANDOM_SELECT_ONE_APPROVE = 1, ANY_APPROVE = 3,
/** /**
* *
*/ */
@ -70,34 +70,34 @@ export enum ConditionType {
// 操作按钮类型枚举 (用于审批节点) // 操作按钮类型枚举 (用于审批节点)
export enum OperationButtonType { export enum OperationButtonType {
/**
*
*/
ADD_SIGN = 5,
/** /**
* *
*/ */
APPROVE = 1, APPROVE = 1,
/**
*
*/
COPY = 7,
/**
*
*/
DELEGATE = 4,
/** /**
* *
*/ */
REJECT = 2, REJECT = 2,
/**
*
*/
TRANSFER = 3,
/**
*
*/
DELEGATE = 4,
/**
*
*/
ADD_SIGN = 5,
/** /**
* 退 * 退
*/ */
RETURN = 6, RETURN = 6,
/** /**
* *
*/ */
TRANSFER = 3, COPY = 7,
} }
// 审批拒绝类型枚举 // 审批拒绝类型枚举
@ -114,6 +114,10 @@ export enum RejectHandlerType {
// 用户任务超时处理类型枚举 // 用户任务超时处理类型枚举
export enum TimeoutHandlerType { export enum TimeoutHandlerType {
/**
*
*/
REMINDER = 1,
/** /**
* *
*/ */
@ -122,10 +126,6 @@ export enum TimeoutHandlerType {
* *
*/ */
REJECT = 3, REJECT = 3,
/**
*
*/
REMINDER = 1,
} }
// 用户任务的审批人为空时,处理类型枚举 // 用户任务的审批人为空时,处理类型枚举
@ -135,49 +135,49 @@ export enum AssignEmptyHandlerType {
*/ */
APPROVE = 1, APPROVE = 1,
/** /**
* *
*/ */
ASSIGN_ADMIN = 4, REJECT = 2,
/** /**
* *
*/ */
ASSIGN_USER = 3, ASSIGN_USER = 3,
/** /**
* *
*/ */
REJECT = 2, ASSIGN_ADMIN = 4,
} }
// 用户任务的审批人与发起人相同时,处理类型枚举 // 用户任务的审批人与发起人相同时,处理类型枚举
export enum AssignStartUserHandlerType { export enum AssignStartUserHandlerType {
/** /**
* *
*/ */
ASSIGN_DEPT_LEADER = 3, START_USER_AUDIT = 1,
/** /**
* 12 * 12
*/ */
SKIP = 2, SKIP = 2,
/** /**
* *
*/ */
START_USER_AUDIT = 1, ASSIGN_DEPT_LEADER = 3,
} }
// 时间单位枚举 // 时间单位枚举
export enum TimeUnitType { export enum TimeUnitType {
/** /**
* *
*/ */
DAY = 3, MINUTE = 1,
/** /**
* *
*/ */
HOUR = 2, HOUR = 2,
/** /**
* *
*/ */
MINUTE = 1, DAY = 3,
} }
/** /**
@ -202,14 +202,14 @@ export enum FieldPermissionType {
* *
*/ */
export enum DelayTypeEnum { export enum DelayTypeEnum {
/**
*
*/
FIXED_DATE_TIME = 2,
/** /**
* *
*/ */
FIXED_TIME_DURATION = 1, FIXED_TIME_DURATION = 1,
/**
*
*/
FIXED_DATE_TIME = 2,
} }
/** /**
@ -217,35 +217,39 @@ export enum DelayTypeEnum {
*/ */
export enum TriggerTypeEnum { export enum TriggerTypeEnum {
/** /**
* * HTTP
*/ */
FORM_DELETE = 11, HTTP_REQUEST = 1,
/**
*
*/
FORM_UPDATE = 10,
/** /**
* HTTP * HTTP
*/ */
HTTP_CALLBACK = 2, HTTP_CALLBACK = 2,
/** /**
* HTTP *
*/ */
HTTP_REQUEST = 1, FORM_UPDATE = 10,
/**
*
*/
FORM_DELETE = 11,
} }
export enum ChildProcessStartUserTypeEnum { export enum ChildProcessStartUserTypeEnum {
/**
*
*/
MAIN_PROCESS_START_USER = 1,
/** /**
* *
*/ */
FROM_FORM = 2, FROM_FORM = 2,
}
export enum ChildProcessStartUserEmptyTypeEnum {
/** /**
* *
*/ */
MAIN_PROCESS_START_USER = 1, MAIN_PROCESS_START_USER = 1,
}
export enum ChildProcessStartUserEmptyTypeEnum {
/** /**
* *
*/ */
@ -254,10 +258,6 @@ export enum ChildProcessStartUserEmptyTypeEnum {
* *
*/ */
MAIN_PROCESS_ADMIN = 3, MAIN_PROCESS_ADMIN = 3,
/**
*
*/
MAIN_PROCESS_START_USER = 1,
} }
export enum ChildProcessMultiInstanceSourceTypeEnum { export enum ChildProcessMultiInstanceSourceTypeEnum {
@ -265,54 +265,50 @@ export enum ChildProcessMultiInstanceSourceTypeEnum {
* *
*/ */
FIXED_QUANTITY = 1, FIXED_QUANTITY = 1,
/**
*
*/
MULTIPLE_FORM = 3,
/** /**
* *
*/ */
NUMBER_FORM = 2, NUMBER_FORM = 2,
/**
*
*/
MULTIPLE_FORM = 3,
} }
// 候选人策略枚举 用于审批节点。抄送节点 ) // 候选人策略枚举 用于审批节点。抄送节点 )
export enum CandidateStrategy { export enum CandidateStrategy {
/** /**
* *
*/ */
APPROVE_USER_SELECT = 34, ROLE = 10,
/**
*
*/
DEPT_LEADER = 21,
/** /**
* *
*/ */
DEPT_MEMBER = 20, DEPT_MEMBER = 20,
/** /**
* *
*/ */
EXPRESSION = 60, DEPT_LEADER = 21,
/**
*
*/
FORM_DEPT_LEADER = 51,
/**
*
*/
FORM_USER = 50,
/**
*
*/
MULTI_LEVEL_DEPT_LEADER = 23,
/** /**
* *
*/ */
POST = 22, POST = 22,
/** /**
* *
*/ */
ROLE = 10, MULTI_LEVEL_DEPT_LEADER = 23,
/**
*
*/
USER = 30,
/**
*
*/
APPROVE_USER_SELECT = 34,
/**
*
*/
START_USER_SELECT = 35,
/** /**
* *
*/ */
@ -325,18 +321,22 @@ export enum CandidateStrategy {
* *
*/ */
START_USER_MULTI_LEVEL_DEPT_LEADER = 38, START_USER_MULTI_LEVEL_DEPT_LEADER = 38,
/**
*
*/
START_USER_SELECT = 35,
/**
*
*/
USER = 30,
/** /**
* *
*/ */
USER_GROUP = 40, USER_GROUP = 40,
/**
*
*/
FORM_USER = 50,
/**
*
*/
FORM_DEPT_LEADER = 51,
/**
*
*/
EXPRESSION = 60,
} }
export enum BpmHttpRequestParamTypeEnum { export enum BpmHttpRequestParamTypeEnum {

View File

@ -27,7 +27,7 @@ type EnvType = 'h5' | 'miniapp'; // 环境类型
// UniApp WebView // UniApp WebView
interface UniWebView { interface UniWebView {
postMessage: (options: { data: any }) => void; postMessage: (options: { data: any }, targetOrigin?: string) => void;
getEnv: (callback: (res: any) => void) => void; getEnv: (callback: (res: any) => void) => void;
navigateTo: (options: { navigateTo: (options: {
fail?: () => void; fail?: () => void;
@ -182,7 +182,7 @@ function postMessageToParent(message: { data: any; type: string }) {
if (envType.value === 'miniapp') { if (envType.value === 'miniapp') {
if (window.uni?.postMessage) { if (window.uni?.postMessage) {
// data // data
window.uni.postMessage({ data: message.data }); window.uni.postMessage({ data: message.data }, window.location.origin);
} else { } else {
console.error('小程序环境下 uni 对象未定义'); console.error('小程序环境下 uni 对象未定义');
} }

View File

@ -3,7 +3,7 @@
* - @ mention * - @ mention
*/ */
// @ts-ignore TinyMCE 全局或通过打包器提供 // TinyMCE 全局或通过打包器提供
import type { Editor } from 'tinymce'; import type { Editor } from 'tinymce';
export interface MentionItem { export interface MentionItem {

View File

@ -9,7 +9,7 @@ const props = withDefaults(
defineProps<{ defineProps<{
bpmnXml?: string; bpmnXml?: string;
loading?: boolean; // loading?: boolean; //
modelView?: Object; modelView?: object;
}>(), }>(),
{ {
loading: false, loading: false,
@ -29,7 +29,7 @@ watch(
async (newModelView) => { async (newModelView) => {
// //
if (newModelView) { if (newModelView) {
// @ts-ignore // @ts-expect-error: viewer instance type is broader than local ref typing
view.value = newModelView; view.value = newModelView;
} }
}, },

View File

@ -107,6 +107,7 @@ const nodeTypeName = ref('审批'); // 节点类型名称
const reasonRequire = ref(); const reasonRequire = ref();
const approveFormRef = ref<FormInstance>(); // const approveFormRef = ref<FormInstance>(); //
// @ts-expect-error: template ref is retained for future provider expansion
const approveSignFormRef = ref(); const approveSignFormRef = ref();
const nextAssigneesActivityNode = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>( const nextAssigneesActivityNode = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>(
[], [],

View File

@ -10,7 +10,7 @@ import { useUserStore } from '@vben/stores';
import { formatDate } from '@vben/utils'; import { formatDate } from '@vben/utils';
import { Button } from 'ant-design-vue'; import { Button } from 'ant-design-vue';
// @ts-ignore - vue3-print-nb v-print // @ts-expect-error - vue3-print-nb v-print
import vPrint from 'vue3-print-nb'; import vPrint from 'vue3-print-nb';
import { getProcessInstancePrintData } from '#/api/bpm/processInstance'; import { getProcessInstancePrintData } from '#/api/bpm/processInstance';

View File

@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
</AnalysisChartsTabs> </AnalysisChartsTabs>
<div class="mt-5 w-full md:flex"> <div class="mt-5 w-full md:flex">
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问数量"> <AnalysisChartCard class="mt-5 md:mt-0 md:mr-4 md:w-1/3" title="访问数量">
<AnalyticsVisitsData /> <AnalyticsVisitsData />
</AnalysisChartCard> </AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问来源"> <AnalysisChartCard class="mt-5 md:mt-0 md:mr-4 md:w-1/3" title="访问来源">
<AnalyticsVisitsSource /> <AnalyticsVisitsSource />
</AnalysisChartCard> </AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="访问来源"> <AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="访问来源">

View File

@ -1,5 +1,5 @@
<!-- eslint-disable no-useless-escape -->
<script setup lang="ts"> <script setup lang="ts">
// oxlint-disable no-useless-escape
import { onMounted, ref, unref } from 'vue'; import { onMounted, ref, unref } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui'; import { Page, useVbenModal } from '@vben/common-ui';

View File

@ -1,6 +1,7 @@
<!-- Modbus 配置 --> <!-- Modbus 配置 -->
<script lang="ts" setup> <script lang="ts" setup>
import type { VbenFormSchema, VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { IotDeviceApi } from '#/api/iot/device/device'; import type { IotDeviceApi } from '#/api/iot/device/device';
import type { IotDeviceModbusConfigApi } from '#/api/iot/device/modbus/config'; import type { IotDeviceModbusConfigApi } from '#/api/iot/device/modbus/config';
import type { IotDeviceModbusPointApi } from '#/api/iot/device/modbus/point'; import type { IotDeviceModbusPointApi } from '#/api/iot/device/modbus/point';
@ -227,7 +228,7 @@ function usePointColumns(): VxeTableGridOptions['columns'] {
]; ];
} }
const [Grid, gridApi] = useVbenVxeGrid<IotDeviceModbusPointApi.ModbusPoint>({ const [Grid, gridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: usePointFormSchema(), schema: usePointFormSchema(),
submitOnChange: true, submitOnChange: true,
@ -255,7 +256,7 @@ const [Grid, gridApi] = useVbenVxeGrid<IotDeviceModbusPointApi.ModbusPoint>({
refresh: true, refresh: true,
search: true, search: true,
}, },
}, } as VxeTableGridOptions<IotDeviceModbusPointApi.ModbusPoint>,
}); });
/** 新增点位 - 使用 useVbenModal */ /** 新增点位 - 使用 useVbenModal */

View File

@ -118,7 +118,7 @@ function useFormSchema(): VbenFormSchema[] {
rules: 'required', rules: 'required',
suffix: () => { suffix: () => {
const addr = formApi.form.values?.registerAddress; const addr = formApi.form.values?.registerAddress;
if (addr == null) { if (addr === null || addr === undefined) {
return ''; return '';
} }
return h( return h(

View File

@ -1,5 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { VbenFormSchema, VxeTableGridOptions } from '#/adapter/vxe-table'; import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { IotDeviceApi } from '#/api/iot/device/device'; import type { IotDeviceApi } from '#/api/iot/device/device';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
@ -71,7 +72,7 @@ function useGridColumns(): VxeTableGridOptions['columns'] {
]; ];
} }
const [Grid, gridApi] = useVbenVxeGrid<IotDeviceApi.Device>({ const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: { gridOptions: {
columns: useGridColumns(), columns: useGridColumns(),
height: 'auto', height: 'auto',
@ -96,7 +97,7 @@ const [Grid, gridApi] = useVbenVxeGrid<IotDeviceApi.Device>({
pagerConfig: { pagerConfig: {
enabled: false, enabled: false,
}, },
}, } as VxeTableGridOptions<IotDeviceApi.Device>,
gridEvents: { gridEvents: {
checkboxAll: handleRowCheckboxChange, checkboxAll: handleRowCheckboxChange,
checkboxChange: handleRowCheckboxChange, checkboxChange: handleRowCheckboxChange,
@ -219,7 +220,7 @@ function useAddGridColumns(): VxeTableGridOptions['columns'] {
]; ];
} }
const [AddGrid, addGridApi] = useVbenVxeGrid<IotDeviceApi.Device>({ const [AddGrid, addGridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: useAddGridFormSchema(), schema: useAddGridFormSchema(),
submitOnChange: true, submitOnChange: true,
@ -247,7 +248,7 @@ const [AddGrid, addGridApi] = useVbenVxeGrid<IotDeviceApi.Device>({
refresh: true, refresh: true,
search: true, search: true,
}, },
}, } as VxeTableGridOptions<IotDeviceApi.Device>,
gridEvents: { gridEvents: {
checkboxAll: handleAddSelectionChange, checkboxAll: handleAddSelectionChange,
checkboxChange: handleAddSelectionChange, checkboxChange: handleAddSelectionChange,

View File

@ -210,7 +210,7 @@ function handleRowCheckboxChange({
checkedIds.value = records.map((item) => item.id!); checkedIds.value = records.map((item) => item.id!);
} }
const [Grid, gridApi] = useVbenVxeGrid<IotDeviceApi.Device>({ const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: { gridOptions: {
checkboxConfig: { checkboxConfig: {
highlight: true, highlight: true,
@ -242,7 +242,7 @@ const [Grid, gridApi] = useVbenVxeGrid<IotDeviceApi.Device>({
refresh: true, refresh: true,
search: true, search: true,
}, },
}, } as VxeTableGridOptions<IotDeviceApi.Device>,
gridEvents: { gridEvents: {
checkboxAll: handleRowCheckboxChange, checkboxAll: handleRowCheckboxChange,
checkboxChange: handleRowCheckboxChange, checkboxChange: handleRowCheckboxChange,

View File

@ -13,8 +13,8 @@ export const IOT_PROVIDE_KEY = {
/** IoT 设备状态枚举 */ /** IoT 设备状态枚举 */
export enum DeviceStateEnum { export enum DeviceStateEnum {
INACTIVE = 0, // 未激活 INACTIVE = 0, // 未激活
OFFLINE = 2, // 离线
ONLINE = 1, // 在线 ONLINE = 1, // 在线
OFFLINE = 2, // 离线
} }
/** IoT 产品物模型类型枚举类 */ /** IoT 产品物模型类型枚举类 */

View File

@ -3,8 +3,8 @@ import dayjs from 'dayjs';
/** 时间范围类型枚举 */ /** 时间范围类型枚举 */
export enum TimeRangeTypeEnum { export enum TimeRangeTypeEnum {
DAY30 = 1, DAY30 = 1,
MONTH = 30,
WEEK = 7, WEEK = 7,
MONTH = 30,
YEAR = 365, YEAR = 365,
} }

View File

@ -3,8 +3,8 @@ import dayjs from 'dayjs';
/** 时间范围类型枚举 */ /** 时间范围类型枚举 */
export enum TimeRangeTypeEnum { export enum TimeRangeTypeEnum {
DAY30 = 1, DAY30 = 1,
MONTH = 30,
WEEK = 7, WEEK = 7,
MONTH = 30,
YEAR = 365, YEAR = 365,
} }

View File

@ -12,7 +12,7 @@ defineOptions({ name: 'ProductCategorySelect' });
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: [Number, Array<Number>], type: [Number, Array<number>],
default: undefined, default: undefined,
}, // ID }, // ID
multiple: { multiple: {

View File

@ -50,7 +50,7 @@ const selectedSku = ref<MallSpuApi.Sku>();
/** 处理商品的选择变化 */ /** 处理商品的选择变化 */
async function handleSpuChange(spu?: MallSpuApi.Spu | null) { async function handleSpuChange(spu?: MallSpuApi.Spu | null) {
// spu null id 0 // spu null id 0
const spuId = spu?.id && spu.id ? spu.id : undefined; const spuId = spu && spu.id ? spu.id : undefined;
formData.value.spuId = spuId; formData.value.spuId = spuId;
await formApi.setFieldValue('spuId', spuId); await formApi.setFieldValue('spuId', spuId);
// //

View File

@ -110,7 +110,7 @@ function validateSku() {
let validate = true; let validate = true;
for (const sku of formData.value!.skus!) { for (const sku of formData.value!.skus!) {
for (const rule of props?.ruleConfig as RuleConfig[]) { for (const rule of props.ruleConfig as RuleConfig[]) {
const value = getNestedValue(sku, rule.name); const value = getNestedValue(sku, rule.name);
if (!rule.rule(value)) { if (!rule.rule(value)) {
validate = false; validate = false;

View File

@ -102,6 +102,7 @@ async function showInput(index: number) {
attributeIndex.value = index; attributeIndex.value = index;
inputRef.value?.[index]?.focus(); inputRef.value?.[index]?.focus();
// //
// oxlint-disable-next-line typescript/no-non-null-asserted-optional-chain
await getAttributeOptions(attributeList.value?.[index]?.id!); await getAttributeOptions(attributeList.value?.[index]?.id!);
} }

View File

@ -64,12 +64,12 @@ const [Form, formApi] = useVbenForm({
// ================= ================= // ================= =================
/** SKU 扩展类型 */ /** SKU 扩展类型 */
interface SkuExtension extends MallSpuApi.Sku { interface SkuExtension extends MallSpuApi.Sku {
productConfig: MallDiscountActivityApi.DiscountProduct; productConfig: MallDiscountActivityApi.DiscountProduct;
} }
/** SPU 扩展类型 */ /** SPU 扩展类型 */
interface SpuExtension extends MallSpuApi.Spu { interface SpuExtension extends MallSpuApi.Spu {
skus?: SkuExtension[]; skus?: SkuExtension[];
} }

View File

@ -50,7 +50,7 @@ function openRightMessage(item: MallKefuConversationApi.Conversation) {
/** 获得消息类型 */ /** 获得消息类型 */
const getConversationDisplayText = computed( const getConversationDisplayText = computed(
() => (lastMessageContentType: number, lastMessageContent: string) => { () => (lastMessageContentType: number, lastMessageContent: string) => {
switch (lastMessageContentType) { switch (lastMessageContentType) {
case KeFuMessageContentTypeEnum.IMAGE: { case KeFuMessageContentTypeEnum.IMAGE: {
return '[图片消息]'; return '[图片消息]';

View File

@ -21,7 +21,7 @@ import ProductBrowsingHistory from './product-browsing-history.vue';
const activeTab = ref<string>('会员信息'); const activeTab = ref<string>('会员信息');
const tabActivation = computed(() => (tab: string) => activeTab.value === tab); const tabActivation = computed(() => (tab: string) => activeTab.value === tab);
/** tab 切换 */ /** tab 切换 */
const productBrowsingHistoryRef = const productBrowsingHistoryRef =

View File

@ -51,7 +51,7 @@ const loadHistory = ref(false); // 加载历史消息
/** 获悉消息内容 */ /** 获悉消息内容 */
const getMessageContent = computed( const getMessageContent = computed(
() => (item: any) => jsonParse(item.content), () => (item: any) => jsonParse(item.content),
); );
/** 获得消息列表 */ /** 获得消息列表 */

View File

@ -25,7 +25,7 @@ const emits = defineEmits<{
}>(); }>();
/** 选择赠送的优惠类型拓展 */ /** 选择赠送的优惠类型拓展 */
interface GiveCoupon extends MallCouponTemplateApi.CouponTemplate { interface GiveCoupon extends MallCouponTemplateApi.CouponTemplate {
giveCount?: number; giveCount?: number;
} }

View File

@ -42,7 +42,7 @@ function handleSend(userId: number) {
messageBoxVisible.value = true; messageBoxVisible.value = true;
} }
const [Grid, gridApi] = useVbenVxeGrid<MpMessageApi.Message>({ const [Grid, gridApi] = useVbenVxeGrid({
formOptions: { formOptions: {
schema: useGridFormSchema(), schema: useGridFormSchema(),
}, },

View File

@ -65,7 +65,7 @@ const [Modal, modalApi] = useVbenModal({
modalApi.lock(); modalApi.lock();
try { try {
formData.value = await getTenant(data.id); formData.value = await getTenant(data.id);
// @ts-ignore https://gitee.com/yudaocode/yudao-ui-admin-vben/issues/ID43CX // @ts-expect-error: special-case workaround for yudao-ui-admin-vben issue ID43CX
formData.value.expireTime = String(formData.value.expireTime); formData.value.expireTime = String(formData.value.expireTime);
// values // values
await formApi.setValues(formData.value); await formApi.setValues(formData.value);

View File

@ -1 +0,0 @@
export { default } from '@vben/tailwind-config';

View File

@ -2,7 +2,6 @@
"$schema": "https://json.schemastore.org/tsconfig", "$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web-app.json", "extends": "@vben/tsconfig/web-app.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".",
"paths": { "paths": {
"#/*": ["./src/*"] "#/*": ["./src/*"]
}, },

View File

@ -6,5 +6,5 @@
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"noEmit": false "noEmit": false
}, },
"include": ["vite.config.mts"] "include": ["vite.config.ts"]
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@vben/web-antdv-next", "name": "@vben/web-antdv-next",
"version": "5.6.0", "version": "5.7.0",
"homepage": "https://vben.pro", "homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {
@ -16,7 +16,7 @@
}, },
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "pnpm vite build --mode production", "#build": "pnpm vite build --mode production",
"build:analyze": "pnpm vite build --mode analyze", "build:analyze": "pnpm vite build --mode analyze",
"dev": "pnpm vite --mode development", "dev": "pnpm vite --mode development",
"preview": "vite preview", "preview": "vite preview",

View File

@ -1 +0,0 @@
export { default } from '@vben/tailwind-config/postcss';

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 KiB

View File

@ -98,8 +98,7 @@ export const useAuthStore = defineStore('auth', () => {
} }
async function fetchUserInfo() { async function fetchUserInfo() {
let userInfo: null | UserInfo = null; const userInfo = await getUserInfoApi();
userInfo = await getUserInfoApi();
userStore.setUserInfo(userInfo); userStore.setUserInfo(userInfo);
return userInfo; return userInfo;
} }

View File

@ -55,8 +55,7 @@ const formSchema = computed((): VbenFormSchema[] => {
* @param values 登录表单数据 * @param values 登录表单数据
*/ */
async function handleLogin(values: Recordable<any>) { async function handleLogin(values: Recordable<any>) {
// eslint-disable-next-line no-console void values;
console.log(values);
} }
</script> </script>

View File

@ -29,8 +29,7 @@ const formSchema = computed((): VbenFormSchema[] => {
}); });
function handleSubmit(value: Recordable<any>) { function handleSubmit(value: Recordable<any>) {
// eslint-disable-next-line no-console void value;
console.log('reset email:', value);
} }
</script> </script>

View File

@ -82,8 +82,7 @@ const formSchema = computed((): VbenFormSchema[] => {
}); });
function handleSubmit(value: Recordable<any>) { function handleSubmit(value: Recordable<any>) {
// eslint-disable-next-line no-console void value;
console.log('register submit:', value);
} }
</script> </script>

View File

@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
</AnalysisChartsTabs> </AnalysisChartsTabs>
<div class="mt-5 w-full md:flex"> <div class="mt-5 w-full md:flex">
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问数量"> <AnalysisChartCard class="mt-5 md:mt-0 md:mr-4 md:w-1/3" title="访问数量">
<AnalyticsVisitsData /> <AnalyticsVisitsData />
</AnalysisChartCard> </AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mr-4 md:mt-0 md:w-1/3" title="访问来源"> <AnalysisChartCard class="mt-5 md:mt-0 md:mr-4 md:w-1/3" title="访问来源">
<AnalyticsVisitsSource /> <AnalyticsVisitsSource />
</AnalysisChartCard> </AnalysisChartCard>
<AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="访问来源"> <AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="访问来源">

View File

@ -1 +0,0 @@
export { default } from '@vben/tailwind-config';

Some files were not shown because too many files have changed in this diff Show More