Compare commits

...

181 Commits

Author SHA1 Message Date
YunaiV dc1a582fbd fix(iot): 修复源端 IoT 对齐基线问题
- 收紧物模型标识符前端校验规则
- 修复场景联动设备状态触发器校验
- 修正设备列表状态字段展示
2026-05-25 00:11:19 +08:00
YunaiV dd009a1de0 优化代码排版 2026-05-24 00:18:49 +08:00
芋道源码 fa42f8e574
Merge pull request #225 from DevDengChao/codex/lint-mall-bpm-misc
chore: fix mall bpm and misc lint
2026-05-23 22:07:05 +08:00
芋道源码 a1c43ed427
Merge pull request #224 from DevDengChao/codex/lint-mp-mes
fix: lint mp and mes views
2026-05-23 22:06:15 +08:00
芋道源码 31a7f6248a
Merge pull request #223 from DevDengChao/codex/lint-iot-ai
chore: fix ai and iot lint
2026-05-23 22:06:03 +08:00
芋道源码 02c0d0cb3b
Merge pull request #222 from DevDengChao/codex/lint-components
fix: lint src components
2026-05-23 21:35:25 +08:00
YunaiV a2fbf5b712 fix: clean up BPMN viewer resize observer 2026-05-23 21:06:20 +08:00
YunaiV cb78c2935d Merge remote-tracking branch 'origin/master' 2026-05-23 18:24:57 +08:00
YunaiV 798318ef7d Merge branch 'master' of https://github.com/yudaocode/yudao-ui-admin-vue3 2026-05-23 18:21:33 +08:00
芋道源码 8971c37059
Merge pull request #221 from HmEJ/feature/bpm
流程图居中显示
2026-05-23 18:21:10 +08:00
芋道源码 7dd7309e9c
!878 fix: 场景联动中设备状态变更配置的参数值(在线-online,离线-offline)与后台实际使用的(在线-1,离线-2)不一致,导致场景不生效。
Merge pull request !878 from 熊猫大侠/master-iot
2026-05-23 09:33:27 +00:00
YunaiV 1612e3e1b6 fix(iot): 场景联动动作类型切换清理逻辑失效
updateActionType 先调 onActionTypeChange(此时 action.type 仍是旧值)
再赋新值,修复 type guard 永远 false 导致切换执行器类型不清空旧
identifier;onActionTypeChange 内恒真的 type !== action.type
简化为 if (action.identifier)
2026-05-22 20:24:58 +08:00
panda 1888757854 fix: 场景联动中设备状态变更配置的参数值(在线-online,离线-offline)与后台实际使用的(在线-1,离线-2)不一致,导致场景不生效。 2026-05-22 10:50:28 +08:00
YunaiV b5bc537f86 feat(alert): simplify alert config loading and display 2026-05-21 17:27:27 +08:00
YunaiV fee633b0c8 feat(iot): 优化代码,尽量使用 ProductStatusEnum 枚举 2026-05-21 00:17:57 +08:00
YunaiV f26c65c03f fix(iot): 固件操作的权限校验 2026-05-20 00:41:32 +08:00
YunaiV 8ad7180c2b fix(CheckOrderForm): remove preselectDisabled option from SKU selection 2026-05-18 21:21:15 +08:00
DevDengChao f5bcaf22f9 chore: fix mall bpm misc lint
Co-authored-by: Codex <codex@openai.com>
2026-05-18 14:19:49 +08:00
DevDengChao 40d762070f fix: lint src components
Co-authored-by: Codex <codex@openai.com>
2026-05-18 14:19:40 +08:00
DevDengChao 7fa9311753 fix lint issues in mp and mes views
Co-authored-by: Codex <codex@openai.com>
2026-05-18 14:18:27 +08:00
DevDengChao 60c74b991e chore: fix ai iot lint
Co-authored-by: Codex <codex@openai.com>
2026-05-18 14:16:58 +08:00
YunaiV c4519a8696 feat(alert): enhance description input to textarea for better usability
feat(movement): add function to retrieve selected inventory keys
2026-05-18 13:30:04 +08:00
YunaiV 073c54bc1d feat(wms):优化出库的交互,已选择的库存,disabled 掉,体验更好 2026-05-18 08:58:33 +08:00
YunaiV f6963cde37 Merge remote-tracking branch 'origin/master' 2026-05-18 01:03:15 +08:00
YunaiV 0c54bf28b3 fix(iot):物模型编辑回显时,service / event 子字段补数组兜底,避免参数列表绑定 undefined 2026-05-18 01:03:04 +08:00
YunaiV d0cd93de5a chore: fix eslint warnings from vite dev 2026-05-18 00:59:59 +08:00
YunaiV a2d043bc72 fix: normalize scss variable injection on Windows
- normalize injected variables.scss path for Windows Sass
- skip variable-defining scss files to avoid duplicate global variables
2026-05-18 00:47:16 +08:00
YunaiV 7622a44bbb fix:尝试修复 windows 的兼容性:additionalData: `@use "${pathResolve('src/styles/variables.scss')}" as *;`, 2026-05-18 00:31:55 +08:00
YunaiV 46c436e0df feat(wms):将首页的枚举值去掉,统一合并到 constants 里,更聚焦点 2026-05-17 18:17:30 +08:00
YunaiV ceb1aa9bce Merge remote-tracking branch 'origin/master' 2026-05-17 17:30:48 +08:00
YunaiV 26a3b87114 feat(iot):移除 DeviceTableSelect.vue、ProductTableSelect.vue 无用组件 2026-05-17 17:30:38 +08:00
YunaiV a3f89d686c Merge pull request #215 from yudaocode/upgrade
# Conflicts:
#	build/vite/optimize.ts
#	package-lock.json
#	package.json
#	pnpm-lock.yaml
2026-05-17 13:56:29 +08:00
YunaiV 28473434da fix: reduce low-risk vue-tsc errors in erp forms
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 18:48:35 +08:00
YunaiV 1f47e3c9d6 feat:增加 ts:check 优化,避免 ai 校验 oom 报错 2026-05-16 17:47:11 +08:00
YunaiV 4f4c64cfff feat(wms):调整 README.md 2026-05-16 15:09:42 +08:00
YunaiV 9a49c2df20 feat(wms):调整 README.md 2026-05-16 14:56:10 +08:00
YunaiV 9b8b1322b7 Merge branch 'wms' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 2026-05-16 14:41:33 +08:00
YunaiV ecb15b6481 feat:更新 README.md 2026-05-16 14:40:33 +08:00
YunaiV a18c81511c fix(wms): 完善单据状态保护与金额精度处理
- 后端补充商品、往来企业唯一性校验
- 单据更新改为按草稿状态条件更新,避免覆盖已完成单据
- 补充 WMS 金额、规格精度迁移 SQL 与测试表结构
- 前端统一明细金额兜底计算,优化完成/作废取消处理
2026-05-15 19:59:45 +08:00
YunaiV b3bb1114ba fix(wms): 完善单据状态保护与金额精度处理
- 后端补充商品、往来企业唯一性校验
- 单据更新改为按草稿状态条件更新,避免覆盖已完成单据
- 补充 WMS 金额、规格精度迁移 SQL 与测试表结构
- 前端统一明细金额兜底计算,优化完成/作废取消处理
2026-05-15 19:48:33 +08:00
YunaiV 50cfbfe58b feat(wms): 统一数量金额精度并清理 schema 脱钩
后端:
- 新增 sql/mysql/wms/20260515_wms_amount_precision.sql
  11 张表金额字段统一升到 decimal(16,2),覆盖 SKU 单价、单据主表/明细
  总金额/行金额、盘库实际金额、库存流水单价/行金额
- 新增 sql/mysql/wms/20260515_wms_sku_dimension_precision.sql
  SKU 长宽高对齐 lite 改为 decimal(10,1)、毛/净重改为 decimal(10,3)
- 测试 SQL create_tables.sql 全量同步生产 MySQL:数量 (20,2)、
  金额 (16,2)、长度 (10,1)、重量 (10,3),修复"测试 schema 与生产
  脱钩"导致单测假阳性的隐患
- WmsWarehouseServiceImpl.validateWarehouseCodeUnique 去掉
  StrUtil.isBlank 提前 return,因 code 已由 VO 层 @NotBlank 强制非空
- WmsWarehouseServiceImplTest 同步调整

前端:
- ReceiptOrderForm / ReceiptOrderDetail 合计行去掉"单价合计"派生展示,
  单价不能跨行相加;保留数量合计与行金额合计

