!884 Merge remote-tracking branch 'origin/master'

Merge pull request !884 from 芋道源码/master
im
芋道源码 2026-06-17 08:32:32 +00:00 committed by Gitee
commit 68c5f3fc4b
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
334 changed files with 23197 additions and 24986 deletions

View File

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

View File

@ -1,75 +0,0 @@
// @ts-check
const { defineConfig } = require('eslint-define-config')
module.exports = defineConfig({
root: true,
env: {
browser: true,
node: true,
es6: true
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true
}
},
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:prettier/recommended',
'@unocss'
],
rules: {
'vue/no-setup-props-destructure': 'off',
'vue/script-setup-uses-vars': 'error',
'vue/no-reserved-component-names': 'off',
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'no-unused-vars': 'off',
'space-before-function-paren': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/require-explicit-emits': 'off',
'vue/require-toggle-inside-transition': 'off',
'vue/html-self-closing': [
'error',
{
html: {
void: 'always',
normal: 'never',
component: 'always'
},
svg: 'always',
math: 'always'
}
],
'vue/multi-word-component-names': 'off',
'vue/no-v-html': 'off',
'prettier/prettier': 'off', // 芋艿:默认关闭 prettier 的 ESLint 校验,因为我们使用的是 IDE 的 Prettier 插件
'@unocss/order': 'off', // 芋艿:禁用 unocss 【css】顺序的提示因为暂时不需要这么严格警告也有点繁琐
'@unocss/order-attributify': 'off' // 芋艿:禁用 unocss 【属性】顺序的提示,因为暂时不需要这么严格,警告也有点繁琐
}
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -87,7 +87,7 @@
* 通用模块(必选):系统功能、基础设施
* 通用模块(可选):工作流程、支付系统、数据报表、会员中心
* 业务系统(按需):ERP 系统、CRM 系统、MES 系统、商城系统、微信公众号、AI 大模型、IoT 物联网
* 业务系统(按需):Mall 电子商城、OA 办公自动化、ERP 企业资源计划系统、WMS 仓库管理系统、CRM 客户关系管理、CMS 内容管理系统、MES 执行制造系统、AI 大模型平台、IoT 物联网系统、IM 即时通讯系统、Mobile 手机移动端、Report 数据大屏
### 系统功能
@ -238,6 +238,14 @@
![功能图](/.image/common/erp-feature.png)
### WMS 系统
演示地址:<https://doc.iocoder.cn/wms-preview/>
![功能图](/.image/common/wms-feature.png)
![预览图](/.image/common/wms-preview.png)
### CRM 系统
演示地址:<https://doc.iocoder.cn/crm-preview/>
@ -276,6 +284,19 @@
![预览图](/.image/common/iot-preview.png)
### IM 即时通讯
演示地址Vue3 + Element Plus<http://dashboard-vue3.yudao.iocoder.cn>
使用文档:<https://doc.iocoder.cn/im-preview/>
![功能图](/.image/common/im-feature.png)
| 聊天界面 | 聊天管理 |
| --- | --- |
| ![聊天界面](/.image/common/im-preview-home.png) | ![聊天管理](/.image/common/im-preview-manager.png) |
## 🐷 演示图
### 系统功能

View File