文档:
- review-opus.md 收口至仅剩 F10 (SQL 导出,用户认领)
- 新增 fix-plan.md 与 精度调整-codex讨论.md,沉淀本轮决策依据
2026-05-15 18:52:37 +08:00
YunaiV c81116678a feat(wms): 拆 simple-list 列表 VO、补首页校验与业务单号搜索框
后端:
- 新增 WmsItemListReqVO / WmsMerchantListReqVO,simple-list 接口不再
  复用分页 PageReqVO,Swagger 上不再误暴露 pageNo/pageSize 字段
- WmsItemController / WmsMerchantController 的 getXxxSimpleList 改用
  独立 ListReqVO;Mapper.selectList、Service.getXxxList 同步调整签名
- WmsHomeStatisticsServiceImpl 三个查询入口加 validateWarehouseIfPresent,
  非空 warehouseId 走 warehouseService.validateWarehouseExists 校验,
  避免前端误传任意 id 直接落到首页 SQL
- 新增 sql/mysql/wms/20260515_wms_total_price.sql:幂等给 4 张明细 / 流水表
  补 total_price 列并按 ROUND(quantity*price, 2) 回填历史数据

前端:
- receipt/index.vue + shipment/index.vue 搜索栏补 bizOrderNo 输入控件,
  对齐已声明的 queryParams 与后端 PageReqVO 支持
- WmsHomeOrderSummaryCards.getStatusPercent 改 function 声明,并去掉
  最小 4% 占比下限,保留真实比例
2026-05-15 18:23:59 +08:00
YunaiV fa570c2637 feat(wms): 持久化出入库移库明细行金额并补全库存流水金额展示 2026-05-15 14:11:14 +08:00
YunaiV 2ffbcbd71f feat(wms):打印时,更新、新增的信息整合 2026-05-15 13:25:56 +08:00
YunaiV 524ed28973 feat(wms):打印时的 barcode 标签 2026-05-15 13:12:23 +08:00
YunaiV 508d06b493 feat(wms):更新修复进展 2026-05-15 13:09:30 +08:00
YunaiV f82ae7e0c8 feat(wms):优化整体代码结构 2026-05-15 12:59:11 +08:00
YunaiV 24343f66fc feat(wms):调整 check 的实现 2026-05-15 11:00:14 +08:00
YunaiV 52972506a8 feat(wms):更新修复进展 2026-05-15 10:45:28 +08:00
YunaiV d128df618e feat(wms):增加 code 字段生成(从后端到前端),用户更可控 2026-05-15 10:22:37 +08:00
YunaiV 5f944548a3 fix(wms): 调整文档的最新内容 2026-05-15 09:46:40 +08:00
YunaiV 7bae330828 fix(wms): 移库选择商品时,必须两个仓库都选择完 2026-05-15 09:06:59 +08:00
YunaiV 7ff8cb78e1 feat(wms):统一 4 个订单界面的样式和代码风格 2026-05-15 08:44:08 +08:00
YunaiV 8252f7b068 feat(wms):优化 onlyPositiveQuantity 只查询库存非空的处理。 2026-05-15 08:24:54 +08:00
YunaiV 19db64c08a feat(wms):优化代码的排版 2026-05-14 23:04:50 +08:00
YunaiV e0352af8b0 feat(wms):优化代码的排版 2026-05-14 22:53:53 +08:00
YunaiV ae54f938cf feat(wms):优化首页的代码实现 2026-05-14 22:35:53 +08:00
YunaiV 58537a34c7 feat(wms):增加首页的 review 2026-05-14 18:53:56 +08:00
YunaiV 804ad667e0 feat(wms):优化盘库单的实现 2026-05-14 17:03:49 +08:00
YunaiV eaedb1e0ca feat(wms):移库管理,调整合计金额、数量的字段与交互。(前端负责展示,后端负责计算) 2026-05-14 09:46:19 +08:00
YunaiV df013ac69c feat(wms):出库管理,调整合计金额、数量的字段与交互。(前端负责展示,后端负责计算) 2026-05-14 09:07:26 +08:00
YunaiV 6f96d004a9 feat(wms):入库管理,调整合计金额、数量的字段与交互。(前端负责展示,后端负责计算) 2026-05-14 08:39:04 +08:00
YunaiV 44808eb3f4 feat(wms):调整 order_time 放到【仓库】后面 2026-05-14 00:02:03 +08:00
YunaiV a7911bcbcf feat(wms):增加 order_time 单据字段 2026-05-13 23:31:03 +08:00
YunaiV 8d06f87e0f feat(wms):减法,去掉批次号等字段 2026-05-13 22:06:37 +08:00
YunaiV fdbb98fe65 feat(wms):减法,去掉 area 表 2026-05-13 20:29:25 +08:00
YunaiV c5948d405e feat(wms):减法,去掉 detail 表,和 mes 更对齐 2026-05-13 18:42:51 +08:00
YunaiV 70aff05ef5 feat(wms):新增移库、盘库管理 2026-05-13 09:47:45 +08:00
YunaiV b3b35e147b feat(wms):新增出库管理 2026-05-13 08:57:41 +08:00
YunaiV 765c8ea94f feat(wms):修复只能删除作废的入库单的问题 2026-05-13 00:42:29 +08:00
YunaiV ac49ba5c6d feat(wms):增加供应商 select 组件 2026-05-12 23:34:45 +08:00
YunaiV 3ef4e8424f feat(wms):增加入库列表的评审 2026-05-12 23:30:06 +08:00
YunaiV c865dfe488 feat(wms):进一步优化入库单的后端实现(对齐 mes) 2026-05-12 23:02:46 +08:00
HmEJ 680c0e0e8b feat: 使流程图居中显示 2026-05-12 16:50:13 +08:00
YunaiV 0be2674277 feat(wms):增加 inv 库存的新增、修改方法,并提供相关单测 2026-05-12 11:14:21 +08:00
YunaiV f0cd639137 feat(wms):优化 inventory history 2026-05-11 15:11:39 +08:00
YunaiV 314293ced3 feat(wms):优化 inventory 的实现 2026-05-11 14:10:16 +08:00
YunaiV 32bbd912a2 feat(wms):增加 inventory history 2026-05-11 13:07:35 +08:00
YunaiV 32442830b0 feat(wms):增加 inventory 2026-05-11 09:45:24 +08:00
YunaiV 711d5abc0a feat(wms):完善往来企业 2026-05-10 23:56:28 +08:00
YunaiV 4da16e95f5 feat(wms):完善商品信息、SKU 信息 2026-05-10 22:46:23 +08:00
YunaiV d890781149 feat(wms):增加商品信息、SKU 信息 2026-05-10 21:33:42 +08:00
YunaiV 30e4fef7bb feat(wms):增加商品分类、商品品牌。 2026-05-10 16:38:25 +08:00
YunaiV 8105cc786a (〃'▽'〃)_v2026_04_发布:新增代码生成器 Excel 导入,增强 IoT 场景联动与数据流转 2026-05-10 10:59:52 +08:00
YunaiV eb8002ce0b chore: make build scripts cross-platform
- use cross-env for NODE_OPTIONS in build commands
- align local/dev/test/stage/prod build scripts with Vite modes
- add cross-env to pnpm lockfile
2026-05-10 10:01:08 +08:00
YunaiV 1aad2f1648 feat(wms):迁移到 md 更整体 2026-05-10 09:02:51 +08:00
YunaiV c3737d3b7a feat(wms):增加 warehouse 功能 2026-05-10 01:22:38 +08:00
DevDengChao 81711a98c9 docs: 删除依赖升级备注 2026-05-06 16:55:01 +08:00
DevDengChao 766b3906fa docs: note sequential build verification
Co-authored-by: OpenAI <support@openai.com>
2026-05-06 16:30:35 +08:00
DevDengChao cd63cf2b34 chore: remove e2e tests and playwright
Co-authored-by: OpenAI <support@openai.com>
2026-05-06 16:24:25 +08:00
DevDengChao 84ae85f545 chore: upgrade runtime dependencies
Co-authored-by: OpenAI <support@openai.com>
2026-05-06 16:13:36 +08:00
DevDengChao 78b6679e63 chore: upgrade dev tooling dependencies
Co-authored-by: OpenAI <support@openai.com>
2026-05-06 16:07:04 +08:00
DevDengChao ee5ed1f97b merge: upstream master into upgrade
Co-authored-by: OpenAI <support@openai.com>
2026-05-06 16:00:18 +08:00
芋道源码 9d8d0647be
!877 feat(system): 优化用户选择 UserSelectV2 布局,多选支持、默认选中当前用户支持、禁选支持、默认部门支持,可替代…
Merge pull request !877 from 半栈幼儿员/hotfix/user
2026-05-05 10:10:47 +00:00
preschooler 536e54062e feat(system): 优化用户选择 UserSelectV2 布局,多选支持、默认选中当前用户支持、禁选支持、默认部门支持,可替代项目所有位置,可移除原 UserSelectForm、UserSelect,避免一次性查询所有用户 2026-05-05 12:35:31 +08:00
芋道源码 5e937d797d
!851 feat:增加说明文案
Merge pull request !851 from steven/feat-自定义海报代码补充开发
2026-05-03 10:59:47 +00:00
芋道源码 3a1f520dc6
!876 回退 'Pull Request !870 : fix: 菜单名称过长时没有正确显示省略号'
Merge pull request !876 from 芋道源码/revert-merge-870-master
2026-05-03 10:55:53 +00:00
芋道源码 5e6b6bdd8e
回退 'Pull Request !870 : fix: 菜单名称过长时没有正确显示省略号' 2026-05-03 10:55:35 +00:00
YunaiV 53b96f87a0 Merge remote-tracking branch 'origin/feat/mes' into feat/mes 2026-05-03 18:48:25 +08:00
YunaiV b7a13a0000 ♻️ refactor(service): 优化请求拦截器中的 token 设置逻辑,简化白名单判断 2026-05-03 18:48:12 +08:00
YunaiV 6d5705b655 fix(bpm):修正流程实例审批弹窗网关分支重算的并发与提交问题
- 提交时不再用节点表单值覆盖 data.variables;与预览阶段使用同一份合并变量
- onChange 加 useDebounceFn(300ms) + 请求序号去重,handleAudit 提交前 await 最新一轮重算
- 切换任务时重置请求序号与 pending 重算
- 改用 form-create 官方 formData() 取节点表单当前值
- 双 nextTick 改为 until 等 fApi 就绪,1s 兜底超时
2026-05-03 18:48:12 +08:00
YunaiV 8571a27a15 fix: 【framework】关闭 TagsView 标签后 keep-alive 缓存未收缩,导致 DOM/JS heap 不回收
delView/delAllViews 误用 delCachedView,关闭非当前标签时会去删 currentRoute
对应的缓存,把要关的 name 留在 cachedViews 里,keep-alive include 不收缩,
旧组件实例无法 unmount。

回退到基于剩余 visitedViews 重建 cachedViews 的实现(对应 5718c7881 之前的写法);
delCachedView 自身保留 issue #180 的修复,仍供 refreshPage 使用。
2026-05-03 18:48:12 +08:00
YunaiV cdcd200c7d 【修复】form-create 单图上传规则 disabled 字段标题与默认值错配 2026-05-03 18:48:12 +08:00
YunaiV 6232330c81 【修复】IoT 场景联动:事件触发器比较值改普通文本输入,允许留空(事件发生即匹配) 2026-05-03 18:48:12 +08:00
YunaiV 418f0c4f52 🐛 fix(system):修复租户 get-by-website 接口不支持端口的问题
🐛 fix(mes):修复常见缺陷的「检测项类型」错用独立字典的问题

「常见缺陷」与「检测项设置」的「检测项类型」语义一致,应共用同一份字典;DefectForm 与列表页统一改为 MES_INDICATOR_TYPE,并清理未使用的 MES_DEFECT_TYPE 常量。
2026-05-03 18:48:12 +08:00
YunaiV 5bce60fd29 ♻️ refactor(service): 优化请求拦截器中的 token 设置逻辑,简化白名单判断 2026-05-03 18:48:04 +08:00
YunaiV beddbe7785 Merge remote-tracking branch 'origin/feat/mes' into feat/mes 2026-05-03 18:39:16 +08:00
芋道源码 2e65691737
!870 fix: 菜单名称过长时没有正确显示省略号
Merge pull request !870 from 李家辉/fix-text-overflow
2026-05-03 10:38:57 +00:00
YunaiV 95cecc8870 fix(bpm):修正流程实例审批弹窗网关分支重算的并发与提交问题
- 提交时不再用节点表单值覆盖 data.variables;与预览阶段使用同一份合并变量
- onChange 加 useDebounceFn(300ms) + 请求序号去重,handleAudit 提交前 await 最新一轮重算
- 切换任务时重置请求序号与 pending 重算
- 改用 form-create 官方 formData() 取节点表单当前值
- 双 nextTick 改为 until 等 fApi 就绪,1s 兜底超时
2026-05-03 18:38:18 +08:00
YunaiV 7a4300116e fix: 【framework】关闭 TagsView 标签后 keep-alive 缓存未收缩,导致 DOM/JS heap 不回收
delView/delAllViews 误用 delCachedView,关闭非当前标签时会去删 currentRoute
对应的缓存,把要关的 name 留在 cachedViews 里,keep-alive include 不收缩,
旧组件实例无法 unmount。

回退到基于剩余 visitedViews 重建 cachedViews 的实现(对应 5718c7881 之前的写法);
delCachedView 自身保留 issue #180 的修复,仍供 refreshPage 使用。
2026-05-03 18:38:18 +08:00
YunaiV 51542e336b 【修复】form-create 单图上传规则 disabled 字段标题与默认值错配 2026-05-03 18:38:18 +08:00
YunaiV 7f33206057 【修复】IoT 场景联动:事件触发器比较值改普通文本输入,允许留空(事件发生即匹配) 2026-05-03 18:38:18 +08:00
YunaiV 8e1430c1a4 🐛 fix(system):修复租户 get-by-website 接口不支持端口的问题
🐛 fix(mes):修复常见缺陷的「检测项类型」错用独立字典的问题

「常见缺陷」与「检测项设置」的「检测项类型」语义一致,应共用同一份字典;DefectForm 与列表页统一改为 MES_INDICATOR_TYPE,并清理未使用的 MES_DEFECT_TYPE 常量。
2026-05-03 18:38:18 +08:00
芋道源码 aafe5f12bc
!866 fix: 修复请求拦截器bug
Merge pull request !866 from funcong/fc-fix-bug
2026-05-03 10:37:47 +00:00
YunaiV 9df6828255 fix(bpm):修正流程实例审批弹窗网关分支重算的并发与提交问题
- 提交时不再用节点表单值覆盖 data.variables;与预览阶段使用同一份合并变量
- onChange 加 useDebounceFn(300ms) + 请求序号去重,handleAudit 提交前 await 最新一轮重算
- 切换任务时重置请求序号与 pending 重算
- 改用 form-create 官方 formData() 取节点表单当前值
- 双 nextTick 改为 until 等 fApi 就绪,1s 兜底超时
2026-05-03 16:34:55 +08:00
YunaiV 06e2ca3100 Merge remote-tracking branch 'origin/feat/mes' into feat/mes 2026-05-03 13:33:23 +08:00
YunaiV fa9facfa0b fix: 【framework】关闭 TagsView 标签后 keep-alive 缓存未收缩,导致 DOM/JS heap 不回收
delView/delAllViews 误用 delCachedView,关闭非当前标签时会去删 currentRoute
对应的缓存,把要关的 name 留在 cachedViews 里,keep-alive include 不收缩,
旧组件实例无法 unmount。

回退到基于剩余 visitedViews 重建 cachedViews 的实现(对应 5718c7881 之前的写法);
delCachedView 自身保留 issue #180 的修复,仍供 refreshPage 使用。
2026-05-03 13:32:43 +08:00
YunaiV 0cc2bff0f4 【修复】form-create 单图上传规则 disabled 字段标题与默认值错配 2026-05-03 13:32:43 +08:00
YunaiV 192a118823 【修复】IoT 场景联动:事件触发器比较值改普通文本输入,允许留空(事件发生即匹配) 2026-05-03 13:32:43 +08:00
YunaiV d2e82b710b 🐛 fix(system):修复租户 get-by-website 接口不支持端口的问题
🐛 fix(mes):修复常见缺陷的「检测项类型」错用独立字典的问题

「常见缺陷」与「检测项设置」的「检测项类型」语义一致,应共用同一份字典;DefectForm 与列表页统一改为 MES_INDICATOR_TYPE,并清理未使用的 MES_DEFECT_TYPE 常量。
2026-05-03 13:32:43 +08:00
芋道源码 583b409fad
!864 条件节点添加包含和不包含操作符
Merge pull request !864 from Lesan/bugfix/bpm-202602
2026-05-03 03:01:48 +00:00
YunaiV 5d0755eea9 Merge remote-tracking branch 'origin/feat/mes' into feat/mes 2026-05-03 09:20:21 +08:00
芋道源码 a698cb1635
!867 feat(iot): 前端新增 Database 数据目的配置表单
Merge pull request !867 from puhui999/master
2026-05-03 01:19:11 +00:00
YunaiV a704620f84 fix: 【framework】关闭 TagsView 标签后 keep-alive 缓存未收缩,导致 DOM/JS heap 不回收
delView/delAllViews 误用 delCachedView,关闭非当前标签时会去删 currentRoute
对应的缓存,把要关的 name 留在 cachedViews 里,keep-alive include 不收缩,
旧组件实例无法 unmount。