@ -2,8 +2,7 @@ import { resolve } from 'path'
import Vue from '@vitejs/plugin-vue'
import VueJsx from '@vitejs/plugin-vue-jsx'
import progress from 'vite-plugin-progress'
import EslintPlugin from 'vite-plugin-eslint'
import PurgeIcons from 'vite-plugin-purge-icons'
import EslintPlugin from 'vite-plugin-eslint2'
import { ViteEjsPlugin } from 'vite-plugin-ejs'
// @ts-ignore
import ElementPlus from 'unplugin-element-plus/vite'
@ -29,7 +28,6 @@ export function createVitePlugins() {
VueJsx(),
UnoCSS(),
progress(),
PurgeIcons(),
ElementPlus({}),
AutoImport({
include: [
@ -64,7 +62,7 @@ export function createVitePlugins() {
dts: 'src/types/auto-components.d.ts',
// 自定义组件的解析器
resolvers: [ElementPlusResolver()],
globs: ["src/components/**/**.{vue, md}", '!src/components/DiyEditor/components/mobile/**']
globs: ['src/components/**/**.{vue, md}', '!src/components/DiyEditor/components/mobile/**']
}),
EslintPlugin({
cache: false,
@ -77,7 +75,7 @@ export function createVitePlugins() {
}),
createSvgIconsPlugin({
iconDirs: [pathResolve('src/assets/svgs')],
symbolId: 'icon-[dir]-[name]',
symbolId: 'icon-[dir]-[name]'
}),
viteCompression({
verbose: true, // 是否在控制台输出压缩结果

View File

@ -2,22 +2,18 @@ const include = [
'qs',
'url',
'vue',
'sass',
'mitt',
'axios',
'pinia',
'dayjs',
'qrcode',
'unocss',
'vue-router',
'vue-types',
'vue-i18n',
'crypto-js',
'cropperjs',
'lodash-es',
'nprogress',
'web-storage-cache',
'@iconify/iconify',
'@vueuse/core',
'@zxcvbn-ts/core',
'echarts/core',
@ -91,18 +87,9 @@ const include = [
'element-plus/es/components/dropdown-menu/style/css',
'element-plus/es/components/dropdown-item/style/css',
'element-plus/es/components/skeleton/style/css',
'element-plus/es/components/skeleton/style/css',
'element-plus/es/components/backtop/style/css',
'element-plus/es/components/menu/style/css',
'element-plus/es/components/sub-menu/style/css',
'element-plus/es/components/menu-item/style/css',
'element-plus/es/components/dropdown/style/css',
'element-plus/es/components/skeleton-item/style/css',
'element-plus/es/components/tree/style/css',
'element-plus/es/components/dropdown-menu/style/css',
'element-plus/es/components/dropdown-item/style/css',
'element-plus/es/components/badge/style/css',
'element-plus/es/components/breadcrumb/style/css',
'element-plus/es/components/breadcrumb-item/style/css',
'element-plus/es/components/image/style/css',
'element-plus/es/components/collapse-transition/style/css',
'element-plus/es/components/timeline/style/css',
@ -119,6 +106,6 @@ const include = [
'element-plus/es/components/progress/style/css'
]
const exclude = ['@iconify/json']
const exclude: string[] = []
export { include, exclude }

115
eslint.config.mjs Normal file
View File

@ -0,0 +1,115 @@
import pluginVue from 'eslint-plugin-vue'
import tseslint from 'typescript-eslint'
import unocss from '@unocss/eslint-config/flat'
import autoImportGlobals from './.eslintrc-auto-import.json' with { type: 'json' }
export default tseslint.config(
// Global ignores (replaces .eslintignore)
{
ignores: [
'build/',
'config/',
'dist/',
'dist*/',
'*.js',
'*.mjs',
'!eslint.config.mjs',
'test/unit/coverage/',
'node_modules/',
'src/main.ts',
'src/types/auto-components.d.ts'
]
},
// Base TypeScript config
...tseslint.configs.recommended,
// Vue recommended config
...pluginVue.configs['flat/recommended'],
// UnoCSS config
unocss,
// Vue files use vue-eslint-parser with TypeScript parser
{
files: ['**/*.vue'],
languageOptions: {
parserOptions: {
parser: tseslint.parser
}
}
},
// Main rules config
{
languageOptions: {
ecmaVersion: 2020,
sourceType: 'module',
globals: {
...autoImportGlobals.globals
},
parserOptions: {
ecmaFeatures: {
jsx: true
}
}
},
rules: {
// Vue rules
'vue/no-reserved-component-names': 'off',
'vue/custom-event-name-casing': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/require-explicit-emits': 'off',
'vue/require-toggle-inside-transition': 'off',
'vue/html-self-closing': [
'error',
{
html: {
void: 'always',
normal: 'never',
component: 'always'
},
svg: 'always',
math: 'always'
}
],
'vue/multi-word-component-names': 'off',
'vue/no-v-html': 'off',
// TypeScript rules
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/no-unsafe-function-type': 'off',
'@typescript-eslint/no-wrapper-object-types': 'off',
'@typescript-eslint/no-this-alias': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'vue/no-ref-as-operand': 'off',
'vue/no-mutating-props': 'off',
'vue/no-side-effects-in-computed-properties': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'no-unused-vars': 'off',
'space-before-function-paren': 'off',
// UnoCSS rules - 芋艿:禁用 unocss 顺序提示
'@unocss/order': 'off',
'@unocss/order-attributify': 'off',
'unocss/order': 'off',
'unocss/order-attributify': 'off'
}
}
)

17542
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "yudao-ui-admin-vue3",
"version": "2026.03-snapshot",
"version": "2026.05-snapshot",
"description": "基于vue3、vite4、element-plus、typesScript",
"author": "xingyu",
"private": false,
@ -8,144 +8,137 @@
"i": "pnpm install",
"dev": "vite --mode env.local",
"dev-server": "vite --mode dev",
"ts:check": "vue-tsc --noEmit",
"build:local": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build",
"build:dev": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode dev",
"build:test": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode test",
"build:stage": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode stage",
"build:prod": "node --max_old_space_size=4096 ./node_modules/vite/bin/vite.js build --mode prod",
"ts:check": "node --max_old_space_size=8192 ./node_modules/vue-tsc/bin/vue-tsc.js --noEmit",
"build:local": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode env.local",
"build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode dev",
"build:test": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode test",
"build:stage": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode stage",
"build:prod": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode prod",
"serve:dev": "vite preview --mode dev",
"serve:prod": "vite preview --mode prod",
"preview": "pnpm build:local && vite preview",
"clean": "npx rimraf node_modules",
"clean:cache": "npx rimraf node_modules/.cache",
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
"lint:eslint": "eslint --fix ./src",
"lint:format": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"",
"lint:style": "stylelint --fix \"./src/**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged -c "
},
"dependencies": {
"@element-plus/icons-vue": "2.3.2",
"@form-create/designer": "^3.2.6",
"@form-create/element-ui": "^3.2.11",
"@iconify/iconify": "^3.1.1",
"@form-create/designer": "^3.4.0",
"@form-create/element-ui": "^3.2.38",
"@iconify/vue": "^5.0.1",
"@microsoft/fetch-event-source": "^2.0.1",
"@videojs-player/vue": "^1.0.0",
"@vueuse/core": "^10.9.0",
"@wangeditor-next/editor": "^5.6.46",
"@vueuse/core": "^14.3.0",
"@wangeditor-next/editor": "^5.7.0",
"@wangeditor-next/editor-for-vue": "^5.1.14",
"@wangeditor-next/plugin-mention": "^1.0.16",
"@wangeditor-next/plugin-mention": "^2.0.0",
"@zxcvbn-ts/core": "^3.0.4",
"animate.css": "^4.1.1",
"axios": "1.9.0",
"axios": "1.16.0",
"benz-amr-recorder": "^1.1.5",
"bpmn-js-token-simulation": "^0.36.0",
"bpmn-js-token-simulation": "^0.39.3",
"camunda-bpmn-moddle": "^7.0.1",
"cropperjs": "^1.6.1",
"cropperjs": "^2.1.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.10",
"dayjs-plugin-lunar": "^1.4.1",
"dayjs": "^1.11.20",
"dhtmlx-gantt": "^9.1.1",
"diagram-js": "^12.8.0",
"driver.js": "^1.3.1",
"echarts": "^5.5.0",
"diagram-js": "^15.14.0",
"driver.js": "^1.4.0",
"echarts": "^6.0.0",
"echarts-wordcloud": "^2.1.0",
"element-plus": "2.11.1",
"element-plus": "2.13.7",
"fast-xml-parser": "^4.3.2",
"highlight.js": "^11.9.0",
"highlight.js": "^11.11.1",
"jsbarcode": "^3.12.3",
"jsencrypt": "^3.3.2",
"jsoneditor": "^10.1.3",
"jsencrypt": "^3.5.4",
"jsoneditor": "^10.4.3",
"livekit-client": "^2.18.9",
"lodash-es": "^4.17.21",
"markdown-it": "^14.1.0",
"markmap-common": "^0.16.0",
"markmap-lib": "^0.16.1",
"markmap-toolbar": "^0.17.0",
"markmap-view": "^0.16.0",
"min-dash": "^4.1.1",
"lodash-es": "^4.18.1",
"markdown-it": "^14.1.1",
"markmap-common": "^0.18.9",
"markmap-lib": "^0.18.12",
"markmap-toolbar": "^0.18.12",
"markmap-view": "^0.18.12",
"min-dash": "^5.0.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"qrcode": "^1.5.3",
"qs": "^6.12.0",
"snabbdom": "^3.6.2",
"sortablejs": "^1.15.3",
"pinia": "^3.0.4",
"pinia-plugin-persistedstate": "^4.7.1",
"qrcode": "^1.5.4",
"qs": "^6.15.1",
"snabbdom": "^3.6.3",
"sortablejs": "^1.15.7",
"steady-xml": "^0.1.0",
"tyme4ts": "^1.4.6",
"url": "^0.11.3",
"video.js": "^7.21.5",
"vue": "3.5.12",
"vue-dompurify-html": "^4.1.4",
"vue-i18n": "9.10.2",
"vue-router": "4.4.5",
"vue-types": "^5.1.1",
"url": "^0.11.4",
"video.js": "^8.23.8",
"vue": "3.5.34",
"vue-dompurify-html": "^5.3.0",
"vue-i18n": "11.4.0",
"vue-router": "5.0.6",
"vue-types": "^6.0.0",
"vue3-print-nb": "^0.1.4",
"vue3-signature": "^0.2.4",
"vue3-signature": "^0.4.4",
"vuedraggable": "^4.1.0",
"web-storage-cache": "^1.1.1",
"xml-js": "^1.6.11"
},
"devDependencies": {
"@commitlint/cli": "^19.0.1",
"@commitlint/config-conventional": "^19.0.0",
"@iconify/json": "^2.2.187",
"@intlify/unplugin-vue-i18n": "^2.0.0",
"@purge-icons/generated": "^0.9.0",
"@types/jsoneditor": "^9.9.5",
"@commitlint/cli": "^20.5.3",
"@commitlint/config-conventional": "^20.5.3",
"@iconify/json": "^2.2.470",
"@intlify/unplugin-vue-i18n": "^11.1.2",
"@types/jsoneditor": "^9.9.6",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.11.21",
"@types/node": "^25.6.0",
"@types/nprogress": "^0.2.3",
"@types/qrcode": "^1.5.5",
"@types/qs": "^6.9.12",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"@unocss/eslint-config": "^0.57.4",
"@unocss/eslint-plugin": "66.1.0-beta.5",
"@unocss/transformer-variant-group": "^0.58.5",
"@vitejs/plugin-legacy": "^5.3.1",
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.17",
"bpmn-js": "^17.9.2",
"bpmn-js-properties-panel": "5.23.0",
"consola": "^3.2.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-define-config": "^2.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.22.0",
"lint-staged": "^15.2.2",
"postcss": "^8.4.35",
"postcss-html": "^1.6.0",
"@types/qrcode": "^1.5.6",
"@types/qs": "^6.15.0",
"@unocss/eslint-config": "^66.6.8",
"@unocss/eslint-plugin": "66.6.8",
"@unocss/transformer-variant-group": "^66.6.8",
"@vitejs/plugin-legacy": "^8.0.1",
"@vitejs/plugin-vue": "^6.0.6",
"@vitejs/plugin-vue-jsx": "^5.1.5",
"autoprefixer": "^10.5.0",
"bpmn-js": "^18.16.1",
"bpmn-js-properties-panel": "5.54.0",
"consola": "^3.4.2",
"eslint": "^10.3.0",
"eslint-plugin-vue": "^10.9.1",
"lint-staged": "^16.4.0",
"postcss": "^8.5.14",
"postcss-html": "^1.8.1",
"postcss-scss": "^4.0.9",
"prettier": "^3.2.5",
"prettier-eslint": "^16.3.0",
"rimraf": "^5.0.5",
"rollup": "^4.12.0",
"sass": "^1.69.5",
"stylelint": "^16.2.1",
"prettier": "^3.8.3",
"prettier-eslint": "^16.4.2",
"rimraf": "^6.1.3",
"rollup": "^4.60.3",
"sass": "^1.99.0",
"stylelint": "^17.11.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-recommended": "^14.0.0",
"stylelint-config-standard": "^36.0.0",
"stylelint-order": "^6.0.4",
"terser": "^5.28.1",
"typescript": "5.3.3",
"unocss": "^0.58.5",
"unplugin-auto-import": "^0.16.7",
"unplugin-element-plus": "^0.8.0",
"unplugin-vue-components": "^0.25.2",
"vite": "5.1.4",
"stylelint-config-recommended": "^18.0.0",
"stylelint-config-standard": "^40.0.0",
"stylelint-order": "^8.1.1",
"terser": "^5.46.2",
"typescript": "6.0.3",
"typescript-eslint": "^8.59.2",
"unocss": "^66.6.8",
"unplugin-auto-import": "^21.0.0",
"unplugin-element-plus": "^0.11.2",
"unplugin-vue-components": "^32.0.0",
"vite": "8.0.10",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-ejs": "^1.7.0",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-eslint2": "^5.1.0",
"vite-plugin-progress": "^0.0.7",
"vite-plugin-purge-icons": "^0.10.0",
"vite-plugin-svg-icons-ng": "^1.3.1",
"vite-plugin-top-level-await": "^1.4.4",
"vue-eslint-parser": "^9.3.2",
"vue-tsc": "^1.8.27"
"vite-plugin-svg-icons-ng": "^1.9.0",
"vite-plugin-top-level-await": "^1.6.0",
"vue-eslint-parser": "^10.4.0",
"vue-tsc": "^3.2.8"
},
"license": "MIT",
"repository": {
@ -158,7 +151,7 @@
"homepage": "https://gitee.com/yudaocode/yudao-ui-admin-vue3",
"web-types": "./web-types.json",
"engines": {
"node": ">= 16.0.0",
"node": ">= 20.19.0",
"pnpm": ">=8.6.0"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,7 @@ export type ApprovalTaskInfo = {
assigneeUser: User
status: number
reason: string
attachments?: string[]
signPicUrl: string
}

View File

@ -66,3 +66,8 @@ export const runJob = (id: number) => {
export const getJobNextTimes = (id: number) => {
return request.get({ url: '/infra/job/get_next_times?id=' + id })
}
// 同步定时任务到 Quartz
export const syncJob = () => {
return request.post({ url: '/infra/job/sync' })
}

View File

@ -10,6 +10,9 @@ export interface AlertConfig {
sceneRuleIds: string // 关联的场景联动规则编号数组
receiveUserIds: string // 接收的用户编号数组
receiveTypes: string // 接收的类型数组
smsTemplateCode?: string // 短信模板编号
mailTemplateCode?: string // 邮件模板编号
notifyTemplateCode?: string // 站内信模板编号
}
// IoT 告警配置 API

View File

@ -13,6 +13,7 @@ export interface DataSinkVO {
| TcpConfig
| WebSocketConfig
| MqttConfig
| DatabaseConfig
| RocketMQConfig
| KafkaMQConfig
| RabbitMQConfig
@ -73,6 +74,14 @@ export interface MqttConfig extends Config {
topic: string
}
/** Database 配置 */
export interface DatabaseConfig extends Config {
jdbcUrl: string
username: string
password: string
tableName: string
}
/** RocketMQ 配置 */
export interface RocketMQConfig extends Config {
nameServer: string

View File

@ -5,6 +5,7 @@ export interface IotSceneRule {
id?: number // 场景编号
name: string // 场景名称
description?: string // 场景描述
lastTriggerTime?: string // 最近触发时间
status: number // 场景状态0-开启1-关闭
triggers: Trigger[] // 触发器数组
actions: Action[] // 执行器数组

View File

@ -215,8 +215,8 @@ export const ThingModelFormRules = {
identifier: [
{ required: true, message: '标识符不能为空', trigger: 'blur' },
{
pattern: /^[a-zA-Z0-9_]{1,50}$/,
message: '支持大小写字母、数字和下划线,不超过 50 个字符',
pattern: /^[a-zA-Z][a-zA-Z0-9_]{0,31}$/,
message: '支持大小写字母、数字和下划线,必须以字母开头,不超过 32 个字符',
trigger: 'blur'
},
{

View File

@ -9,6 +9,7 @@ export interface UserVO {
loginIp: string
mark: string
mobile: string
email: string | undefined
name: string | undefined
nickname: string | undefined
registerIp: string

View File

@ -6,6 +6,7 @@ export interface ProAndonConfigVO {
reason: string // 呼叫原因
level: number // 级别
handlerRoleId: number // 处置人角色编号
handlerRoleName: string // 处置人角色名称
handlerUserId: number // 处置人编号
handlerUserNickname: string // 处置人昵称
remark: string // 备注

View File

@ -5,7 +5,7 @@ export interface WmMiscIssueVO {
id: number
code: string
name: string
type: string
type: number
sourceDocType: string
sourceDocId: number
sourceDocCode: string

View File

@ -13,6 +13,20 @@ export interface WmSnGroupVO {
createTime?: Date
}
export interface WmSnVO {
id?: number
uuid?: string
code?: string
itemId?: number
itemCode?: string
itemName?: string
specification?: string
unitName?: string
batchCode?: string
workOrderId?: number
createTime?: Date
}
export interface WmSnGenerateVO {
itemId?: number
batchCode?: string
@ -30,6 +44,11 @@ export const getSnGroupPage = async (params: any) => {
return await request.get({ url: `/mes/wm/sn/group-page`, params })
}
// 获得批次 SN 码明细列表
export const getSnListByUuid = async (uuid: string) => {
return await request.get({ url: `/mes/wm/sn/list-by-uuid`, params: { uuid } })
}
// 批量删除 SN 码(按批次 UUID
export const deleteSnBatch = async (uuid: string) => {
return await request.delete({ url: `/mes/wm/sn/delete-batch`, params: { uuid } })

View File

@ -19,6 +19,16 @@ export interface MailSendReqVO {
templateParams: Map<String, Object>
}
export interface MailTemplateSimpleVO {
id: number
name: string
code: string
}
// 查询邮件模版精简列表
export const getSimpleMailTemplateList = async () => {
return await request.get({ url: '/system/mail-template/simple-list' })
}
// 查询邮件模版列表
export const getMailTemplatePage = async (params: PageParam) => {
return await request.get({ url: '/system/mail-template/page', params })

View File

@ -18,6 +18,17 @@ export interface NotifySendReqVO {
templateParams: Map<String, Object>
}
export interface NotifyTemplateSimpleVO {
id: number
name: string
code: string
}
// 查询站内信模板精简列表
export const getSimpleNotifyTemplateList = async () => {
return await request.get({ url: '/system/notify-template/simple-list' })
}
// 查询站内信模板列表
export const getNotifyTemplatePage = async (params: PageParam) => {
return await request.get({ url: '/system/notify-template/page', params })
@ -45,7 +56,10 @@ export const deleteNotifyTemplate = async (id: number) => {
// 批量删除站内信模板
export const deleteNotifyTemplateList = async (ids: number[]) => {
return await request.delete({ url: '/system/notify-template/delete-list', params: { ids: ids.join(',') } })
return await request.delete({
url: '/system/notify-template/delete-list',
params: { ids: ids.join(',') }
})
}
// 发送站内信

View File

@ -21,6 +21,16 @@ export interface SendSmsReqVO {
templateParams: Map<String, Object>
}
export interface SmsTemplateSimpleVO {
id: number
name: string
code: string
}
// 查询短信模板精简列表
export const getSimpleSmsTemplateList = () => {
return request.get({ url: '/system/sms-template/simple-list' })
}
// 查询短信模板列表
export const getSmsTemplatePage = (params: PageParam) => {
return request.get({ url: '/system/sms-template/page', params })

View File

@ -41,18 +41,9 @@ export const getUserPage = (params: PageParam) => {
return request.get({ url: '/system/user/page', params })
}
// 查询所有用户列表
export const getAllUser = () => {
return request.get({ url: '/system/user/simple-list' })
}
/**
*
* @param id
* @returns
*/
export const getDeptUser = (deptId: number): Promise<UserVO[]> => {
return request.get({ url: '/system/user/simple-list?deptId=' + deptId })
// 查询用户管理列表
export const getUserList = (ids: number[]) => {
return request.get({ url: '/system/user/list', params: { ids: ids.join(',') } })
}
// 查询用户详情

77
src/api/wms/home/index.ts Normal file
View File

@ -0,0 +1,77 @@
import request from '@/config/axios'
// WMS 首页统计查询参数
export interface WmsHomeStatisticsReqVO {
warehouseId?: number
goodsLimit?: number
warehouseLimit?: number
}
// WMS 首页单据状态统计 VO
export interface WmsHomeOrderStatusVO {
status: number
count: number
}
// WMS 首页单据汇总统计 VO
export interface WmsHomeOrderSummaryVO {
type: number
total: number
statuses: WmsHomeOrderStatusVO[]
}
// WMS 首页单据趋势 VO
export interface WmsHomeOrderTrendVO {
time: string | number
receiptCount: number
shipmentCount: number
movementCount: number
checkCount: number
}
// WMS 首页商品库存排行 VO
export interface WmsHomeInventoryItemRankVO {
id: number
name: string
quantity: number
}
// WMS 首页仓库库存排行 VO
export interface WmsHomeInventoryWarehouseRankVO {
id: number
name: string
quantity: number
}
// WMS 首页库存汇总统计 VO
export interface WmsHomeInventorySummaryVO {
totalQuantity: number
goodsShareList: WmsHomeInventoryItemRankVO[]
warehouseDistributionList: WmsHomeInventoryWarehouseRankVO[]
}
// WMS 首页统计 API
export const WmsHomeStatisticsApi = {
// 获得首页单据汇总统计
getOrderSummary: async (params?: WmsHomeStatisticsReqVO): Promise<WmsHomeOrderSummaryVO[]> => {
return await request.get({ url: `/wms/home-statistics/order-summary`, params })
},
// 获得首页单据趋势
getOrderTrend: async (
days?: number,
params?: WmsHomeStatisticsReqVO
): Promise<WmsHomeOrderTrendVO[]> => {
return await request.get({
url: `/wms/home-statistics/order-trend`,
params: { ...params, days }
})
},
// 获得首页库存汇总统计
getInventorySummary: async (
params?: WmsHomeStatisticsReqVO
): Promise<WmsHomeInventorySummaryVO> => {
return await request.get({ url: `/wms/home-statistics/inventory-summary`, params })
}
}

View File

@ -0,0 +1,33 @@
import request from '@/config/axios'
// WMS 库存记录 VO
export interface InventoryHistoryVO {
id?: number
itemId?: number
itemCode?: string
itemName?: string
unit?: string
skuId?: number
skuCode?: string
skuName?: string
warehouseId?: number
warehouseName?: string
quantity?: number
beforeQuantity?: number
afterQuantity?: number
price?: number
totalPrice?: number
remark?: string
orderId?: number
orderNo?: string
orderType?: number
createTime?: Date
}
// WMS 库存记录 API
export const InventoryHistoryApi = {
// 查询库存记录分页
getInventoryHistoryPage: async (params: any) => {
return await request.get({ url: '/wms/inventory-history/page', params })
}
}

View File

@ -0,0 +1,36 @@
import request from '@/config/axios'
// WMS 库存统计 VO
export interface InventoryVO {
id?: number
itemId?: number
itemCode?: string
itemName?: string
unit?: string
skuId?: number
skuCode?: string
skuName?: string
warehouseId?: number
warehouseName?: string
quantity?: number
remark?: string
createTime?: Date
}
// WMS 库存统计列表 Request VO
export interface InventoryListReqVO {
warehouseId: number
}
// WMS 库存统计 API
export const InventoryApi = {
// 查询库存统计分页
getInventoryPage: async (params: any) => {
return await request.get({ url: '/wms/inventory/page', params })
},
// 查询库存统计列表
getInventoryList: async (params: InventoryListReqVO) => {
return await request.get({ url: '/wms/inventory/list', params })
}
}

View File

@ -0,0 +1,47 @@
import request from '@/config/axios'
// WMS 商品品牌 VO
export interface ItemBrandVO {
id?: number
code?: string
name?: string
createTime?: Date
}
// WMS 商品品牌 API
export const ItemBrandApi = {
// 查询商品品牌分页
getItemBrandPage: async (params: any) => {
return await request.get({ url: '/wms/item-brand/page', params })
},
// 查询商品品牌精简列表
getItemBrandSimpleList: async () => {
return await request.get({ url: '/wms/item-brand/simple-list' })
},
// 查询商品品牌详情
getItemBrand: async (id: number) => {
return await request.get({ url: '/wms/item-brand/get?id=' + id })
},
// 新增商品品牌
createItemBrand: async (data: ItemBrandVO) => {
return await request.post({ url: '/wms/item-brand/create', data })
},
// 修改商品品牌
updateItemBrand: async (data: ItemBrandVO) => {
return await request.put({ url: '/wms/item-brand/update', data })
},
// 删除商品品牌
deleteItemBrand: async (id: number) => {
return await request.delete({ url: '/wms/item-brand/delete?id=' + id })
},
// 导出商品品牌
exportItemBrand: async (params) => {
return await request.download({ url: '/wms/item-brand/export-excel', params })
}
}

View File

@ -0,0 +1,46 @@
import request from '@/config/axios'
// WMS 商品分类 VO
export interface ItemCategoryVO {
id?: number
parentId?: number
code?: string
name?: string
sort?: number
status?: number
createTime?: Date
children?: ItemCategoryVO[]
}
// WMS 商品分类 API
export const ItemCategoryApi = {
// 查询商品分类列表
getItemCategoryList: async (params?: any) => {
return await request.get({ url: '/wms/item-category/list', params })
},
// 查询商品分类精简列表
getItemCategorySimpleList: async () => {
return await request.get({ url: '/wms/item-category/simple-list' })
},
// 查询商品分类详情
getItemCategory: async (id: number) => {
return await request.get({ url: '/wms/item-category/get?id=' + id })
},
// 新增商品分类
createItemCategory: async (data: ItemCategoryVO) => {
return await request.post({ url: '/wms/item-category/create', data })
},
// 修改商品分类
updateItemCategory: async (data: ItemCategoryVO) => {
return await request.put({ url: '/wms/item-category/update', data })
},
// 删除商品分类
deleteItemCategory: async (id: number) => {
return await request.delete({ url: '/wms/item-category/delete?id=' + id })
}
}

View File

@ -0,0 +1,55 @@
import request from '@/config/axios'
import { ItemSkuVO } from './sku'
// WMS 商品 VO
export interface ItemVO {
id?: number
code?: string
name?: string
categoryId?: number
categoryName?: string
unit?: string
brandId?: number
brandName?: string
remark?: string
skus?: ItemSkuVO[]
createTime?: Date
}
// WMS 商品 API
export const ItemApi = {
// 查询商品分页
getItemPage: async (params: any) => {
return await request.get({ url: '/wms/item/page', params })
},
// 查询商品精简列表
getItemSimpleList: async (params?: any) => {
return await request.get({ url: '/wms/item/simple-list', params })
},
// 查询商品详情
getItem: async (id: number) => {
return await request.get({ url: '/wms/item/get?id=' + id })
},
// 新增商品
createItem: async (data: ItemVO) => {
return await request.post({ url: '/wms/item/create', data })
},
// 修改商品
updateItem: async (data: ItemVO) => {
return await request.put({ url: '/wms/item/update', data })
},
// 删除商品
deleteItem: async (id: number) => {
return await request.delete({ url: '/wms/item/delete?id=' + id })
},
// 导出商品
exportItem: async (params: any) => {
return await request.download({ url: '/wms/item/export-excel', params })
}
}

View File

@ -0,0 +1,33 @@
import request from '@/config/axios'
// WMS 商品 SKU VO
export interface ItemSkuVO {
id?: number
name?: string
itemId?: number
itemCode?: string
itemName?: string
categoryId?: number
categoryName?: string
unit?: string
brandId?: number
brandName?: string
barCode?: string
code?: string
length?: number
width?: number
height?: number
grossWeight?: number
netWeight?: number
costPrice?: number
sellingPrice?: number
createTime?: Date
}
// WMS 商品 SKU API
export const ItemSkuApi = {
// 按 SKU 维度分页(支持商品 / 品牌 / 分类多表联查筛选)
getItemSkuPage: async (params: any) => {
return await request.get({ url: '/wms/item-sku/page', params })
}
}

View File

@ -0,0 +1,61 @@
import request from '@/config/axios'
// WMS 往来企业 VO
export interface MerchantVO {
id?: number
code?: string
name?: string
type?: number
level?: string
bankName?: string
bankAccount?: string
address?: string
mobile?: string
telephone?: string
contact?: string
email?: string
remark?: string
createTime?: Date
}
export interface MerchantSimpleListReqVO {
types?: number[]
}
// WMS 往来企业 API
export const MerchantApi = {
// 查询往来企业分页
getMerchantPage: async (params: any) => {
return await request.get({ url: '/wms/merchant/page', params })
},
// 查询往来企业精简列表
getMerchantSimpleList: async (params?: MerchantSimpleListReqVO) => {
return await request.get({ url: '/wms/merchant/simple-list', params })
},
// 查询往来企业详情
getMerchant: async (id: number) => {
return await request.get({ url: '/wms/merchant/get?id=' + id })
},
// 新增往来企业
createMerchant: async (data: MerchantVO) => {
return await request.post({ url: '/wms/merchant/create', data })
},
// 修改往来企业
updateMerchant: async (data: MerchantVO) => {
return await request.put({ url: '/wms/merchant/update', data })
},
// 删除往来企业
deleteMerchant: async (id: number) => {
return await request.delete({ url: '/wms/merchant/delete?id=' + id })
},
// 导出往来企业
exportMerchant: async (params: any) => {
return await request.download({ url: '/wms/merchant/export-excel', params })
}
}

View File

@ -0,0 +1,49 @@
import request from '@/config/axios'
// WMS 仓库 VO
export interface WarehouseVO {
id?: number
code?: string
name?: string
remark?: string
sort?: number
createTime?: Date
}
// WMS 仓库 API
export const WarehouseApi = {
// 查询仓库分页
getWarehousePage: async (params: any) => {
return await request.get({ url: '/wms/warehouse/page', params })
},
// 查询仓库精简列表
getWarehouseSimpleList: async () => {
return await request.get({ url: '/wms/warehouse/simple-list' })
},
// 查询仓库详情
getWarehouse: async (id: number) => {
return await request.get({ url: '/wms/warehouse/get?id=' + id })
},
// 新增仓库
createWarehouse: async (data: WarehouseVO) => {
return await request.post({ url: '/wms/warehouse/create', data })
},
// 修改仓库
updateWarehouse: async (data: WarehouseVO) => {
return await request.put({ url: '/wms/warehouse/update', data })
},
// 删除仓库
deleteWarehouse: async (id: number) => {
return await request.delete({ url: '/wms/warehouse/delete?id=' + id })
},
// 导出仓库
exportWarehouse: async (params) => {
return await request.download({ url: '/wms/warehouse/export-excel', params })
}
}

View File

@ -0,0 +1,21 @@
// WMS 盘库单明细 VO
export interface CheckOrderDetailVO {
id?: number
orderId?: number
itemId?: number
itemCode?: string
itemName?: string
unit?: string
skuId?: number
skuCode?: string
skuName?: string
inventoryId?: number
warehouseId?: number
warehouseName?: string
receiptTime?: Date
quantity?: number
checkQuantity?: number
availableQuantity?: number
price?: number
createTime?: Date
}

View File

@ -0,0 +1,73 @@
import request from '@/config/axios'
import { CheckOrderDetailVO } from './detail'
// WMS 盘库单 VO
export interface CheckOrderVO {
id?: number
no?: string
orderTime?: string
status?: number
remark?: string
warehouseId?: number
warehouseName?: string
totalQuantity?: number
totalPrice?: number
actualPrice?: number
details?: CheckOrderDetailVO[]
createTime?: Date
creator?: string
creatorName?: string
updateTime?: Date
updater?: string
updaterName?: string
}
// WMS 盘库单 API
export const CheckOrderApi = {
// 查询盘库单分页
getCheckOrderPage: async (params: any) => {
return await request.get({ url: '/wms/check-order/page', params })
},
// 查询盘库单详情
getCheckOrder: async (id: number) => {
return await request.get({ url: '/wms/check-order/get?id=' + id })
},
// 查询盘库单明细
getCheckOrderDetailListByOrderId: async (orderId: number) => {
return await request.get({
url: '/wms/check-order-detail/list-by-order-id?orderId=' + orderId
})
},
// 新增盘库单
createCheckOrder: async (data: CheckOrderVO) => {
return await request.post({ url: '/wms/check-order/create', data })
},
// 修改盘库单
updateCheckOrder: async (data: CheckOrderVO) => {
return await request.put({ url: '/wms/check-order/update', data })
},
// 完成盘库
completeCheckOrder: async (id: number) => {
return await request.put({ url: '/wms/check-order/complete?id=' + id })
},
// 作废盘库单
cancelCheckOrder: async (id: number) => {
return await request.put({ url: '/wms/check-order/cancel?id=' + id })
},
// 删除盘库单
deleteCheckOrder: async (id: number) => {
return await request.delete({ url: '/wms/check-order/delete?id=' + id })
},
// 导出盘库单
exportCheckOrder: async (params: any) => {
return await request.download({ url: '/wms/check-order/export-excel', params })
}
}

View File

@ -0,0 +1,21 @@
// WMS 移库单明细 VO
export interface MovementOrderDetailVO {
id?: number
orderId?: number
itemId?: number
itemCode?: string
itemName?: string
unit?: string
skuId?: number
skuCode?: string
skuName?: string
sourceWarehouseId?: number
sourceWarehouseName?: string
targetWarehouseId?: number
targetWarehouseName?: string
quantity?: number
availableQuantity?: number
price?: number
totalPrice?: number
createTime?: Date
}

View File

@ -0,0 +1,74 @@
import request from '@/config/axios'
import { MovementOrderDetailVO } from './detail'
// WMS 移库单 VO
export interface MovementOrderVO {
id?: number
no?: string
orderTime?: string
status?: number
remark?: string
sourceWarehouseId?: number
sourceWarehouseName?: string
targetWarehouseId?: number
targetWarehouseName?: string
totalQuantity?: number
totalPrice?: number
details?: MovementOrderDetailVO[]
createTime?: Date
creator?: string
creatorName?: string
updateTime?: Date
updater?: string
updaterName?: string
}
// WMS 移库单 API
export const MovementOrderApi = {
// 查询移库单分页
getMovementOrderPage: async (params: any) => {
return await request.get({ url: '/wms/movement-order/page', params })
},
// 查询移库单详情
getMovementOrder: async (id: number) => {
return await request.get({ url: '/wms/movement-order/get?id=' + id })
},
// 查询移库单明细
getMovementOrderDetailListByOrderId: async (orderId: number) => {
return await request.get({
url: '/wms/movement-order-detail/list-by-order-id?orderId=' + orderId
})
},
// 新增移库单
createMovementOrder: async (data: MovementOrderVO) => {
return await request.post({ url: '/wms/movement-order/create', data })
},
// 修改移库单
updateMovementOrder: async (data: MovementOrderVO) => {
return await request.put({ url: '/wms/movement-order/update', data })
},
// 完成移库
completeMovementOrder: async (id: number) => {
return await request.put({ url: '/wms/movement-order/complete?id=' + id })
},
// 作废移库单
cancelMovementOrder: async (id: number) => {
return await request.put({ url: '/wms/movement-order/cancel?id=' + id })
},
// 删除移库单
deleteMovementOrder: async (id: number) => {
return await request.delete({ url: '/wms/movement-order/delete?id=' + id })
},
// 导出移库单
exportMovementOrder: async (params: any) => {
return await request.download({ url: '/wms/movement-order/export-excel', params })
}
}

View File

@ -0,0 +1,18 @@
// WMS 入库单明细 VO
export interface ReceiptOrderDetailVO {
id?: number
orderId?: number
itemId?: number
itemCode?: string
itemName?: string
unit?: string
skuId?: number
skuCode?: string
skuName?: string
warehouseId?: number
warehouseName?: string
quantity?: number
price?: number
totalPrice?: number
createTime?: Date
}

View File

@ -0,0 +1,76 @@
import request from '@/config/axios'
import { ReceiptOrderDetailVO } from './detail'
// WMS 入库单 VO
export interface ReceiptOrderVO {
id?: number
no?: string
type?: number
orderTime?: string
status?: number
bizOrderNo?: string
merchantId?: number
merchantName?: string
remark?: string
warehouseId?: number
warehouseName?: string
totalQuantity?: number
totalPrice?: number
details?: ReceiptOrderDetailVO[]
createTime?: Date
creator?: string
creatorName?: string
updateTime?: Date
updater?: string
updaterName?: string
}
// WMS 入库单 API
export const ReceiptOrderApi = {
// 查询入库单分页
getReceiptOrderPage: async (params: any) => {
return await request.get({ url: '/wms/receipt-order/page', params })
},
// 查询入库单详情
getReceiptOrder: async (id: number) => {
return await request.get({ url: '/wms/receipt-order/get?id=' + id })
},
// 查询入库单明细
getReceiptOrderDetailListByOrderId: async (orderId: number) => {
return await request.get({
url: '/wms/receipt-order-detail/list-by-order-id?orderId=' + orderId
})
},
// 新增入库单
createReceiptOrder: async (data: ReceiptOrderVO) => {
return await request.post({ url: '/wms/receipt-order/create', data })
},
// 修改入库单
updateReceiptOrder: async (data: ReceiptOrderVO) => {
return await request.put({ url: '/wms/receipt-order/update', data })
},
// 完成入库
completeReceiptOrder: async (id: number) => {
return await request.put({ url: '/wms/receipt-order/complete?id=' + id })
},
// 作废入库单
cancelReceiptOrder: async (id: number) => {
return await request.put({ url: '/wms/receipt-order/cancel?id=' + id })
},
// 删除入库单
deleteReceiptOrder: async (id: number) => {
return await request.delete({ url: '/wms/receipt-order/delete?id=' + id })
},
// 导出入库单
exportReceiptOrder: async (params: any) => {
return await request.download({ url: '/wms/receipt-order/export-excel', params })
}
}

View File

@ -0,0 +1,19 @@
// WMS 出库单明细 VO
export interface ShipmentOrderDetailVO {
id?: number
orderId?: number
itemId?: number
itemCode?: string
itemName?: string
unit?: string
skuId?: number
skuCode?: string
skuName?: string
warehouseId?: number
warehouseName?: string
quantity?: number
availableQuantity?: number
price?: number
totalPrice?: number
createTime?: Date
}

View File

@ -0,0 +1,76 @@
import request from '@/config/axios'
import { ShipmentOrderDetailVO } from './detail'
// WMS 出库单 VO
export interface ShipmentOrderVO {
id?: number
no?: string
type?: number
orderTime?: string
status?: number
bizOrderNo?: string
merchantId?: number
merchantName?: string
remark?: string
warehouseId?: number
warehouseName?: string
totalQuantity?: number
totalPrice?: number
details?: ShipmentOrderDetailVO[]
createTime?: Date
creator?: string
creatorName?: string
updateTime?: Date
updater?: string
updaterName?: string
}
// WMS 出库单 API
export const ShipmentOrderApi = {
// 查询出库单分页
getShipmentOrderPage: async (params: any) => {
return await request.get({ url: '/wms/shipment-order/page', params })
},
// 查询出库单详情
getShipmentOrder: async (id: number) => {
return await request.get({ url: '/wms/shipment-order/get?id=' + id })
},
// 查询出库单明细
getShipmentOrderDetailListByOrderId: async (orderId: number) => {
return await request.get({
url: '/wms/shipment-order-detail/list-by-order-id?orderId=' + orderId
})
},
// 新增出库单
createShipmentOrder: async (data: ShipmentOrderVO) => {
return await request.post({ url: '/wms/shipment-order/create', data })
},
// 修改出库单
updateShipmentOrder: async (data: ShipmentOrderVO) => {
return await request.put({ url: '/wms/shipment-order/update', data })
},
// 完成出库
completeShipmentOrder: async (id: number) => {
return await request.put({ url: '/wms/shipment-order/complete?id=' + id })
},
// 作废出库单
cancelShipmentOrder: async (id: number) => {
return await request.put({ url: '/wms/shipment-order/cancel?id=' + id })
},
// 删除出库单
deleteShipmentOrder: async (id: number) => {
return await request.delete({ url: '/wms/shipment-order/delete?id=' + id })
},
// 导出出库单
exportShipmentOrder: async (params: any) => {
return await request.download({ url: '/wms/shipment-order/export-excel', params })
}
}

View File

@ -7,6 +7,7 @@ import { useWindowSize } from '@vueuse/core'
import { useAppStore } from '@/store/modules/app'
import { setCssVar } from '@/utils'
import { useDesign } from '@/hooks/web/useDesign'
import { normalizeLayout } from '@/utils/layout'
const { variables } = useDesign()
@ -33,7 +34,9 @@ watch(
!appStore.getMobile ? appStore.setMobile(true) : undefined
setCssVar('--left-menu-min-width', '0')
appStore.setCollapse(true)
appStore.getLayout !== 'classic' ? appStore.setLayout('classic') : undefined
normalizeLayout(appStore.getLayout) !== 'sidebar-nav'
? appStore.setLayout('sidebar-nav')
: undefined
} else {
appStore.getMobile ? appStore.setMobile(false) : undefined
setCssVar('--left-menu-min-width', '64px')

View File

@ -11,7 +11,7 @@ const prefixCls = getPrefixCls('content-wrap')
defineProps({
title: propTypes.string.def(''),
message: propTypes.string.def(''),
bodyStyle: propTypes.object.def({ padding: '10px' })
bodyStyle: propTypes.object.def({ padding: '10px', overflow: 'hidden' })
})
</script>

View File

@ -162,13 +162,24 @@ function handleReady(cropperInstance: Cropper) {
}
function handlerToolbar(event: string, arg?: number) {
if (event === 'scaleX') {
scaleX = arg = scaleX === -1 ? 1 : -1
if (!cropper.value) return
const cropperImage = cropper.value.getCropperImage()
const cropperSelection = cropper.value.getCropperSelection()
if (event === 'reset') {
cropperImage?.$resetTransform()
cropperSelection?.$reset()
} else if (event === 'rotate') {
cropperImage?.$rotate(`${arg}deg`)
} else if (event === 'scaleX') {
scaleX = scaleX === -1 ? 1 : -1
cropperImage?.$scale(scaleX, 1)
} else if (event === 'scaleY') {
scaleY = scaleY === -1 ? 1 : -1
cropperImage?.$scale(1, scaleY)
} else if (event === 'zoom') {
cropperImage?.$zoom(arg!)
}
if (event === 'scaleY') {
scaleY = arg = scaleY === -1 ? 1 : -1
}
cropper?.value?.[event]?.(arg)
}
async function handleOk() {
@ -208,7 +219,8 @@ $prefix-cls: #{$namespace}-cropper-am;
&-cropper {
height: 300px;
background: #eee;
background-image: linear-gradient(
background-image:
linear-gradient(
45deg,
rgb(0 0 0 / 25%) 25%,
transparent 0,

View File

@ -1,50 +1,17 @@
<template>
<div :class="getClass" :style="getWrapperStyle">
<img
v-show="isReady"
ref="imgElRef"
:alt="alt"
:crossorigin="crossorigin"
:src="src"
:style="getImageStyle"
/>
<div ref="containerRef" :class="getClass" :style="getWrapperStyle">
<img v-show="false" ref="imgElRef" :alt="alt" :crossorigin="crossorigin" :src="src" />
</div>
</template>
<script lang="ts" setup>
import { CSSProperties, PropType } from 'vue'
import Cropper from 'cropperjs'
import 'cropperjs/dist/cropper.css'
import { useDesign } from '@/hooks/web/useDesign'
import { propTypes } from '@/utils/propTypes'
import { useDebounceFn } from '@vueuse/core'
defineOptions({ name: 'Cropper' })
type Options = Cropper.Options
const defaultOptions: Options = {
aspectRatio: 1,
zoomable: true,
zoomOnTouch: true,
zoomOnWheel: true,
cropBoxMovable: true,
cropBoxResizable: true,
toggleDragModeOnDblclick: true,
autoCrop: true,
background: true,
highlight: true,
center: true,
responsive: true,
restore: true,
checkCrossOrigin: true,
checkOrientation: true,
scalable: true,
modal: true,
guides: true,
movable: true,
rotatable: true
}
const props = defineProps({
src: propTypes.string.def(''),
alt: propTypes.string.def(''),
@ -56,35 +23,21 @@ const props = defineProps({
default: undefined
},
imageStyle: { type: Object as PropType<CSSProperties>, default: () => ({}) },
options: { type: Object as PropType<Options>, default: () => ({}) }
options: { type: Object as PropType<Record<string, any>>, default: () => ({}) }
})
const emit = defineEmits(['cropend', 'ready', 'cropendError'])
const attrs = useAttrs()
const imgElRef = ref<ElRef<HTMLImageElement>>()
const cropper = ref<Nullable<Cropper>>()
const isReady = ref(false)
const imgElRef = ref<HTMLImageElement>()
const containerRef = ref<HTMLElement>()
const cropper = ref<Cropper>()
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('cropper-image')
const debounceRealTimeCroppered = useDebounceFn(realTimeCroppered, 80)
const getImageStyle = computed((): CSSProperties => {
return {
height: props.height,
maxWidth: '100%',
...props.imageStyle
}
})
const getClass = computed(() => {
return [
prefixCls,
attrs.class,
{
[`${prefixCls}--circled`]: props.circled
}
]
return [prefixCls, attrs.class]
})
const getWrapperStyle = computed((): CSSProperties => {
return { height: `${props.height}`.replace(/px/, '') + 'px' }
@ -98,27 +51,39 @@ onUnmounted(() => {
async function init() {
const imgEl = unref(imgElRef)
if (!imgEl) {
return
}
const containerEl = unref(containerRef)
if (!imgEl || !containerEl) return
cropper.value = new Cropper(imgEl, {
...defaultOptions,
ready: () => {
isReady.value = true
realTimeCroppered()
emit('ready', cropper.value)
},
crop() {
debounceRealTimeCroppered()
},
zoom() {
debounceRealTimeCroppered()
},
cropmove() {
debounceRealTimeCroppered()
},
container: containerEl,
...props.options
})
// Wait for custom elements to be ready, then configure
await nextTick()
const cropperSelection = cropper.value.getCropperSelection()
const cropperImage = cropper.value.getCropperImage()
if (cropperSelection) {
cropperSelection.initialCoverage = 0.5
cropperSelection.aspectRatio = 1
cropperSelection.movable = true
cropperSelection.resizable = true
cropperSelection.addEventListener('change', () => {
debounceRealTimeCroppered()
})
}
if (cropperImage) {
cropperImage.addEventListener('transform', () => {
debounceRealTimeCroppered()
})
// Emit ready once image loads
cropperImage.addEventListener('load', () => {
emit('ready', cropper.value)
realTimeCroppered()
})
}
}
// Real-time display preview
@ -127,33 +92,45 @@ function realTimeCroppered() {
}
// event: return base64 and width and height information after cropping
function croppered() {
if (!cropper.value) {
return
async function croppered() {
if (!cropper.value) return
const selection = cropper.value.getCropperSelection()
if (!selection) return
const imgInfo = {
x: selection.x,
y: selection.y,
width: selection.width,
height: selection.height
}
let imgInfo = cropper.value.getData()
const canvas = props.circled ? getRoundedCanvas() : cropper.value.getCroppedCanvas()
canvas.toBlob((blob) => {
if (!blob) {
return
try {
let canvas = await selection.$toCanvas()
if (props.circled) {
canvas = getRoundedCanvas(canvas)
}
let fileReader: FileReader = new FileReader()
fileReader.readAsDataURL(blob)
fileReader.onloadend = (e) => {
emit('cropend', {
imgBase64: e.target?.result ?? '',
imgInfo
})
}
fileReader.onerror = () => {
emit('cropendError')
}
}, 'image/png')
canvas.toBlob((blob) => {
if (!blob) return
const fileReader = new FileReader()
fileReader.readAsDataURL(blob)
fileReader.onloadend = (e) => {
emit('cropend', {
imgBase64: e.target?.result ?? '',
imgInfo
})
}
fileReader.onerror = () => {
emit('cropendError')
}
}, 'image/png')
} catch {
// Selection may not be ready yet
}
}
// Get a circular picture canvas
function getRoundedCanvas() {
const sourceCanvas = cropper.value!.getCroppedCanvas()
function getRoundedCanvas(sourceCanvas: HTMLCanvasElement) {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')!
const width = sourceCanvas.width
@ -169,15 +146,13 @@ function getRoundedCanvas() {
return canvas
}
</script>
<style lang="scss">
<style lang="scss" scoped>
$prefix-cls: #{$namespace}-cropper-image;
.#{$prefix-cls} {
&--circled {
.cropper-view-box,
.cropper-face {
border-radius: 50%;
}
// cropperjs scoped 穿
> :deep(cropper-canvas) {
height: 100%;
}
}
</style>

View File

@ -2,7 +2,7 @@ import type Cropper from 'cropperjs'
export interface CropendResult {
imgBase64: string
imgInfo: Cropper.Data
imgInfo: { x: number; y: number; width: number; height: number }
}
export type { Cropper }

View File

@ -116,7 +116,8 @@ const toggleClick = () => {
:row="{
label: item.label
}"
>{{ item.label }}
>
{{ item.label }}
</slot>
</template>
@ -130,9 +131,7 @@ const toggleClick = () => {
<DictTag :type="item.dictType" :value="data[item.field] + ''" />
</slot>
<slot v-else :name="item.field" :row="data">
{{
item.mappedField ? data[item.mappedField] : data[item.field]
}}
{{ item.mappedField ? data[item.mappedField] : data[item.field] }}
</slot>
</template>
</ElDescriptionsItem>

View File

@ -165,8 +165,8 @@ $toolbar-position: -55px;
width: 80px;
height: 25px;
font-size: 12px;
color: #6a6a6a;
line-height: 25px;
color: #6a6a6a;
text-align: center;
background: #fff;
box-shadow:

View File

@ -94,9 +94,9 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
<style scoped lang="scss">
.editor-left {
z-index: 1;
flex-shrink: 0;
user-select: none;
box-shadow: 8px 0 8px -8px rgb(0 0 0 / 12%);
user-select: none;
flex-shrink: 0;
:deep(.el-collapse) {
border-top: none;

View File

@ -22,8 +22,9 @@
<div
v-if="property.indicator === 'number'"
class="absolute bottom-10px right-10px rounded-xl bg-black p-x-8px p-y-2px text-10px text-white opacity-40"
>{{ currentIndex }} / {{ property.items.length }}</div
>
{{ currentIndex }} / {{ property.items.length }}
</div>
</div>
</template>
<script setup lang="ts">

View File

@ -55,12 +55,12 @@ const handleToggleFab = () => {
/* 模态背景 */
.modal-bg {
position: absolute;
left: calc(50% - 375px / 2);
top: 0;
left: calc(50% - 375px / 2);
z-index: 11;
width: 375px;
height: 100%;
background-color: rgba(#000000, 0.4);
background-color: rgb(0 0 0 / 40%);
}
.fab-icon {

View File

@ -192,39 +192,39 @@ const handleAppLinkChange = (appLink: AppLink) => {
<style scoped lang="scss">
.hot-zone {
position: absolute;
background: var(--el-color-primary-light-7);
opacity: 0.8;
border: 1px solid var(--el-color-primary);
color: var(--el-color-primary);
font-size: 16px;
z-index: 10;
display: flex;
font-size: 16px;
color: var(--el-color-primary);
cursor: move;
background: var(--el-color-primary-light-7);
border: 1px solid var(--el-color-primary);
opacity: 0.8;
align-items: center;
justify-content: center;
cursor: move;
z-index: 10;
/* 控制点 */
.ctrl-dot {
position: absolute;
z-index: 11;
width: 8px;
height: 8px;
border-radius: 50%;
border: inherit;
background-color: #fff;
z-index: 11;
border: inherit;
border-radius: 50%;
}
.delete {
display: none;
position: absolute;
top: 0;
right: 0;
display: none;
padding: 2px 2px 6px 6px;
background-color: var(--el-color-primary);
border-radius: 0 0 0 80%;
cursor: pointer;
color: #fff;
text-align: right;
cursor: pointer;
background-color: var(--el-color-primary);
border-radius: 0 0 0 80%;
}
&:hover {

View File

@ -28,15 +28,15 @@ const props = defineProps<{ property: HotZoneProperty }>()
<style scoped lang="scss">
.hot-zone {
position: absolute;
background: var(--el-color-primary-light-7);
opacity: 0.8;
border: 1px solid var(--el-color-primary);
color: var(--el-color-primary);
font-size: 14px;
z-index: 10;
display: flex;
font-size: 14px;
color: var(--el-color-primary);
cursor: move;
background: var(--el-color-primary-light-7);
border: 1px solid var(--el-color-primary);
opacity: 0.8;
align-items: center;
justify-content: center;
cursor: move;
z-index: 10;
}
</style>

View File

@ -42,22 +42,22 @@ const handleOpenEditDialog = () => {
<style scoped lang="scss">
.hot-zone {
position: absolute;
display: flex;
font-size: 12px;
color: #fff;
cursor: move;
background: #409effbf;
border: 1px solid var(--el-color-primary);
color: #fff;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
cursor: move;
/* 控制点 */
.ctrl-dot {
position: absolute;
width: 4px;
height: 4px;
border-radius: 50%;
background-color: #fff;
border-radius: 50%;
}
}
</style>

View File

@ -39,7 +39,7 @@
</span>
</div>
</div>
</el-carousel-item>
</el-carousel-item>
</el-carousel>
</template>
@ -103,13 +103,16 @@ watch(
.el-carousel__indicator {
padding-top: 0;
padding-bottom: 0;
.el-carousel__button {
--el-carousel-indicator-height: 6px;
--el-carousel-indicator-width: 6px;
--el-carousel-indicator-out-color: #ff6000;
border-radius: 6px;
}
}
.el-carousel__indicator.is-active {
.el-carousel__button {
--el-carousel-indicator-width: 12px;

View File

@ -93,8 +93,8 @@ defineOptions({ name: 'NavigationBarCellProperty' })
const props = withDefaults(
defineProps<{
modelValue: NavigationBarCellProperty[]
isMp: boolean
modelValue?: NavigationBarCellProperty[]
isMp?: boolean
}>(),
{
modelValue: () => [],

View File

@ -67,10 +67,10 @@ const getSearchProp = computed(() => (cell: NavigationBarCellProperty) => {
.navigation-bar {
display: flex;
height: 50px;
padding: 0 6px;
background: #fff;
justify-content: space-between;
align-items: center;
padding: 0 6px;
/* 左边 */
.left {

View File

@ -77,7 +77,8 @@
v-if="property.fields.marketPrice.show && spu.marketPrice"
class="ml-4px text-10px line-through"
:style="{ color: property.fields.marketPrice.color }"
>{{ fenToYuan(spu.marketPrice) }}
>
{{ fenToYuan(spu.marketPrice) }}
</span>
</div>
<div class="text-12px">

View File

@ -74,8 +74,9 @@
v-if="property.fields.marketPrice.show && spu.marketPrice"
class="ml-4px text-10px line-through"
:style="{ color: property.fields.marketPrice.color }"
>{{ fenToYuan(spu.marketPrice) }}</span
>
{{ fenToYuan(spu.marketPrice) }}
</span>
</div>
<div class="text-12px">
<!-- 销量 -->

View File

@ -74,8 +74,9 @@
v-if="property.fields.marketPrice.show && spu.marketPrice"
class="ml-4px text-10px line-through"
:style="{ color: property.fields.marketPrice.color }"
>{{ fenToYuan(spu.marketPrice) }}</span
>
{{ fenToYuan(spu.marketPrice) }}
</span>
</div>
<div class="text-12px">
<!-- 销量 -->

View File

@ -583,12 +583,12 @@ $toolbar-height: 42px;
gap: 8px;
:deep(.el-tag) {
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
border: none;
box-shadow: 0 2px 8px 0 rgb(0 0 0 / 10%);
.el-tag__content {
width: 100%;
display: flex;
width: 100%;
align-items: center;
justify-content: flex-start;

View File

@ -27,7 +27,6 @@ const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('form')
export default defineComponent({
// eslint-disable-next-line vue/no-reserved-component-names
name: 'Form',
props: {
// Form

View File

@ -1,6 +1,12 @@
<!-- 数据字典 Select 选择器 -->
<template>
<el-select v-if="selectType === 'select'" class="w-1/1" v-bind="attrs">
<el-select
v-if="selectType === 'select'"
v-model="selectedValue"
class="w-1/1"
v-bind="attrs"
@change="handleChange"
>
<el-option
v-for="(dict, index) in getDictOptions"
:key="index"
@ -8,12 +14,24 @@
:value="dict.value"
/>
</el-select>
<el-radio-group v-if="selectType === 'radio'" class="w-1/1" v-bind="attrs">
<el-radio-group
v-if="selectType === 'radio'"
v-model="selectedValue"
class="w-1/1"
v-bind="attrs"
@change="handleChange"
>
<el-radio v-for="(dict, index) in getDictOptions" :key="index" :value="dict.value">
{{ dict.label }}
</el-radio>
</el-radio-group>
<el-checkbox-group v-if="selectType === 'checkbox'" class="w-1/1" v-bind="attrs">
<el-checkbox-group
v-if="selectType === 'checkbox'"
v-model="selectedValue"
class="w-1/1"
v-bind="attrs"
@change="handleChange"
>
<el-checkbox
v-for="(dict, index) in getDictOptions"
:key="index"
@ -33,6 +51,7 @@ const attrs = useAttrs()
//
interface Props {
dictType: string //
modelValue?: any // form-create modelValue
valueType?: 'str' | 'int' | 'bool' //
selectType?: 'select' | 'radio' | 'checkbox' // select checkbox radio
formCreateInject?: any
@ -43,6 +62,20 @@ const props = withDefaults(defineProps<Props>(), {
selectType: 'select'
})
const emit = defineEmits<{
(e: 'update:modelValue', value: any): void
}>()
const selectedValue = ref<any>()
watch(
() => props.modelValue,
(newValue) => {
selectedValue.value = newValue
},
{ immediate: true }
)
//
const getDictOptions = computed(() => {
switch (props.valueType) {
@ -56,4 +89,8 @@ const getDictOptions = computed(() => {
return []
}
})
const handleChange = (value: any) => {
emit('update:modelValue', value)
}
</script>

View File

@ -55,7 +55,6 @@ const displayUrl = computed(() => props.url || props.modelValue || '') // 显示
const showPreview = computed(() => {
return displayUrl.value && isUrl(displayUrl.value)
}) //
</script>
<style scoped>
@ -64,9 +63,9 @@ const showPreview = computed(() => {
}
.iframe-preview {
overflow: hidden;
border: 1px solid #dcdfe6;
border-radius: 4px;
overflow: hidden;
}
.iframe-content {
@ -76,11 +75,11 @@ const showPreview = computed(() => {
.iframe-placeholder {
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
background-color: #fafafa;
border: 1px dashed #dcdfe6;
border-radius: 4px;
background-color: #fafafa;
align-items: center;
justify-content: center;
}
</style>

View File

@ -74,8 +74,8 @@ export const useUploadImgRule = () => {
{
type: 'switch',
field: 'disabled',
title: '是否显示删除按钮',
value: true
title: '是否禁用',
value: false
},
{
type: 'switch',

View File

@ -7,7 +7,7 @@ const props = defineProps({
src: propTypes.string.def('')
})
const loading = ref(true)
const frameRef = ref<HTMLElement | null>(null)
const frameRef = ref<HTMLIFrameElement | null>(null)
const init = () => {
nextTick(() => {
loading.value = true
@ -20,6 +20,11 @@ const init = () => {
onMounted(() => {
init()
})
onBeforeUnmount(() => {
if (!frameRef.value) return
frameRef.value.onload = null
frameRef.value.src = 'about:blank'
})
watch(
() => props.src,
() => {

View File

@ -1,6 +1,6 @@
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import Iconify from '@purge-icons/generated'
import { Icon as IconifyIcon } from '@iconify/vue'
import { useDesign } from '@/hooks/web/useDesign'
defineOptions({ name: 'Icon' })
@ -20,57 +20,16 @@ const props = defineProps({
svgClass: propTypes.string.def('')
})
const elRef = ref<ElRef>(null)
const isLocal = computed(() => props.icon?.startsWith('svg-icon:'))
const symbolId = computed(() => {
return unref(isLocal) ? `#icon-${props.icon.split('svg-icon:')[1]}` : props.icon
})
const getIconifyStyle = computed(() => {
const { color, size } = props
return {
fontSize: `${size}px`,
height: '1em',
color
}
})
const getSvgClass = computed(() => {
const { svgClass } = props
return `iconify ${svgClass}`
})
const updateIcon = async (icon: string) => {
if (unref(isLocal)) return
const el = unref(elRef)
if (!el) return
await nextTick()
if (!icon) return
const svg = Iconify.renderSVG(icon, {})
if (svg) {
el.textContent = ''
el.appendChild(svg)
} else {
const span = document.createElement('span')
span.className = 'iconify'
span.dataset.icon = icon
el.textContent = ''
el.appendChild(span)
}
}
watch(
() => props.icon,
(icon: string) => {
updateIcon(icon)
}
)
</script>
<template>
@ -79,8 +38,11 @@ watch(
<use :xlink:href="symbolId" />
</svg>
<span v-else ref="elRef" :class="$attrs.class" :style="getIconifyStyle">
<span :class="getSvgClass" :data-icon="symbolId"></span>
</span>
<IconifyIcon
v-else
:icon="symbolId"
:class="getSvgClass"
:style="{ fontSize: `${size}px`, color }"
/>
</ElIcon>
</template>

View File

@ -14,7 +14,6 @@ defineProps({
title: propTypes.string.def(''),
schema: {
type: Array as PropType<Array<string | TipSchema>>,
required: true,
default: () => []
},
showIndex: propTypes.bool.def(true),

View File

@ -26,6 +26,7 @@ const { modelValue, color } = useVModels(props, emit)
<style scoped lang="scss">
:deep(.el-input-group__append) {
padding: 0;
.el-color-picker__trigger {
padding: 0;
border-left: none;

View File

@ -225,15 +225,16 @@ const eachCube = (callback: (x: number, y: number, cube: Cube) => void) => {
<style lang="scss" scoped>
.cube-table {
position: relative;
border-spacing: 0;
border-collapse: collapse;
border-spacing: 0;
.cube {
border: 1px solid var(--el-border-color);
text-align: center;
color: var(--el-text-color-secondary);
text-align: center;
cursor: pointer;
border: 1px solid var(--el-border-color);
box-sizing: border-box;
&.active {
background: var(--el-color-primary-light-9);
}
@ -242,28 +243,28 @@ const eachCube = (callback: (x: number, y: number, cube: Cube) => void) => {
.hot-area {
position: absolute;
display: flex;
color: var(--el-color-primary);
cursor: pointer;
background: var(--el-color-primary-light-8);
border: 1px solid var(--el-color-primary);
border-collapse: collapse;
border-spacing: 0;
box-sizing: border-box;
align-items: center;
justify-content: center;
border: 1px solid var(--el-color-primary);
background: var(--el-color-primary-light-8);
color: var(--el-color-primary);
box-sizing: border-box;
border-spacing: 0;
border-collapse: collapse;
cursor: pointer;
.btn-delete {
z-index: 1;
position: absolute;
top: -8px;
right: -8px;
height: 16px;
width: 16px;
z-index: 1;
display: flex;
width: 16px;
height: 16px;
background-color: #fff;
border-radius: 50%;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: #fff;
}
}
}

View File

@ -51,14 +51,14 @@ onMounted(async () => {
<style lang="scss">
.markdown-view {
font-family: PingFang SC;
max-width: 100%;
font-family: 'PingFang SC';
font-size: 0.95rem;
font-weight: 400;
line-height: 1.6rem;
letter-spacing: 0em;
text-align: left;
letter-spacing: 0;
color: #3b3e55;
max-width: 100%;
text-align: left;
pre {
position: relative;
@ -69,22 +69,23 @@ onMounted(async () => {
}
code.hljs {
border-radius: 6px;
padding-top: 20px;
width: auto;
@media screen and (min-width: 1536px) {
padding-top: 20px;
border-radius: 6px;
@media screen and (width >= 1536px) {
width: 960px;
}
@media screen and (max-width: 1536px) and (min-width: 1024px) {
@media screen and (width <= 1536px) and (width >= 1024px) {
width: calc(100vw - 400px - 64px - 32px * 2);
}
@media screen and (max-width: 1024px) and (min-width: 768px) {
@media screen and (width <= 1024px) and (width >= 768px) {
width: calc(100vw - 32px * 2);
}
@media screen and (max-width: 768px) {
@media screen and (width <= 768px) {
width: calc(100vw - 16px * 2);
}
}
@ -107,9 +108,9 @@ onMounted(async () => {
h4,
h5,
h6 {
color: var(--color-G900);
margin: 24px 0 8px;
font-weight: 600;
color: #3b3e55;
}
h1 {
@ -145,8 +146,8 @@ onMounted(async () => {
/* 列表(有序,无序) */
ul,
ol {
margin: 0 0 8px 0;
padding: 0;
margin: 0 0 8px;
font-size: 16px;
line-height: 24px;
color: #3b3e55; // var(--color-CG600);
@ -158,8 +159,8 @@ onMounted(async () => {
}
ol > li {
list-style-type: decimal;
margin-bottom: 1rem;
list-style-type: decimal;
// ,
// &:nth-child(n + 10) {
// margin-left: 30px;
@ -171,23 +172,23 @@ onMounted(async () => {
}
ul > li {
list-style-type: disc;
font-size: 16px;
line-height: 24px;
margin-right: 11px;
margin-bottom: 1rem;
font-size: 16px;
line-height: 24px;
color: #3b3e55; // var(--color-G900);
list-style-type: disc;
}
ol ul,
ol ul > li,
ul ul,
ul ul li {
margin-bottom: 1rem;
margin-left: 6px;
// list-style: circle;
font-size: 16px;
list-style: none;
margin-left: 6px;
margin-bottom: 1rem;
}
ul ul ul,

View File

@ -31,7 +31,7 @@ import { ElTag } from 'element-plus'
defineOptions({ name: 'OperateLogV2' })
interface Props {
logList: OperateLogVO[] //
logList?: OperateLogVO[] //
}
withDefaults(defineProps<Props>(), {

View File

@ -18,7 +18,7 @@
</el-select>
</ElDialog>
<div v-else class="custom-hover" @click.stop="showTopSearch = !showTopSearch">
<Icon icon="ep:search" :color="color"/>
<Icon icon="ep:search" :color="color" />
<el-select
@click.stop
filterable

View File

@ -246,9 +246,9 @@ onMounted(() => {
<style lang="scss" scoped>
.simple-process-model-container {
position: relative;
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
user-select: none; //
}

View File

@ -629,6 +629,14 @@ export const COMPARISON_OPERATORS: DictDataVO = [
{
value: '<=',
label: '小于等于'
},
{
value: 'contain',
label: '包含'
},
{
value: '!contain',
label: '不包含'
}
]
// 审批操作按钮名称

View File

@ -17,18 +17,18 @@
v-model="currentNode.name"
:placeholder="currentNode.name"
/>
<div v-else class="node-name"
>{{ currentNode.name }}
<Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()"
/></div>
<div v-else class="node-name">
{{ currentNode.name }}
<Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
</div>
<div class="divide-line"></div>
</div>
</template>
<div>
<div class="mb-3 font-size-16px" v-if="currentNode.conditionSetting?.defaultFlow"
>未满足其它条件时将进入此分支该分支不可编辑和删除</div
>
<div class="mb-3 font-size-16px" v-if="currentNode.conditionSetting?.defaultFlow">
未满足其它条件时将进入此分支该分支不可编辑和删除
</div>
<div v-else>
<Condition ref="conditionRef" v-model="condition" />
</div>

View File

@ -218,8 +218,9 @@
:value="FieldPermissionType.READ"
size="large"
:label="FieldPermissionType.WRITE"
><span></span
></el-radio>
>
<span></span>
</el-radio>
</div>
<div class="item-radio-wrap">
<el-radio
@ -227,16 +228,18 @@
size="large"
:label="FieldPermissionType.WRITE"
disabled
><span></span
></el-radio>
>
<span></span>
</el-radio>
</div>
<div class="item-radio-wrap">
<el-radio
:value="FieldPermissionType.NONE"
size="large"
:label="FieldPermissionType.NONE"
><span></span
></el-radio>
>
<span></span>
</el-radio>
</div>
</el-radio-group>
</div>

View File

@ -95,24 +95,27 @@
:value="FieldPermissionType.READ"
size="large"
:label="FieldPermissionType.READ"
><span></span
></el-radio>
>
<span></span>
</el-radio>
</div>
<div class="item-radio-wrap">
<el-radio
:value="FieldPermissionType.WRITE"
size="large"
:label="FieldPermissionType.WRITE"
><span></span
></el-radio>
>
<span></span>
</el-radio>
</div>
<div class="item-radio-wrap">
<el-radio
:value="FieldPermissionType.NONE"
size="large"
:label="FieldPermissionType.NONE"
><span></span
></el-radio>
>
<span></span>
</el-radio>
</div>
</el-radio-group>
</div>

View File

@ -414,7 +414,7 @@
<div>
<el-divider content-position="left">跳过表达式</el-divider>
<el-form-item prop="skipExpression">
<el-input v-model="configForm.skipExpression" type="textarea" />
<el-input v-model="configForm.skipExpression" type="textarea" />
</el-form-item>
</div>
</el-form>
@ -444,9 +444,9 @@
:placeholder="item.displayName"
v-if="btnDisplayNameEdit[index]"
/>
<el-button v-else text @click="changeBtnDisplayName(index)"
>{{ item.displayName }} &nbsp;<Icon icon="ep:edit"
/></el-button>
<el-button v-else text @click="changeBtnDisplayName(index)">
{{ item.displayName }} &nbsp;<Icon icon="ep:edit" />
</el-button>
</div>
<div class="button-setting-item-label">
<el-switch v-model="item.enable" />
@ -483,24 +483,27 @@
:value="FieldPermissionType.READ"
size="large"
:label="FieldPermissionType.READ"
><span></span
></el-radio>
>
<span></span>
</el-radio>
</div>
<div class="item-radio-wrap">
<el-radio
:value="FieldPermissionType.WRITE"
size="large"
:label="FieldPermissionType.WRITE"
><span></span
></el-radio>
>
<span></span>
</el-radio>
</div>
<div class="item-radio-wrap">
<el-radio
:value="FieldPermissionType.NONE"
size="large"
:label="FieldPermissionType.NONE"
><span></span
></el-radio>
>
<span></span>
</el-radio>
</div>
</el-radio-group>
</div>

View File

@ -40,9 +40,9 @@
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
</div>
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
<div class="toolbar-icon">
<Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode" />
</div>
</div>
</div>

View File

@ -33,9 +33,9 @@
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
</div>
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
<div class="toolbar-icon">
<Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode" />
</div>
</div>
</div>

View File

@ -33,9 +33,9 @@
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
</div>
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
<div class="toolbar-icon">
<Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode" />
</div>
</div>
</div>

View File

@ -1,63 +1,67 @@
<template>
<div class="end-node-wrapper">
<div class="end-node-box cursor-pointer" :class="`${useTaskStatusClass(currentNode?.activityStatus)}`" @click="nodeClick">
<div
class="end-node-box cursor-pointer"
:class="`${useTaskStatusClass(currentNode?.activityStatus)}`"
@click="nodeClick"
>
<span class="node-fixed-name" title="结束">结束</span>
</div>
</div>
<el-dialog title="审批信息" v-model="dialogVisible" width="1000px" append-to-body>
<el-row>
<el-table
:data="processInstanceInfos"
size="small"
border
header-cell-class-name="table-header-gray"
>
<el-table-column
label="序号"
header-align="center"
align="center"
type="index"
width="50"
/>
<el-table-column
label="发起人"
prop="assigneeUser.nickname"
min-width="100"
align="center"
/>
<el-table-column label="部门" min-width="100" align="center">
<template #default="scope">
{{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
</template>
</el-table-column>
<el-table-column
:formatter="dateFormatter"
align="center"
label="开始时间"
prop="createTime"
min-width="140"
/>
<el-table-column
:formatter="dateFormatter"
align="center"
label="结束时间"
prop="endTime"
min-width="140"
/>
<el-table-column align="center" label="审批状态" prop="status" min-width="90">
<template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column align="center" label="耗时" prop="durationInMillis" width="100">
<template #default="scope">
{{ formatPast2(scope.row.durationInMillis) }}
</template>
</el-table-column>
</el-table>
</el-row>
</el-dialog>
<el-row>
<el-table
:data="processInstanceInfos"
size="small"
border
header-cell-class-name="table-header-gray"
>
<el-table-column
label="序号"
header-align="center"
align="center"
type="index"
width="50"
/>
<el-table-column
label="发起人"
prop="assigneeUser.nickname"
min-width="100"
align="center"
/>
<el-table-column label="部门" min-width="100" align="center">
<template #default="scope">
{{ scope.row.assigneeUser?.deptName || scope.row.ownerUser?.deptName }}
</template>
</el-table-column>
<el-table-column
:formatter="dateFormatter"
align="center"
label="开始时间"
prop="createTime"
min-width="140"
/>
<el-table-column
:formatter="dateFormatter"
align="center"
label="结束时间"
prop="endTime"
min-width="140"
/>
<el-table-column align="center" label="审批状态" prop="status" min-width="90">
<template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column align="center" label="耗时" prop="durationInMillis" width="100">
<template #default="scope">
{{ formatPast2(scope.row.durationInMillis) }}
</template>
</el-table-column>
</el-table>
</el-row>
</el-dialog>
</template>
<script setup lang="ts">
import { SimpleFlowNode } from '../consts'
@ -83,17 +87,17 @@ const dialogVisible = ref(false) // 弹窗可见性
const processInstanceInfos = ref<any[]>([]) //
const nodeClick = () => {
if (readonly) {
if(processInstance && processInstance.value){
if (readonly) {
if (processInstance && processInstance.value) {
processInstanceInfos.value = [
{
assigneeUser: processInstance.value.startUser,
createTime: processInstance.value.startTime,
endTime: processInstance.value.endTime,
status: processInstance.value.status,
durationInMillis: processInstance.value.durationInMillis
}
]
{
assigneeUser: processInstance.value.startUser,
createTime: processInstance.value.startTime,
endTime: processInstance.value.endTime,
status: processInstance.value.status,
durationInMillis: processInstance.value.durationInMillis
}
]
dialogVisible.value = true
}
}

View File

@ -8,9 +8,9 @@
>
<span class="iconfont icon-exclusive icon-size condition"></span>
</div>
<el-button v-else class="branch-node-add" color="#67c23a" @click="addCondition" plain
>添加条件</el-button
>
<el-button v-else class="branch-node-add" color="#67c23a" @click="addCondition" plain>
添加条件
</el-button>
<div
class="branch-node-item"

View File

@ -8,9 +8,9 @@
>
<span class="iconfont icon-inclusive icon-size inclusive"></span>
</div>
<el-button v-else class="branch-node-add" color="#345da2" @click="addCondition" plain
>添加条件</el-button
>
<el-button v-else class="branch-node-add" color="#345da2" @click="addCondition" plain>
添加条件
</el-button>
<div
class="branch-node-item"
v-for="(item, index) in currentNode.conditionNodes"

View File

@ -8,9 +8,9 @@
>
<span class="iconfont icon-parallel icon-size parallel"></span>
</div>
<el-button v-else class="branch-node-add" color="#626aef" @click="addCondition" plain
>添加分支</el-button
>
<el-button v-else class="branch-node-add" color="#626aef" @click="addCondition" plain>
添加分支
</el-button>
<div
class="branch-node-item"
v-for="(item, index) in currentNode.conditionNodes"

View File

@ -35,9 +35,9 @@
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
</div>
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
<div class="toolbar-icon">
<Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode" />
</div>
</div>
</div>

View File

@ -9,9 +9,9 @@
]"
>
<div class="node-title-container">
<div class="node-title-icon start-user"
><span class="iconfont icon-start-user"></span
></div>
<div class="node-title-icon start-user">
<span class="iconfont icon-start-user"></span>
</div>
<input
v-if="!readonly && showInput"
type="text"

View File

@ -35,9 +35,9 @@
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
</div>
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
<div class="toolbar-icon">
<Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode" />
</div>
</div>
</div>

View File

@ -40,9 +40,9 @@
<Icon icon="ep:arrow-right-bold" v-if="!readonly" />
</div>
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon"
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
/></div>
<div class="toolbar-icon">
<Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode" />
</div>
</div>
</div>
<!-- 传递子节点给添加节点组件会在子节点前面添加节点 -->

View File

@ -9,7 +9,6 @@ import { set } from 'lodash-es'
import { Pagination, TableColumn, TableSetPropsType, TableSlotDefault } from '@/types/table'
export default defineComponent({
// eslint-disable-next-line vue/no-reserved-component-names
name: 'Table',
props: {
pageSize: propTypes.number.def(10),

View File

@ -33,8 +33,8 @@ import { ElTable } from 'element-plus'
defineOptions({ name: 'TableSelectForm' })
withDefaults(
defineProps<{
modelValue: any[]
title: string
modelValue?: any[]
title?: string
}>(),
{ modelValue: () => [], title: '选择' }
)

View File

@ -1,5 +1,5 @@
<template>
<div class="upload-box">
<div class="upload-box" :style="uploadStyle">
<el-upload
:id="uuid"
:accept="fileType.join(',')"
@ -82,6 +82,13 @@ const props = defineProps({
showBtnText: propTypes.bool.def(true), //
directory: propTypes.string.def(undefined) // ==> undefined
})
const uploadStyle = computed(() => ({
'--upload-width': props.width,
'--upload-height': props.height,
'--upload-border-radius': props.borderradius
}))
const { t } = useI18n() //
const message = useMessage() //
// id
@ -167,11 +174,11 @@ const uploadError = () => {
display: flex;
align-items: center;
justify-content: center;
width: v-bind(width);
height: v-bind(height);
width: var(--upload-width);
height: var(--upload-height);
overflow: hidden;
border: 1px dashed var(--el-border-color-darker);
border-radius: v-bind(borderradius);
border-radius: var(--upload-border-radius);
transition: var(--el-transition-duration-fast);
&:hover {
@ -192,7 +199,7 @@ const uploadError = () => {
overflow: hidden;
background-color: transparent;
border: 1px dashed var(--el-border-color-darker);
border-radius: v-bind(borderradius);
border-radius: var(--upload-border-radius);
&:hover {
border: 1px dashed var(--el-color-primary);

View File

@ -1,5 +1,5 @@
<template>
<div class="upload-box">
<div class="upload-box" :style="uploadStyle">
<el-upload
v-model:file-list="fileList"
:accept="fileType.join(',')"
@ -85,6 +85,12 @@ const props = defineProps({
directory: propTypes.string.def(undefined) // ==> undefined
})
const uploadStyle = computed(() => ({
'--upload-width': props.width,
'--upload-height': props.height,
'--upload-border-radius': props.borderradius
}))
const { uploadUrl, httpRequest } = useUpload(props.directory)
const fileList = ref<UploadUserFile[]>([])
@ -238,7 +244,7 @@ const handleExceed = () => {
padding: 0;
overflow: hidden;
border: 1px dashed var(--el-border-color-darker);
border-radius: v-bind(borderradius);
border-radius: var(--upload-border-radius);
&:hover {
border: 1px dashed var(--el-color-primary);
@ -252,10 +258,10 @@ const handleExceed = () => {
.el-upload-list__item,
.el-upload--picture-card {
width: v-bind(width);
height: v-bind(height);
width: var(--upload-width);
height: var(--upload-height);
background-color: transparent;
border-radius: v-bind(borderradius);
border-radius: var(--upload-border-radius);
}
.upload-image {

View File

@ -24,7 +24,7 @@ export const useUpload = (directory?: string) => {
const uploadProgressHandler = (evt: AxiosProgressEvent) => {
const upEvt: UploadProgressEvent = Object.assign(evt.event)
upEvt.percent = evt.progress ? evt.progress * 100 : 0
options.onProgress(upEvt) // 触发 el-upload 的 on-progress
options.onProgress?.(upEvt) // 触发 el-upload 的 on-progress
}
// 模式一:前端上传

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