回退到基于剩余 visitedViews 重建 cachedViews 的实现(对应 5718c7881 之前的写法);
delCachedView 自身保留 issue #180 的修复,仍供 refreshPage 使用。
2026-05-03 00:28:27 +08:00
芋道源码 7fd0a24ca5
!875 fix(bpm):修复流程网关分支问题
Merge pull request !875 from 郭某人/master
2026-05-02 16:01:00 +00:00
YunaiV e98d575b3a 【修复】form-create 单图上传规则 disabled 字段标题与默认值错配 2026-05-02 22:56:56 +08:00
YunaiV d5a9e2e313 【修复】IoT 场景联动:事件触发器比较值改普通文本输入,允许留空(事件发生即匹配) 2026-05-02 14:32:42 +08:00
芋道源码 f9c0cace70
!871 fix: 修复响应拦截器 code=0 的逻辑错误
Merge pull request !871 from zhulh/fix/code-0-bug
2026-05-02 03:21:51 +00:00
YunaiV 2c3842582f 🐛 fix(system):修复租户 get-by-website 接口不支持端口的问题
🐛 fix(mes):修复常见缺陷的「检测项类型」错用独立字典的问题

「常见缺陷」与「检测项设置」的「检测项类型」语义一致,应共用同一份字典;DefectForm 与列表页统一改为 MES_INDICATOR_TYPE,并清理未使用的 MES_DEFECT_TYPE 常量。
2026-05-02 00:35:16 +08:00
guoanhao 11495a64f5 fix(bpm):修复流程网关分支问题 2026-04-22 17:37:30 +08:00
DevDengChao 14edd68d77 test: harden e2e auth setup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 16:23:50 +08:00
DevDengChao 968a1ccb40 chore: upgrade typescript to 6.0.2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 14:54:46 +08:00
DevDengChao c163ed152c chore: upgrade unplugin-vue-components
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 14:46:30 +08:00
DevDengChao 2499d59f28 chore: upgrade console runtime dependencies
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 14:38:29 +08:00
DevDengChao 176cddc21f chore: upgrade console dev dependencies
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-10 14:33:43 +08:00
zhulianghu 74128f53a5 fix: 修复响应拦截器 code=0 的逻辑错误 2026-04-01 09:53:32 +08:00
lijiahui 491e09c136 fix: 菜单名称过长时没有正确显示省略号 2026-03-30 15:31:51 +08:00
DevDengChao 5959539a03 docs: add Vite 8 upgrade and validation notes
- document Vite 8 config migration notes
- record effective upgrade validation commands and existing baseline issues

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-03-23 11:10:23 +08:00
DevDengChao 26c7544829 chore: upgrade vite to 8.0.1 and @vitejs/plugin-legacy to 8.0.0
- vite
- @vitejs/plugin-legacy
- adapt vite.config.ts for Vite 8 code splitting and Lightning CSS recovery

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-03-23 11:08:26 +08:00
DevDengChao c792f5fa0f chore: upgrade stylelint-order to 8.1.1
- stylelint-order

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-03-23 11:00:59 +08:00
DevDengChao f497bf8e23 chore: upgrade wangEditor and BPMN packages
- @wangeditor-next/editor
- @wangeditor-next/plugin-mention
- bpmn-js
- diagram-js
- bpmn-js-token-simulation

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-03-23 10:58:02 +08:00
DevDengChao eade6bd9a4 chore: upgrade eslint, stylelint, typescript-eslint and unocss
- eslint
- stylelint
- typescript-eslint
- unocss
- @unocss/eslint-config
- @unocss/eslint-plugin
- @unocss/transformer-variant-group

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-03-23 10:53:25 +08:00
DevDengChao 91c97d7302 chore: upgrade @vitejs/plugin-vue*, rollup, sass, vue-tsc and Vite helpers
- @vitejs/plugin-vue
- @vitejs/plugin-vue-jsx
- rollup
- sass
- vite-plugin-eslint2
- vite-plugin-svg-icons-ng
- vue-tsc

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-03-23 10:48:47 +08:00
DevDengChao 27b3c36976 chore: upgrade vue, vue-router, element-plus, vue-i18n and dayjs
- vue
- vue-router
- element-plus
- vue-i18n
- dayjs

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-03-23 10:43:21 +08:00
DevDengChao bfcce06577 chore: upgrade @commitlint/*, @types/*, lint-staged, terser and @iconify/json
- @commitlint/cli
- @commitlint/config-conventional
- @iconify/json
- @types/node
- @types/qs
- lint-staged
- terser

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-03-23 10:39:12 +08:00
puhui999 dfee5b999d style(iot): 优化 Database 数据目的的建表提示UX
- 表名输入框右侧附加「查看/收起表结构提示」按钮
- 引入 el-collapse-transition 结合酷炫终端卡片实现平滑的折叠动画
- 修正 Vue template 中由于缺少闭合 div 导致的语法编译错误
2026-03-13 12:46:57 +08:00
puhui999 9f19835a80 feat(iot): Database 表单增加建表 SQL 提示和一键复制
- 顶部 el-alert 友好提示用户需要先创建表
- 内嵌 SQL 文本框(monospace字体) + 复制按钮
- tableName 默认值设为 iot_device_message_sink
2026-03-13 12:30:04 +08:00
puhui999 ad376b24b4 feat(iot): 前端新增 Database 数据目的配置表单
1. DatabaseConfigForm.vue: 新增 Database 配置表单(JDBC地址/用户名/密码/目标表名)
2. config/index.ts: 导出 DatabaseConfigForm 组件
3. DataSinkForm.vue: 引入 DatabaseConfigForm 条件渲染和校验规则
4. api/sink/index.ts: 添加 DatabaseConfig 接口和联合类型
2026-03-13 12:27:09 +08:00
funcong 8cffb4a8ca fix: 修复请求拦截器bug 2026-03-10 18:48:03 +08:00
DevDengChao a997f25f98 Merge remote-tracking branch 'upstream/master' into upgrade 2026-03-09 09:57:30 +08:00
DevDengChao 52e538aa43 fix(router): auto-reload on chunk load failure after rebuild
Add two layers of error handling for stale chunk imports:
- `vite:preloadError` listener in main.ts for Vite preload failures
- `router.onError` in router/index.ts for dynamic import failures during navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 15:31:02 +08:00
DevDengChao c4908548a1 feat(cropper): upgrade cropperjs from v1 to v2
cropperjs v2 is a complete rewrite using Web Components architecture.

- Cropper.vue: rewrite to use v2 API
  - `new Cropper(img, { container })` with Web Components template
  - `selection.$toCanvas()` (async) replaces `cropper.getCroppedCanvas()`
  - Selection `change` event replaces `crop`/`cropmove` callbacks
  - CropperImage `load` event replaces `ready` callback
- CopperModal.vue: update toolbar handlers
  - `cropperImage.$rotate()` replaces `cropper.rotate()`
  - `cropperImage.$zoom()` replaces `cropper.zoom()`
  - `cropperImage.$scale()` replaces `cropper.scaleX/Y()`
  - `cropperImage.$resetTransform()` + `selection.$reset()` replaces `cropper.reset()`
- types.ts: replace `Cropper.Data` with inline type (v2 has no Data type)
- Remove v1 CSS import (v2 uses shadow DOM styles)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:37:34 +08:00
DevDengChao 2520de56b4 refactor(icon): replace deprecated @iconify/iconify with @iconify/vue
- Remove @iconify/iconify (deprecated), @purge-icons/generated, vite-plugin-purge-icons
- Add @iconify/vue which uses @iconify/utils iconToSVG internally
- Rewrite Icon.vue to use @iconify/vue Icon component instead of manual DOM manipulation
- Pre-load ep/fa/fa-solid icon sets via addCollection for offline support
- Other icon sets (ion, mdi, heroicons, etc.) load from Iconify API on demand
- Remove PurgeIcons() from Vite plugin config
- Verified: all 22 icons on login page render correctly as SVGs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 12:26:12 +08:00
DevDengChao 860d2c0b29 chore(deps): minor updates for bpmn-js and purge-icons
- bpmn-js 18.12.0 → 18.13.0
- bpmn-js-properties-panel 5.52.1 → 5.53.0
- @purge-icons/generated 0.9.0 → 0.10.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 11:22:25 +08:00
DevDengChao 53a1024c11 chore(deps): patch updates for UnoCSS ecosystem (66.6.x)
- unocss 66.6.2 → 66.6.5
- @unocss/eslint-config 66.6.3 → 66.6.5
- @unocss/eslint-plugin 66.6.3 → 66.6.5
- @unocss/transformer-variant-group 66.6.3 → 66.6.5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 11:19:42 +08:00
DevDengChao 037b465a64 chore(deps): patch updates for toolchain (commitlint, lint-staged, postcss, iconify/json)
- @commitlint/cli 20.4.2 → 20.4.3
- @commitlint/config-conventional 20.4.2 → 20.4.3
- lint-staged 16.3.1 → 16.3.2
- postcss 8.5.6 → 8.5.8
- @iconify/json 2.2.444 → 2.2.446

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 11:17:06 +08:00
DevDengChao ac6097aa9a fix: resolve ESLint 10 + eslint-plugin-vue 10 rule compat for dev server
Disable rules that are new/stricter in eslint-plugin-vue 10 and
typescript-eslint 8+ which would block dev server rendering via
vite-plugin-eslint2:
- vue/no-ref-as-operand, vue/no-mutating-props,
  vue/no-side-effects-in-computed-properties
- @typescript-eslint/no-unused-expressions, no-unsafe-function-type,
  no-wrapper-object-types, no-this-alias, no-empty-object-type
- Ignore auto-generated src/types/auto-components.d.ts
- Fix hasPermi.ts short-circuit expression

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 01:00:29 +08:00
DevDengChao cb5f0fb3f0 fix(deps): restore @iconify/iconify required by @purge-icons/generated
Re-added @iconify/iconify as runtime dependency — it's imported by
@purge-icons/generated which is used in src/plugins/svgIcon/index.ts.
Also reverted @purge-icons/generated 0.10 back to 0.9 (both versions
have the same @iconify/iconify import).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 20:56:06 +08:00
DevDengChao 49f0fb06f4 chore(deps): BPMN ecosystem upgrade and cleanup (Phase D)
- diagram-js 12 → 15, min-dash 4 → 5
- Remove unused fast-xml-parser (replaced by steady-xml)
- @purge-icons/generated 0.9 → 0.10

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 20:50:46 +08:00
DevDengChao c0845eae75 chore(deps): medium-risk major upgrades (Phase C)
- stylelint 16 → 17, config-recommended 14 → 18, config-standard 36 → 40, order 6 → 7
- vue-types 5 → 6
- video.js 7 → 8
- cropperjs v2 evaluated but reverted to v1 due to incompatible API rewrite

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 20:46:12 +08:00
DevDengChao e31423bc6d chore(deps): low-risk major upgrades (Phase B)
- Remove deprecated @iconify/iconify (replaced by @purge-icons/generated)
- @commitlint/cli + config-conventional 19 → 20
- lint-staged 15 → 16
- rimraf 5 → 6
- markmap-common/lib/toolbar/view 0.16-0.17 → 0.18
- vue3-signature 0.2 → 0.4
- vue-dompurify-html 4 → 5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 20:35:24 +08:00
DevDengChao 5284b00706 chore(deps): update all semver-compatible packages to latest
Bump ~23 packages within their declared semver ranges, including:
- vue 3.5.26 → 3.5.29
- dayjs, lodash-es, qs, highlight.js, jsencrypt
- prettier, postcss, rollup, terser, autoprefixer
- and others

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 20:33:08 +08:00
DevDengChao 550c30eae4 chore: post-upgrade cleanup
- Update Node.js engine requirement to >= 20.19.0 (Vite 7 requirement)
- Remove duplicate entries in optimizeDeps include list
- Remove build-time-only packages (sass, unocss) from optimizeDeps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 20:12:44 +08:00
DevDengChao fd11e07e92 chore(deps): upgrade Vite 5.1.4 → 7.3.1 and ecosystem plugins
Core:
- vite 5.1.4 → 7.3.1
- @vitejs/plugin-vue 5 → 6.0.4
- @vitejs/plugin-vue-jsx 3 → 5.1.4
- @vitejs/plugin-legacy 5 → 7.2.1
- @types/node 20.17.9 → 25.3.3

Plugins:
- unplugin-auto-import 0.16.7 → 21.0.0
- unplugin-vue-components 0.25.2 → 31.0.0
- unplugin-element-plus 0.8.0 → 0.11.2
- vite-plugin-svg-icons-ng 1.3.1 → 1.5.2
- vite-plugin-top-level-await 1.4.4 → 1.6.0

Config:
- Switch to Sass Modern Compiler API (api: 'modern-compiler')
- Remove silenceDeprecations workaround

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:49:57 +08:00
DevDengChao f4b8fea579 chore(deps): upgrade ECharts 5.5.0 → 6.0.0
Note: echarts-wordcloud has unmet peer dep (expects echarts ^5)
but works at runtime. Monitor for official v6 support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:46:13 +08:00
DevDengChao 6ffcbbc1d2 chore(deps): migrate ESLint 8 → 10 with flat config
- Upgrade eslint 8.57.1 → 10.0.2
- Migrate .eslintrc.js → eslint.config.mjs (flat config)
- Replace vite-plugin-eslint → vite-plugin-eslint2
- Replace @typescript-eslint/eslint-plugin + parser → typescript-eslint
- Upgrade eslint-plugin-vue 9 → 10, vue-eslint-parser 9 → 10
- Remove eslint-define-config, eslint-config-prettier, eslint-plugin-prettier
- Delete .eslintignore (now handled in flat config ignores)
- Remove deprecated rules: vue/script-setup-uses-vars, vue/no-setup-props-destructure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:44:40 +08:00
DevDengChao 53f779afa2 chore(deps): upgrade UnoCSS 0.58.5 → 66.6.2 and related packages
- unocss 0.58.9 → 66.6.2
- @unocss/eslint-config 0.57.7 → 66.6.3
- @unocss/eslint-plugin 66.1.0-beta.5 → 66.6.3
- @unocss/transformer-variant-group 0.58.9 → 66.6.3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:38:32 +08:00
DevDengChao 643eb92aff chore(deps): upgrade Vue I18n 9.10.2 → 11.2.8, fix related compat issues
- Upgrade vue-i18n to 11.2.8, @intlify/unplugin-vue-i18n to 11.0.7
- Remove vue-i18n CJS alias from vite.config.ts (no longer needed)
- Remove vue-i18n from optimizeDeps include list
- Upgrade bpmn-js-token-simulation 0.36.2 → 0.39.2 (ids@3 compat)
- Fix duplicate route name 'Redirect' (Vue Router 5 enforces uniqueness)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:36:31 +08:00
DevDengChao 7991028e0a chore(deps): upgrade @vueuse/core 10.9.0 → 14.2.1
Crosses 4 major versions. All composables used by the project remain
compatible. All 24 E2E tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:16:43 +08:00
DevDengChao c0414a563f chore(deps): upgrade Vue Router 4.4.5 → 5.0.3
Major version with no breaking changes for this codebase. Dynamic route
generation via router.addRoute() and navigation guards work correctly.
All 24 E2E tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:14:18 +08:00
DevDengChao 7326634cd3 chore(deps): upgrade Pinia 2.1.7 → 3.0.4, persistedstate 3.2.1 → 4.7.1
Pinia 3 is a "boring major" removing only deprecated APIs. The
persistedstate plugin v4 default export remains compatible.
All 24 E2E tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:12:27 +08:00
DevDengChao 81f00adae3 chore(deps): upgrade TypeScript 5.3.3 → 5.9.3
Multiple minor versions with improved type inference. All 24 E2E tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:10:28 +08:00
DevDengChao 136ef314b5 chore(deps): upgrade bpmn-js-properties-panel 5.23.0 → 5.52.1
bpmn-js was already at v18. Properties panel minor update.
All 24 E2E tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:06:04 +08:00
DevDengChao c13d5d7dfd chore(deps): upgrade vue-tsc 1.8.27 → 3.2.5
Major version bump with stricter type checking. Pre-existing type errors
in source code are surfaced but do not affect runtime. All 24 E2E tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:03:39 +08:00
DevDengChao b6f40353ad chore(deps): upgrade Sass 1.69.5 → 1.97.3
Patch update. All 24 E2E tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:00:25 +08:00
DevDengChao dec43ffcee chore(deps): upgrade Element Plus 2.11.1 → 2.13.3
Minor update. All 24 E2E tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:58:47 +08:00
DevDengChao 3dad65a53f chore(deps): upgrade Axios 1.9.0 → 1.13.6
Minor/patch update. All 24 E2E tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:56:55 +08:00
DevDengChao 9864cf5a92 chore(deps): upgrade Vue 3.5.12 → 3.5.26
Patch update with no breaking changes. All 24 E2E tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:54:36 +08:00
DevDengChao 1b9fcc51a1 test: add Playwright E2E test suite as regression safety net
Set up 24 E2E tests covering auth, navigation, user CRUD, permissions,
UI features, and smoke tests using Playwright with API mocking via
page.route(). This provides a safety net before proceeding with
dependency upgrades.

- Add playwright.config.ts with setup project + storageState auth
- Add .env.e2e disabling captcha/tenant/encryption for test mode
- Add e2e/ directory with fixtures, helpers, page objects, and tests
- Add test:e2e scripts to package.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 18:49:50 +08:00
LesanOuO 80128c5406 feat: 条件节点添加包含和不包含操作符 2026-02-14 16:53:21 +08:00
8614095 3314dfe365 feat:增加说明文案 2026-01-15 11:13:20 +08:00
264 changed files with 19582 additions and 24331 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.

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/>

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.04-snapshot",
"description": "基于vue3、vite4、element-plus、typesScript",
"author": "xingyu",
"private": false,
@ -8,143 +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": "^1.11.20",
"dayjs-plugin-lunar": "^1.4.1",
"dhtmlx-gantt": "^9.1.1",
"diagram-js": "^12.8.0",
"driver.js": "^1.3.1",
"echarts": "^5.5.0",
"diagram-js": "^15.14.0",
"driver.js": "^1.4.0",
"echarts": "^6.0.0",
"echarts-wordcloud": "^2.1.0",
"element-plus": "2.11.1",
"element-plus": "2.13.7",
"fast-xml-parser": "^4.3.2",
"highlight.js": "^11.9.0",
"highlight.js": "^11.11.1",
"jsbarcode": "^3.12.3",
"jsencrypt": "^3.3.2",
"jsoneditor": "^10.1.3",
"lodash-es": "^4.17.21",
"markdown-it": "^14.1.0",
"markmap-common": "^0.16.0",
"markmap-lib": "^0.16.1",
"markmap-toolbar": "^0.17.0",
"markmap-view": "^0.16.0",
"min-dash": "^4.1.1",
"jsencrypt": "^3.5.4",
"jsoneditor": "^10.4.3",
"lodash-es": "^4.18.1",
"markdown-it": "^14.1.1",
"markmap-common": "^0.18.9",
"markmap-lib": "^0.18.12",
"markmap-toolbar": "^0.18.12",
"markmap-view": "^0.18.12",
"min-dash": "^5.0.0",
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"qrcode": "^1.5.3",
"qs": "^6.12.0",
"snabbdom": "^3.6.2",
"sortablejs": "^1.15.3",
"pinia": "^3.0.4",
"pinia-plugin-persistedstate": "^4.7.1",
"qrcode": "^1.5.4",
"qs": "^6.15.1",
"snabbdom": "^3.6.3",
"sortablejs": "^1.15.7",
"steady-xml": "^0.1.0",
"tyme4ts": "^1.4.6",
"url": "^0.11.3",
"video.js": "^7.21.5",
"vue": "3.5.12",
"vue-dompurify-html": "^4.1.4",
"vue-i18n": "9.10.2",
"vue-router": "4.4.5",
"vue-types": "^5.1.1",
"url": "^0.11.4",
"video.js": "^8.23.8",
"vue": "3.5.34",
"vue-dompurify-html": "^5.3.0",
"vue-i18n": "11.4.0",
"vue-router": "5.0.6",
"vue-types": "^6.0.0",
"vue3-print-nb": "^0.1.4",
"vue3-signature": "^0.2.4",
"vue3-signature": "^0.4.4",
"vuedraggable": "^4.1.0",
"web-storage-cache": "^1.1.1",
"xml-js": "^1.6.11"
},
"devDependencies": {
"@commitlint/cli": "^19.0.1",
"@commitlint/config-conventional": "^19.0.0",
"@iconify/json": "^2.2.187",
"@intlify/unplugin-vue-i18n": "^2.0.0",
"@purge-icons/generated": "^0.9.0",
"@types/jsoneditor": "^9.9.5",
"@commitlint/cli": "^20.5.3",
"@commitlint/config-conventional": "^20.5.3",
"@iconify/json": "^2.2.470",
"@intlify/unplugin-vue-i18n": "^11.1.2",
"@types/jsoneditor": "^9.9.6",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.11.21",
"@types/node": "^25.6.0",
"@types/nprogress": "^0.2.3",
"@types/qrcode": "^1.5.5",
"@types/qs": "^6.9.12",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"@unocss/eslint-config": "^0.57.4",
"@unocss/eslint-plugin": "66.1.0-beta.5",
"@unocss/transformer-variant-group": "^0.58.5",
"@vitejs/plugin-legacy": "^5.3.1",
"@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.17",
"bpmn-js": "^17.9.2",
"bpmn-js-properties-panel": "5.23.0",
"consola": "^3.2.3",
"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": {
@ -157,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

@ -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

@ -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

@ -15,6 +15,7 @@ export interface UserVO {
remark: string
loginDate: Date
createTime: Date
disabled?: boolean
}
// 查询用户管理列表
@ -22,6 +23,11 @@ export const getUserPage = (params: PageParam) => {
return request.get({ url: '/system/user/page', params })
}
// 查询用户管理列表
export const getUserList = (ids: number[]) => {
return request.get({ url: '/system/user/list', params: { ids: ids.join(',') } })
}
// 查询用户详情
export const getUser = (id: number) => {
return request.get({ url: '/system/user/get?id=' + id })

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

@ -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,3 @@ function getRoundedCanvas() {
return canvas
}
</script>
<style lang="scss">
$prefix-cls: #{$namespace}-cropper-image;
.#{$prefix-cls} {
&--circled {
.cropper-view-box,
.cropper-face {
border-radius: 50%;
}
}
}
</style>

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

@ -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

@ -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

@ -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-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-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'
@ -84,16 +88,16 @@ const processInstanceInfos = ref<any[]>([]) // 流程的审批信息
const nodeClick = () => {
if (readonly) {
if(processInstance && processInstance.value){
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
}
// 模式一:前端上传

View File

@ -158,11 +158,13 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗
.el-transfer {
display: flex;
}
.el-transfer__buttons {
display: flex !important;
flex-direction: column-reverse;
justify-content: center;
gap: 20px;
.el-transfer__button:nth-child(2) {
margin: 0;
}

View File

@ -176,6 +176,41 @@ const processReZoom = () => {
bpmnViewer.value?.get('canvas').zoom('fit-viewport', 'auto')
}
let resizeObserver: ResizeObserver | null = null
/** 停止 ResizeObserver */
const stopResizeObserver = () => {
if (resizeObserver) {
resizeObserver.disconnect()
resizeObserver = null
}
}
/** 启动 ResizeObserver 监听容器尺寸变化 */
const startResizeObserver = () => {
stopResizeObserver()
if (!processCanvas.value || !bpmnViewer.value) {
return
}
const { clientWidth, clientHeight } = processCanvas.value
if (clientWidth > 0 && clientHeight > 0) {
processReZoom()
return
}
resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect
if (width > 0 && height > 0 && bpmnViewer.value) {
processReZoom()
stopResizeObserver()
}
}
})
resizeObserver.observe(processCanvas.value)
}
/** Zoom放大 */
const processZoomIn = (zoomStep = 0.1) => {
let newZoom = Math.floor(defaultZoom.value * 100 + zoomStep * 100) / 100
@ -198,6 +233,7 @@ const processZoomOut = (zoomStep = 0.1) => {
/** 流程图预览清空 */
const clearViewer = () => {
stopResizeObserver()
if (processCanvas.value) {
processCanvas.value.innerHTML = ''
}
@ -277,6 +313,12 @@ const importXML = async (xml: string) => {
isLoading.value = false
//
setProcessStatus(props.view)
// ResizeObserver
// https://github.com/yudaocode/yudao-ui-admin-vue3/pull/221
if (bpmnViewer.value) {
await nextTick()
startResizeObserver()
}
}
}
}

View File

@ -12,7 +12,6 @@ import { isFunction, isObject, some } from 'min-dash'
const WILDCARD = '*'
function CamundaModdleExtension(eventBus) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this
eventBus.on('moddleCopy.canCopyProperty', function (context) {

View File

@ -27,9 +27,9 @@
<element-form :id="elementId" :type="elementType" />
</el-collapse-item>
<el-collapse-item name="task" v-if="isTaskCollapseItemShow(elementType)" key="task">
<template #title
><Icon icon="ep:checked" />{{ getTaskCollapseItemName(elementType) }}</template
>
<template #title>
<Icon icon="ep:checked" />{{ getTaskCollapseItemName(elementType) }}
</template>
<element-task :id="elementId" :type="elementType" />
</el-collapse-item>
<el-collapse-item

View File

@ -112,9 +112,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" @change="updateElementExtensions" />
@ -127,15 +127,15 @@
<div class="field-permit-title">
<div class="setting-title-label first-title"> 字段名称 </div>
<div class="other-titles">
<span class="setting-title-label cursor-pointer" @click="updatePermission('READ')"
>只读</span
>
<span class="setting-title-label cursor-pointer" @click="updatePermission('WRITE')"
>可编辑</span
>
<span class="setting-title-label cursor-pointer" @click="updatePermission('NONE')"
>隐藏</span
>
<span class="setting-title-label cursor-pointer" @click="updatePermission('READ')">
只读
</span>
<span class="setting-title-label cursor-pointer" @click="updatePermission('WRITE')">
可编辑
</span>
<span class="setting-title-label cursor-pointer" @click="updatePermission('NONE')">
隐藏
</span>
</div>
</div>
<div class="field-setting-item" v-for="(item, index) in fieldsPermissionEl" :key="index">

View File

@ -11,13 +11,13 @@
/>
<el-table-column label="操作" width="100px">
<template #default="scope">
<el-button size="small" link @click="openListenerForm(scope.row, scope.$index)"
>编辑</el-button
>
<el-button size="small" link @click="openListenerForm(scope.row, scope.$index)">
编辑
</el-button>
<el-divider direction="vertical" />
<el-button size="small" link style="color: #ff4d4f" @click="removeListener(scope.$index)"
>移除</el-button
>
<el-button size="small" link style="color: #ff4d4f" @click="removeListener(scope.$index)">
移除
</el-button>
</template>
</el-table-column>
</el-table>
@ -167,17 +167,18 @@
/>
<el-table-column label="操作" width="130px">
<template #default="scope">
<el-button size="small" link @click="openListenerFieldForm(scope.row, scope.$index)"
>编辑</el-button
>
<el-button size="small" link @click="openListenerFieldForm(scope.row, scope.$index)">
编辑
</el-button>
<el-divider direction="vertical" />
<el-button
size="small"
link
style="color: #ff4d4f"
@click="removeListenerField(scope.$index)"
>移除</el-button
>
移除
</el-button>
</template>
</el-table-column>
</el-table>
@ -419,10 +420,7 @@ const saveListenerConfig = async () => {
bpmnElement.businessObject?.extensionElements?.values?.filter(
(ex) => ex.$type !== `${prefix}:ExecutionListener`
) ?? []
updateElementExtensions(
bpmnElement,
otherExtensionList.value.concat(bpmnElementListeners.value)
)
updateElementExtensions(bpmnElement, otherExtensionList.value.concat(bpmnElementListeners.value))
// 4.
listenerFormModelVisible.value = false
listenerForm.value = {}
@ -448,10 +446,7 @@ const selectProcessListener = (listener) => {
bpmnElement.businessObject?.extensionElements?.values?.filter(
(ex) => ex.$type !== `${prefix}:ExecutionListener`
) ?? []
updateElementExtensions(
bpmnElement,
otherExtensionList.value.concat(bpmnElementListeners.value)
)
updateElementExtensions(bpmnElement, otherExtensionList.value.concat(bpmnElementListeners.value))
}
watch(

View File

@ -17,17 +17,18 @@
/>
<el-table-column label="操作" width="90px">
<template #default="scope">
<el-button size="small" link @click="openListenerForm(scope.row, scope.$index)"
>编辑</el-button
>
<el-button size="small" link @click="openListenerForm(scope.row, scope.$index)">
编辑
</el-button>
<el-divider direction="vertical" />
<el-button
size="small"
link
style="color: #ff4d4f"
@click="removeListener(scope.row, scope.$index)"
>移除</el-button
>
移除
</el-button>
</template>
</el-table-column>
</el-table>
@ -183,9 +184,9 @@
<el-divider />
<p class="listener-filed__title">
<span><Icon icon="ep:menu" />注入字段</span>
<el-button size="small" type="primary" @click="openListenerFieldForm(null)"
>添加字段</el-button
>
<el-button size="small" type="primary" @click="openListenerFieldForm(null)">
添加字段
</el-button>
</p>
<el-table
:data="fieldsListOfListener"
@ -211,17 +212,18 @@
/>
<el-table-column label="操作" width="100px">
<template #default="scope">
<el-button size="small" link @click="openListenerFieldForm(scope.row, scope.$index)"
>编辑</el-button
>
<el-button size="small" link @click="openListenerFieldForm(scope.row, scope.$index)">
编辑
</el-button>
<el-divider direction="vertical" />
<el-button
size="small"
link
style="color: #ff4d4f"
@click="removeListenerField(scope.row, scope.$index)"
>移除</el-button
>
移除
</el-button>
</template>
</el-table-column>
</el-table>
@ -423,10 +425,7 @@ const saveListenerConfig = async () => {
bpmnElement.businessObject?.extensionElements?.values?.filter(
(ex) => ex.$type !== `${prefix}:TaskListener`
) ?? []
updateElementExtensions(
bpmnElement,
otherExtensionList.value.concat(bpmnElementListeners.value)
)
updateElementExtensions(bpmnElement, otherExtensionList.value.concat(bpmnElementListeners.value))
// 4.
listenerFormModelVisible.value = false
listenerForm.value = {}
@ -490,10 +489,7 @@ const selectProcessListener = (listener) => {
bpmnElement.businessObject?.extensionElements?.values?.filter(
(ex) => ex.$type !== `${prefix}:TaskListener`
) ?? []
updateElementExtensions(
bpmnElement,
otherExtensionList.value.concat(bpmnElementListeners.value)
)
updateElementExtensions(bpmnElement, otherExtensionList.value.concat(bpmnElementListeners.value))
}
watch(

View File

@ -27,9 +27,7 @@
</div>
</div>
</el-radio-group>
<div v-else>
除了UserTask以外节点的多实例待实现
</div>
<div v-else> UserTask </div>
<!-- 与Simple设计器配置合并保留以前的代码 -->
<el-form label-width="90px" style="display: none">
<el-form-item label="快捷配置">

View File

@ -141,8 +141,8 @@ watch(
.header-editor {
.header-list {
max-height: 400px;
overflow-y: auto;
margin-bottom: 16px;
overflow-y: auto;
}
.header-item {
@ -156,8 +156,8 @@ watch(
}
.separator {
color: #606266;
font-weight: 500;
color: #606266;
}
.header-value {

View File

@ -23,8 +23,8 @@
<div style="margin-bottom: 8px">
<el-radio-group v-model="cronMode[f.key]" :key="'radio-' + f.key">
<el-radio label="every" :key="'every-' + f.key">{{ f.label }}</el-radio>
<el-radio label="range" :key="'range-' + f.key"
>
<el-radio label="range" :key="'range-' + f.key">
<el-input-number
v-model="cronRange[f.key][0]"
:min="f.min"
@ -42,10 +42,10 @@
style="width: 60px"
:key="'range1-' + f.key"
/>
之间每{{ f.label }}</el-radio
>
<el-radio label="step" :key="'step-' + f.key"
>从第
之间每{{ f.label }}
</el-radio>
<el-radio label="step" :key="'step-' + f.key">
从第
<el-input-number
v-model="cronStep[f.key][0]"
:min="f.min"
@ -63,8 +63,8 @@
style="width: 60px"
:key="'step1-' + f.key"
/>
{{ f.label }}</el-radio
>
{{ f.label }}
</el-radio>
<el-radio label="appoint" :key="'appoint-' + f.key">指定</el-radio>
</el-radio-group>
</div>
@ -74,8 +74,9 @@
v-for="n in f.max + 1"
:label="pad(n - 1)"
:key="'cb-' + f.key + '-' + (n - 1)"
>{{ pad(n - 1) }}</el-checkbox
>
{{ pad(n - 1) }}
</el-checkbox>
</el-checkbox-group>
</div>
</el-tab-pane>
@ -90,73 +91,79 @@
:key="'isoStr'"
/>
</div>
<div style="margin-bottom: 10px"
>循环次数<el-input-number v-model="repeat" :min="1" style="width: 100px" :key="'repeat'"
/></div>
<div style="margin-bottom: 10px"
>日期时间<el-date-picker
<div style="margin-bottom: 10px">
循环次数<el-input-number v-model="repeat" :min="1" style="width: 100px" :key="'repeat'" />
</div>
<div style="margin-bottom: 10px">
日期时间<el-date-picker
v-model="isoDate"
type="datetime"
placeholder="选择日期时间"
style="width: 200px"
:key="'isoDate'"
/></div>
<div style="margin-bottom: 10px"
>当前时长<el-input
/>
</div>
<div style="margin-bottom: 10px">
当前时长<el-input
v-model="isoDuration"
placeholder="如P3DT30M30S"
style="width: 200px"
:key="'isoDuration'"
/></div>
/>
</div>
<div>
<div
><el-button
<div>
<el-button
v-for="s in [5, 10, 30, 50]"
@click="setDuration('S', s)"
:key="'sec-' + s"
>{{ s }}</el-button
>自定义</div
>
<div
><el-button
>
{{ s }}
</el-button>
自定义
</div>
<div>
<el-button
v-for="m in [5, 10, 30, 50]"
@click="setDuration('M', m)"
:key="'min-' + m"
>{{ m }}</el-button
>自定义</div
>
<div
>小时<el-button
>
{{ m }}
</el-button>
自定义
</div>
<div>
小时<el-button
v-for="h in [4, 8, 12, 24]"
@click="setDuration('H', h)"
:key="'hour-' + h"
>{{ h }}</el-button
>自定义</div
>
<div
><el-button
v-for="d in [1, 2, 3, 4]"
@click="setDuration('D', d)"
:key="'day-' + d"
>{{ d }}</el-button
>自定义</div
>
<div
><el-button
>
{{ h }}
</el-button>
自定义
</div>
<div>
<el-button v-for="d in [1, 2, 3, 4]" @click="setDuration('D', d)" :key="'day-' + d">
{{ d }}
</el-button>
自定义
</div>
<div>
<el-button
v-for="mo in [1, 2, 3, 4]"
@click="setDuration('M', mo)"
:key="'mon-' + mo"
>{{ mo }}</el-button
>自定义</div
>
<div
><el-button
v-for="y in [1, 2, 3, 4]"
@click="setDuration('Y', y)"
:key="'year-' + y"
>{{ y }}</el-button
>自定义</div
>
>
{{ mo }}
</el-button>
自定义
</div>
<div>
<el-button v-for="y in [1, 2, 3, 4]" @click="setDuration('Y', y)" :key="'year-' + y">
{{ y }}
</el-button>
自定义
</div>
</div>
</el-tab-pane>
</el-tabs>

View File

@ -1,8 +1,8 @@
<template>
<div>
<div style="margin-bottom: 10px"
>当前选择<el-input v-model="isoString" readonly style="width: 300px"
/></div>
<div style="margin-bottom: 10px">
当前选择<el-input v-model="isoString" readonly style="width: 300px" />
</div>
<div v-for="unit in units" :key="unit.key" style="margin-bottom: 8px">
<span>{{ unit.label }}</span>
<el-button-group>
@ -11,8 +11,9 @@
:key="val"
size="mini"
@click="setUnit(unit.key, val)"
>{{ val }}</el-button
>
{{ val }}
</el-button>
<el-input
v-model.number="custom[unit.key]"
size="mini"

View File

@ -3,22 +3,23 @@
<div style="margin-top: 10px">
<span>类型</span>
<el-button-group>
<el-button size="mini" :type="type === 'time' ? 'primary' : ''" @click="setType('time')"
>时间</el-button
>
<el-button size="mini" :type="type === 'time' ? 'primary' : ''" @click="setType('time')">
时间
</el-button>
<el-button
size="mini"
:type="type === 'duration' ? 'primary' : ''"
@click="setType('duration')"
>持续</el-button
>
<el-button size="mini" :type="type === 'cycle' ? 'primary' : ''" @click="setType('cycle')"
>循环</el-button
>
持续
</el-button>
<el-button size="mini" :type="type === 'cycle' ? 'primary' : ''" @click="setType('cycle')">
循环
</el-button>
</el-button-group>
<el-icon v-if="valid" color="green" style="margin-left: 8px"><CircleCheckFilled /></el-icon>
</div>
<div style="margin-top: 10px; display: flex; align-items: center">
<div style="display: flex; margin-top: 10px; align-items: center">
<span>条件</span>
<el-input
v-model="condition"
@ -33,9 +34,9 @@
<el-icon color="orange"><WarningFilled /></el-icon>
</el-tooltip>
<el-tooltip :content="helpText" placement="top">
<el-icon color="#409EFF" style="cursor: pointer" @click="showHelp = true"
><QuestionFilled
/></el-icon>
<el-icon color="#409EFF" style="cursor: pointer" @click="showHelp = true">
<QuestionFilled />
</el-icon>
</el-tooltip>
<el-button
v-if="type === 'time'"

View File

@ -49,15 +49,13 @@ const service: AxiosInstance = axios.create({
// request拦截器
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
// 是否需要设置 token
let isToken = (config!.headers || {}).isToken === false
whiteList.some((v) => {
if (config.url && config.url.indexOf(v) > -1) {
return (isToken = false)
}
})
if (getAccessToken() && !isToken) {
config.headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
// 是否需要设置 token命中白名单的接口如 /login不带 token
let isToken = (config!.headers || {}).isToken !== false
if (isToken && whiteList.some((v) => config.url?.includes(v))) {
isToken = false
}
if (getAccessToken() && isToken) {
config.headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义 token
}
// 设置租户
if (tenantEnable && tenantEnable === 'true') {
@ -145,7 +143,7 @@ service.interceptors.response.use(
}
data = await new Response(response.data).json()
}
const code = data.code || result_code
const code = data.code ?? result_code
// 获取错误信息
const msg = data.msg || errorCode[code] || errorCode['default']
if (ignoreMsgs.indexOf(msg) !== -1) {
@ -211,7 +209,7 @@ service.interceptors.response.use(
'<div>5 分钟搭建本地环境</div>'
})
return Promise.reject(new Error(msg))
} else if (code !== 200) {
} else if (code !== 0 && code !== 200) {
if (msg === '无效的刷新令牌') {
// hard coding忽略这个提示直接登出
console.log(msg)

View File

@ -2,7 +2,6 @@
import { useAppStore } from '@/store/modules/app'
import { useDesign } from '@/hooks/web/useDesign'
// eslint-disable-next-line vue/no-reserved-component-names
defineOptions({ name: 'Footer' })
const { getPrefixCls } = useDesign()

View File

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

View File

@ -382,10 +382,10 @@ watch(
<Icon
v-if="
tagsViewIcon &&
(item?.meta?.icon ||
(item?.matched &&
item.matched[0] &&
item.matched[item.matched.length - 1].meta?.icon))
(item?.meta?.icon ||
(item?.matched &&
item.matched[0] &&
item.matched[item.matched.length - 1].meta?.icon))
"
:icon="item?.meta?.icon || item.matched[item.matched.length - 1].meta.icon"
:size="12"
@ -393,7 +393,7 @@ watch(
/>
{{
t(item?.meta?.title as string) +
(item?.meta?.titleSuffix ? ` (${item?.meta?.titleSuffix})` : '')
(item?.meta?.titleSuffix ? ` (${item?.meta?.titleSuffix})` : '')
}}
<Icon
:class="`${prefixCls}__item--close`"

View File

@ -47,6 +47,12 @@ import { setupWangEditorPlugin } from '@/views/bpm/model/form/PrintTemplate'
import print from 'vue3-print-nb' // 打印插件
// 处理 Vite 预加载模块失败(如重新构建后 chunk 哈希变化),自动刷新页面
window.addEventListener('vite:preloadError', (event) => {
event.preventDefault()
window.location.reload()
})
// 创建实例
const setupAll = async () => {
const app = createApp(App)

View File

@ -1,3 +1,10 @@
import 'virtual:svg-icons-register'
import '@purge-icons/generated'
import { addCollection } from '@iconify/vue'
import epIcons from '@iconify/json/json/ep.json'
import faIcons from '@iconify/json/json/fa.json'
import faSolidIcons from '@iconify/json/json/fa-solid.json'
addCollection(epIcons)
addCollection(faIcons)
addCollection(faSolidIcons)

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