From bd506fab0d24e565e3893f8ecb1bd089ebb77571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=A3=E5=8F=A3=E5=8F=A3?= <17975121@qq.com> Date: Wed, 20 Aug 2025 03:32:41 +0000 Subject: [PATCH 001/136] =?UTF-8?q?update=20src/store/modules/tagsView.ts.?= =?UTF-8?q?=20=E5=BD=93=E5=AD=98=E5=9C=A8=E5=A4=9A=E4=B8=AA=E7=9B=B8?= =?UTF-8?q?=E5=90=8C=E5=90=8D=E7=A7=B0=E7=9A=84=E6=A0=87=E7=AD=BE=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E5=88=A0=E9=99=A4=E7=BC=93=E5=AD=98=E5=BA=94=E8=AF=A5?= =?UTF-8?q?=E6=8E=92=E9=99=A4=E6=98=AF=E5=90=A6=E5=AD=98=E5=9C=A8=E7=9B=B8?= =?UTF-8?q?=E5=90=8C=E7=9A=84=E5=90=8D=E7=A7=B0=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 口口口 <17975121@qq.com> --- src/store/modules/tagsView.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts index 2b2d817f1..6dd509358 100644 --- a/src/store/modules/tagsView.ts +++ b/src/store/modules/tagsView.ts @@ -93,6 +93,11 @@ export const useTagsViewStore = defineStore('tagsView', { delCachedView() { const route = router.currentRoute.value const index = findIndex(this.getCachedViews, (v) => v === route.name) + for (const v of this.visitedViews) { + if (v.name === route.name) { + return + } + } if (index > -1) { this.cachedViews.delete(this.getCachedViews[index]) } From 24493843065df3c82c31c9d5150b1d81ddcb2e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E8=B6=85=E6=9D=B0?= Date: Thu, 21 Aug 2025 19:05:03 +0800 Subject: [PATCH 002/136] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4=E7=94=BB?= =?UTF-8?q?=E5=B8=83=E7=9A=84=20CSS=20transition=20=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E4=BB=A5=E8=A7=A3=E5=86=B3=E6=8B=96=E6=8B=BD=E5=8D=A1=E9=A1=BF?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SimpleProcessDesignerV2/theme/simple-process-designer.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss b/src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss index d0adbdbb9..33795639f 100644 --- a/src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss +++ b/src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss @@ -212,7 +212,6 @@ transform-origin: 50% 0 0; min-width: fit-content; transform: scale(1); - transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); background: url(@/assets/svgs/bpm/simple-process-bg.svg) 0 0 repeat; // 节点容器 定义节点宽度 .node-container { From 722d6405a172da9fbb493267453354c144e3c0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E9=87=8E=E7=BE=A1=E6=B0=91?= Date: Fri, 5 Sep 2025 03:58:12 +0000 Subject: [PATCH 003/136] =?UTF-8?q?=E9=9B=AA=E8=8A=B1=20ID=20=E6=BA=A2?= =?UTF-8?q?=E5=87=BA=E9=97=AE=E9=A2=98=20update=20src/views/mall/promotion?= =?UTF-8?q?/rewardActivity/components/RewardRuleCouponSelect.vue.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 山野羡民 --- .../rewardActivity/components/RewardRuleCouponSelect.vue | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/views/mall/promotion/rewardActivity/components/RewardRuleCouponSelect.vue b/src/views/mall/promotion/rewardActivity/components/RewardRuleCouponSelect.vue index 504e72275..41b1f2bbb 100644 --- a/src/views/mall/promotion/rewardActivity/components/RewardRuleCouponSelect.vue +++ b/src/views/mall/promotion/rewardActivity/components/RewardRuleCouponSelect.vue @@ -89,9 +89,7 @@ const initGiveCouponList = async () => { if (isEmpty(rewardRule.value) || isEmpty(rewardRule.value.giveCouponTemplateCounts)) { return } - const tempLateIds = Object.keys(rewardRule.value.giveCouponTemplateCounts!).map((item) => - parseInt(item) - ) + const tempLateIds = Object.keys(rewardRule.value.giveCouponTemplateCounts!) const data = await CouponTemplateApi.getCouponTemplateList(tempLateIds) if (!data) { return From a126f42c35c9bd6efcde1761e8551708fd99638f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 2 Oct 2025 09:36:06 +0800 Subject: [PATCH 004/136] =?UTF-8?q?fix=EF=BC=9A=E3=80=90bpm=20=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E3=80=91=E5=B7=B2=E5=8A=9E=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E7=9A=84=E5=AE=A1=E6=89=B9=E7=8A=B6=E6=80=81=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E4=B8=8D=E6=AD=A3=E7=A1=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/bpm/task/done/index.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/bpm/task/done/index.vue b/src/views/bpm/task/done/index.vue index 29c4d7558..202cd0e7d 100644 --- a/src/views/bpm/task/done/index.vue +++ b/src/views/bpm/task/done/index.vue @@ -52,13 +52,13 @@ Date: Thu, 2 Oct 2025 09:41:54 +0800 Subject: [PATCH 005/136] =?UTF-8?q?fix=EF=BC=9A=E5=89=8D=E7=AB=AF=E7=9A=84?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E9=95=BF=E5=BA=A6=E9=99=90=E5=88=B6=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=EF=BC=8Chttps://gitee.com/yudaocode/yudao-ui-admin-vu?= =?UTF-8?q?e3/issues/ICVDAT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/Profile/components/ResetPwd.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/Profile/components/ResetPwd.vue b/src/views/Profile/components/ResetPwd.vue index 477be91f4..d9e0de183 100644 --- a/src/views/Profile/components/ResetPwd.vue +++ b/src/views/Profile/components/ResetPwd.vue @@ -44,11 +44,11 @@ const equalToPassword = (_rule, value, callback) => { const rules = reactive({ oldPassword: [ { required: true, message: t('profile.password.oldPwdMsg'), trigger: 'blur' }, - { min: 6, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur' } + { min: 4, max: 16, message: t('profile.password.pwdRules'), trigger: 'blur' } ], newPassword: [ { required: true, message: t('profile.password.newPwdMsg'), trigger: 'blur' }, - { min: 6, max: 20, message: t('profile.password.pwdRules'), trigger: 'blur' } + { min: 4, max: 16, message: t('profile.password.pwdRules'), trigger: 'blur' } ], confirmPassword: [ { required: true, message: t('profile.password.cfPwdMsg'), trigger: 'blur' }, From 62a9a991469e6db8d61e844f7b895f91c408206b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 2 Oct 2025 10:24:22 +0800 Subject: [PATCH 006/136] =?UTF-8?q?fix=EF=BC=9A=E3=80=90pay=20=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E3=80=91=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98=20public?= =?UTF-8?q?KeyContent=20=E8=B0=83=E6=95=B4=E4=B8=BA=E9=9D=9E=E5=BF=85?= =?UTF-8?q?=E5=A1=AB=EF=BC=8C=E5=85=BC=E5=AE=B9=20https://t.zsxq.com/ODR5V?= =?UTF-8?q?=E3=80=81https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues?= =?UTF-8?q?/ICUE53?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/channel/WeixinChannelForm.vue | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/views/pay/app/components/channel/WeixinChannelForm.vue b/src/views/pay/app/components/channel/WeixinChannelForm.vue index daebb41e0..fd915548e 100644 --- a/src/views/pay/app/components/channel/WeixinChannelForm.vue +++ b/src/views/pay/app/components/channel/WeixinChannelForm.vue @@ -147,7 +147,11 @@ 前往微信商户平台查看证书序列号 - + - + - + 微信支付公钥产品简介及使用说明 @@ -246,7 +243,6 @@ const formRules = { { required: true, message: '请上传 apiclient_key.pem 证书', trigger: 'blur' } ], 'config.certSerialNo': [{ required: true, message: '请输入证书序列号', trigger: 'blur' }], - 'config.publicKeyContent': [{ required: true, message: '请上传 public_key.pem 证书', trigger: 'blur' }], 'config.publicKeyId': [{ required: true, message: '请输入公钥 ID', trigger: 'blur' }], 'config.apiV3Key': [{ required: true, message: '请上传 api V3 密钥值', trigger: 'blur' }] } From bdb42bb927261410af56c39a75a7821a3e2791ec Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 2 Oct 2025 11:25:34 +0800 Subject: [PATCH 007/136] =?UTF-8?q?fix=EF=BC=9A=E3=80=90=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E3=80=91=E4=BA=A7=E5=93=81=E8=B7=B3=E8=BD=AC=E5=88=B0=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E5=88=97=E8=A1=A8=E6=97=B6=EF=BC=8CproductId=20?= =?UTF-8?q?=E6=9C=AA=E4=BC=A0=E9=80=92=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=20https://t.zsxq.com/jvXyq?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/iot/device/device/index.vue | 10 ++++++++-- .../product/product/detail/ProductDetailsHeader.vue | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/views/iot/device/device/index.vue b/src/views/iot/device/device/index.vue index 56139be9a..67ef01cb7 100644 --- a/src/views/iot/device/device/index.vue +++ b/src/views/iot/device/device/index.vue @@ -384,6 +384,7 @@ defineOptions({ name: 'IoTDevice' }) const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 +const route = useRoute() const loading = ref(true) // 列表加载中 const list = ref([]) // 列表的数据 @@ -392,7 +393,7 @@ const queryParams = reactive({ pageNo: 1, pageSize: 10, deviceName: undefined, - productId: undefined, + productId: undefined as number | undefined, deviceType: undefined, nickname: undefined, status: undefined, @@ -513,7 +514,12 @@ const handleImport = () => { /** 初始化 **/ onMounted(async () => { - getList() + // 处理 productId 参数 + const { productId } = route.query + if (productId) { + queryParams.productId = Number(productId) + } + await getList() // 获取产品列表 products.value = await ProductApi.getSimpleProductList() diff --git a/src/views/iot/product/product/detail/ProductDetailsHeader.vue b/src/views/iot/product/product/detail/ProductDetailsHeader.vue index 919006477..3eb999fa4 100644 --- a/src/views/iot/product/product/detail/ProductDetailsHeader.vue +++ b/src/views/iot/product/product/detail/ProductDetailsHeader.vue @@ -72,7 +72,7 @@ const copyToClipboard = async (text: string) => { /** 路由跳转到设备管理 */ const { push } = useRouter() const goToDeviceList = (productId: number) => { - push({ name: 'IoTDevice', params: { productId } }) + push({ name: 'IoTDevice', query: { productId } }) } /** 修改操作 */ From 9bc289cb2a354b3bca138f7ac7ec2987b503bff6 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 6 Oct 2025 20:45:35 +0800 Subject: [PATCH 008/136] =?UTF-8?q?fix=EF=BC=9A=E6=9B=B4=E6=96=B0=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E6=96=87=E6=9C=AC=E4=B8=BA=E2=80=9C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E9=94=80=E5=94=AE=E4=BA=A7=E5=93=81=E2=80=9D=E5=B9=B6=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=94=AF=E4=BB=98=E4=BF=A1=E6=81=AF=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/erp/sale/order/components/SaleOrderItemForm.vue | 2 +- src/views/pay/cashier/index.vue | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/erp/sale/order/components/SaleOrderItemForm.vue b/src/views/erp/sale/order/components/SaleOrderItemForm.vue index 3a579d5b5..817775f56 100644 --- a/src/views/erp/sale/order/components/SaleOrderItemForm.vue +++ b/src/views/erp/sale/order/components/SaleOrderItemForm.vue @@ -132,7 +132,7 @@ - + 添加采购产品 + + 添加销售产品 diff --git a/src/views/mp/freePublish/index.vue b/src/views/mp/freePublish/index.vue index 2ed8ae77e..c5639ec3e 100644 --- a/src/views/mp/freePublish/index.vue +++ b/src/views/mp/freePublish/index.vue @@ -97,7 +97,9 @@ const handleDelete = async (item: any) => { message.success(t('common.delSuccess')) // 刷新列表 await getList() - } catch {} + } catch { + // + } } diff --git a/src/views/mp/template/MsgTemplateLog.vue b/src/views/mp/template/MsgTemplateLog.vue new file mode 100644 index 000000000..e3fd9234c --- /dev/null +++ b/src/views/mp/template/MsgTemplateLog.vue @@ -0,0 +1,214 @@ + + + diff --git a/src/views/mp/template/MsgTemplateLogForm.vue b/src/views/mp/template/MsgTemplateLogForm.vue new file mode 100644 index 000000000..1266806f5 --- /dev/null +++ b/src/views/mp/template/MsgTemplateLogForm.vue @@ -0,0 +1,144 @@ + + \ No newline at end of file diff --git a/src/views/mp/template/MsgTemplateSend.vue b/src/views/mp/template/MsgTemplateSend.vue new file mode 100644 index 000000000..286be80e9 --- /dev/null +++ b/src/views/mp/template/MsgTemplateSend.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/src/views/mp/user/index.vue b/src/views/mp/user/index.vue index 772018e80..431e1c6ba 100644 --- a/src/views/mp/user/index.vue +++ b/src/views/mp/user/index.vue @@ -11,7 +11,7 @@ label-width="68px" > - + - + + @@ -105,20 +106,24 @@ From c099f27f178dfc65c7ab40559edb5476fa72b99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=93=E8=B6=85?= <2325690622@qq.com> Date: Fri, 21 Nov 2025 11:38:47 +0800 Subject: [PATCH 034/136] Add bottom padding to prevent scrollbar from hiding last row --- src/views/mall/product/spu/components/SkuList.vue | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/views/mall/product/spu/components/SkuList.vue b/src/views/mall/product/spu/components/SkuList.vue index 2ffb37d0b..c41da4b2c 100644 --- a/src/views/mall/product/spu/components/SkuList.vue +++ b/src/views/mall/product/spu/components/SkuList.vue @@ -574,3 +574,10 @@ const getSkuTableRef = () => { // 暴露出生成 sku 方法,给添加属性成功时调用 defineExpose({ generateTableData, validateSku, getSkuTableRef }) + From 224ea2977be8fd9af867316a911b1c569283e09d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Nov 2025 09:01:13 +0800 Subject: [PATCH 035/136] =?UTF-8?q?fix=EF=BC=9A=E3=80=90erp=E3=80=91receip?= =?UTF-8?q?t=20=E5=B0=86=20supplierList=20=E4=BF=AE=E5=A4=8D=E4=B8=BA=20cu?= =?UTF-8?q?stomerList?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/erp/finance/receipt/index.vue | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/views/erp/finance/receipt/index.vue b/src/views/erp/finance/receipt/index.vue index b96a33d5c..d754c4608 100644 --- a/src/views/erp/finance/receipt/index.vue +++ b/src/views/erp/finance/receipt/index.vue @@ -33,16 +33,16 @@ class="!w-240px" /> - + ([]) // 供应商列表 +const customerList = ref([]) // 客户列表 const userList = ref([]) // 用户列表 const accountList = ref([]) // 账户列表 @@ -384,8 +384,8 @@ const handleSelectionChange = (rows: FinanceReceiptVO[]) => { /** 初始化 **/ onMounted(async () => { await getList() - // 加载供应商、用户、账户 - supplierList.value = await SupplierApi.getSupplierSimpleList() + // 加载客户、用户、账户 + customerList.value = await CustomerApi.getCustomerSimpleList() userList.value = await UserApi.getSimpleUserList() accountList.value = await AccountApi.getAccountSimpleList() }) From 559a13defd89f58c5a2698befffa55a7e8e58901 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Nov 2025 10:28:28 +0800 Subject: [PATCH 036/136] =?UTF-8?q?fix:=20=E7=AE=80=E5=8C=96=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E7=9A=84=E6=89=8B=E6=9C=BA=E6=A0=A1=E9=AA=8C=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E5=85=BC=E5=AE=B9=E6=9B=B4=E5=A4=9A=E5=8F=B7?= =?UTF-8?q?=E6=AE=B5=EF=BC=8C=E4=BE=8B=E5=A6=82=E8=AF=B4=20190?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/Profile/components/BasicInfo.vue | 2 +- src/views/mall/trade/delivery/pickUpStore/PickUpStoreForm.vue | 2 +- src/views/system/dept/DeptForm.vue | 4 +--- src/views/system/user/UserForm.vue | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/views/Profile/components/BasicInfo.vue b/src/views/Profile/components/BasicInfo.vue index 1492664fc..094df400e 100644 --- a/src/views/Profile/components/BasicInfo.vue +++ b/src/views/Profile/components/BasicInfo.vue @@ -48,7 +48,7 @@ const rules = reactive({ mobile: [ { required: true, message: t('profile.rules.phone'), trigger: 'blur' }, { - pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, + pattern: /^1[3-9]\d{9}$/, message: t('profile.rules.truephone'), trigger: 'blur' } diff --git a/src/views/mall/trade/delivery/pickUpStore/PickUpStoreForm.vue b/src/views/mall/trade/delivery/pickUpStore/PickUpStoreForm.vue index 077ba6720..f026ff6d3 100644 --- a/src/views/mall/trade/delivery/pickUpStore/PickUpStoreForm.vue +++ b/src/views/mall/trade/delivery/pickUpStore/PickUpStoreForm.vue @@ -145,7 +145,7 @@ const formRules = reactive({ logo: [{ required: true, message: '门店 logo 不能为空', trigger: 'blur' }], phone: [ { required: true, message: '门店手机不能为空', trigger: 'blur' }, - { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' } + { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' } ], areaId: [{ required: true, message: '门店所在区域不能为空', trigger: 'blur' }], detailAddress: [{ required: true, message: '门店详细地址不能为空', trigger: 'blur' }], diff --git a/src/views/system/dept/DeptForm.vue b/src/views/system/dept/DeptForm.vue index c759ef38d..d6333f786 100644 --- a/src/views/system/dept/DeptForm.vue +++ b/src/views/system/dept/DeptForm.vue @@ -90,9 +90,7 @@ const formRules = reactive({ name: [{ required: true, message: '部门名称不能为空', trigger: 'blur' }], sort: [{ required: true, message: '显示排序不能为空', trigger: 'blur' }], email: [{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] }], - phone: [ - { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' } - ], + phone: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }], status: [{ required: true, message: '状态不能为空', trigger: 'blur' }] }) const formRef = ref() // 表单 Ref diff --git a/src/views/system/user/UserForm.vue b/src/views/system/user/UserForm.vue index 630688a33..89498e034 100644 --- a/src/views/system/user/UserForm.vue +++ b/src/views/system/user/UserForm.vue @@ -140,7 +140,7 @@ const formRules = reactive({ ], mobile: [ { - pattern: /^(?:(?:\+|00)86)?1(?:3[\d]|4[5-79]|5[0-35-9]|6[5-7]|7[0-8]|8[\d]|9[189])\d{8}$/, + pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' } From 5ebbc756c4509ec277b6c3edc974ec5f2a6305f5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Nov 2025 10:53:29 +0800 Subject: [PATCH 037/136] =?UTF-8?q?fix:=20=E3=80=90mall=E3=80=91=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=95=86=E5=9F=8E=E7=B3=BB=E7=BB=9F=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E4=B8=AD=E8=A1=A8=E5=A4=B4=E6=9C=AA=E6=A0=B9?= =?UTF-8?q?=E6=8D=AE=E8=A1=A8=E4=BD=93=E8=87=AA=E9=80=82=E5=BA=94=E5=AE=BD?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/components/OrderTableColumn.vue | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/src/views/mall/trade/order/components/OrderTableColumn.vue b/src/views/mall/trade/order/components/OrderTableColumn.vue index a2955941e..6e387cf03 100644 --- a/src/views/mall/trade/order/components/OrderTableColumn.vue +++ b/src/views/mall/trade/order/components/OrderTableColumn.vue @@ -40,7 +40,7 @@ From 50c7d4798436bde5a1221677c6bbf2ebcc7056c8 Mon Sep 17 00:00:00 2001 From: sinkingsoul <286447685@qq.com> Date: Fri, 31 Oct 2025 17:07:31 +0800 Subject: [PATCH 053/136] =?UTF-8?q?feat:=20=E5=88=A0=E9=99=A4http=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=BB=84=E4=BB=B6=E5=9B=BE=E6=A0=87=EF=BC=8C=E6=94=B9?= =?UTF-8?q?=E6=88=90=E6=9C=8D=E5=8A=A1=E4=BB=BB=E5=8A=A1=E7=9A=84=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=B1=BB=E5=9E=8B=E4=B8=8B=E6=8B=89=E6=A1=86=E4=B8=AD?= =?UTF-8?q?=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../designer/plugins/palette/CustomPalette.js | 28 ------------------- .../plugins/palette/paletteProvider.js | 28 ------------------- 2 files changed, 56 deletions(-) diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js b/src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js index 8bdbcfca3..126838717 100644 --- a/src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/palette/CustomPalette.js @@ -89,25 +89,6 @@ F.prototype.getPaletteEntries = function () { create.start(event, elementFactory.createParticipantShape()) } - function createHttpServiceTask(event) { - const httpTask = elementFactory.createShape({ - type: 'bpmn:ServiceTask' - }) - - const businessObject = httpTask.businessObject - - if (typeof businessObject.set === 'function') { - businessObject.set('flowable:type', 'http') - } else { - businessObject['flowable:type'] = 'http' - } - - if (!businessObject.name) { - businessObject.name = translate('HTTP Task') - } - - create.start(event, httpTask) - } assign(actions, { 'hand-tool': { @@ -197,15 +178,6 @@ F.prototype.getPaletteEntries = function () { 'bpmn-icon-service', translate('Create Service Task') ), - 'create.http-service-task': { - group: 'activity', - className: 'bpmn-icon-service', - title: translate('Create HTTP Task'), - action: { - dragstart: createHttpServiceTask, - click: createHttpServiceTask - } - }, 'create.data-object': createAction( 'bpmn:DataObjectReference', 'data-object', diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js b/src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js index e9cb2d31a..8a5858887 100644 --- a/src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js +++ b/src/components/bpmnProcessDesigner/package/designer/plugins/palette/paletteProvider.js @@ -96,25 +96,6 @@ PaletteProvider.prototype.getPaletteEntries = function () { create.start(event, elementFactory.createParticipantShape()) } - function createHttpServiceTask(event) { - const httpTask = elementFactory.createShape({ - type: 'bpmn:ServiceTask' - }) - - const businessObject = httpTask.businessObject - - if (typeof businessObject.set === 'function') { - businessObject.set('flowable:type', 'http') - } else { - businessObject['flowable:type'] = 'http' - } - - if (!businessObject.name) { - businessObject.name = translate('HTTP Task') - } - - create.start(event, httpTask) - } assign(actions, { 'hand-tool': { @@ -197,15 +178,6 @@ PaletteProvider.prototype.getPaletteEntries = function () { 'bpmn-icon-service', translate('Create Service Task') ), - 'create.http-service-task': { - group: 'activity', - className: 'bpmn-icon-service', - title: translate('Create HTTP Task'), - action: { - dragstart: createHttpServiceTask, - click: createHttpServiceTask - } - }, 'create.data-object': createAction( 'bpmn:DataObjectReference', 'data-object', From d04a30fe41f0b6da0e79030c3019df5969b9eb46 Mon Sep 17 00:00:00 2001 From: zhanglc Date: Tue, 25 Nov 2025 11:31:47 +0800 Subject: [PATCH 054/136] typo --- .../package/penal/task/task-components/ServiceTask.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue b/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue index cc4d2abff..37573c8c7 100644 --- a/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue +++ b/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue @@ -1,6 +1,6 @@ @@ -94,12 +100,15 @@ const HTTP_FIELD_NAMES = [ 'requestHeaders', 'disallowRedirects', 'ignoreException', + 'saveResponseParameters', + 'resultVariablePrefix', 'saveResponseParametersTransient', 'saveResponseVariableAsJson' ] const HTTP_BOOLEAN_FIELDS = new Set([ 'disallowRedirects', 'ignoreException', + 'saveResponseParameters', 'saveResponseParametersTransient', 'saveResponseVariableAsJson' ]) @@ -115,8 +124,10 @@ const DEFAULT_HTTP_FORM = { requestMethod: 'GET', requestUrl: '', requestHeaders: 'Content-Type: application/json', + resultVariablePrefix: '', disallowRedirects: false, ignoreException: false, + saveResponseParameters: false, saveResponseParametersTransient: false, saveResponseVariableAsJson: false } From c50e0dee8c76d6eea04b0121f1cd69dbf9037300 Mon Sep 17 00:00:00 2001 From: zhanglc Date: Tue, 18 Nov 2025 11:40:58 +0800 Subject: [PATCH 057/136] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DHTTP=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E4=B8=AD=E8=AF=B7=E6=B1=82=E5=A4=B4=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E4=BD=BF=E7=94=A8EL=E8=A1=A8=E8=BE=BE=E5=BC=8F=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/task-components/ServiceTask.vue | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue b/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue index eda9168e3..3c8e37e36 100644 --- a/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue +++ b/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue @@ -139,22 +139,32 @@ const httpInitializing = ref(false) const bpmnInstances = () => (window as any)?.bpmnInstances +// 判断字符串是否包含表达式 +const isExpression = (value: string): boolean => { + if (!value) return false + // 检测 ${...} 或 #{...} 格式的表达式 + return /\${[^}]+}/.test(value) || /#{[^}]+}/.test(value) +} + const collectHttpExtensionInfo = () => { const businessObject = bpmnElement.value?.businessObject const extensionElements = businessObject?.extensionElements const httpFields = new Map() + const httpFieldTypes = new Map() const otherExtensions: any[] = [] extensionElements?.values?.forEach((item: any) => { if (item?.$type === flowableFieldType && HTTP_FIELD_NAMES.includes(item.name)) { const value = item.string ?? item.stringValue ?? item.expression ?? '' + const fieldType = item.expression ? 'expression' : 'string' httpFields.set(item.name, value) + httpFieldTypes.set(item.name, fieldType) } else { otherExtensions.push(item) } }) - return { httpFields, otherExtensions } + return { httpFields, httpFieldTypes, otherExtensions } } const resetHttpDefaults = () => { @@ -168,7 +178,7 @@ const resetHttpDefaults = () => { const resetHttpForm = () => { httpInitializing.value = true const { httpFields } = collectHttpExtensionInfo() - const nextForm: Record = { ...DEFAULT_HTTP_FORM } + const nextForm = { ...DEFAULT_HTTP_FORM } HTTP_FIELD_NAMES.forEach((name) => { const stored = httpFields.get(name) @@ -228,7 +238,11 @@ const updateHttpExtensions = (force = false) => { return } - const { httpFields: existingFields, otherExtensions } = collectHttpExtensionInfo() + const { + httpFields: existingFields, + httpFieldTypes: existingTypes, + otherExtensions + } = collectHttpExtensionInfo() const desiredEntries: [string, string][] = [] HTTP_FIELD_NAMES.forEach((name) => { @@ -246,21 +260,32 @@ const updateHttpExtensions = (force = false) => { desiredEntries.push([name, persisted]) }) - if ( - !force && - desiredEntries.length === existingFields.size && - desiredEntries.every(([name, value]) => existingFields.get(name) === value) - ) { - return + // 检查是否有变化:不仅比较值,还要比较字段类型(string vs expression) + if (!force && desiredEntries.length === existingFields.size) { + let noChange = true + for (const [name, value] of desiredEntries) { + const existingValue = existingFields.get(name) + const existingType = existingTypes.get(name) + const currentType = isExpression(value) ? 'expression' : 'string' + if (existingValue !== value || existingType !== currentType) { + noChange = false + break + } + } + if (noChange) { + return + } } const moddle = bpmnInstances().moddle - const httpFieldElements = desiredEntries.map(([name, value]) => - moddle.create(flowableFieldType, { + const httpFieldElements = desiredEntries.map(([name, value]) => { + // 根据值是否包含表达式来决定使用 string 还是 expression 属性 + const isExpr = isExpression(value) + return moddle.create(flowableFieldType, { name, - string: value + ...(isExpr ? { expression: value } : { string: value }) }) - ) + }) updateElementExtensions(bpmnElement.value, [...otherExtensions, ...httpFieldElements]) } From d66cd5d209c52ab565b53cd3806028a1a2bd7b24 Mon Sep 17 00:00:00 2001 From: zhanglc Date: Tue, 18 Nov 2025 14:52:42 +0800 Subject: [PATCH 058/136] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96HTTP=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=AF=B7=E6=B1=82=E5=A4=B4=E8=BE=93=E5=85=A5=EF=BC=8C?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E9=94=AE=E5=80=BC=E5=AF=B9=E5=8D=95=E7=8B=AC?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E6=A1=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/task-components/HttpHeaderEditor.vue | 178 ++++++++++++++++++ .../task/task-components/ServiceTask.vue | 55 ++++-- 2 files changed, 219 insertions(+), 14 deletions(-) create mode 100644 src/components/bpmnProcessDesigner/package/penal/task/task-components/HttpHeaderEditor.vue diff --git a/src/components/bpmnProcessDesigner/package/penal/task/task-components/HttpHeaderEditor.vue b/src/components/bpmnProcessDesigner/package/penal/task/task-components/HttpHeaderEditor.vue new file mode 100644 index 000000000..5d0a13349 --- /dev/null +++ b/src/components/bpmnProcessDesigner/package/penal/task/task-components/HttpHeaderEditor.vue @@ -0,0 +1,178 @@ + + + + + diff --git a/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue b/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue index 3c8e37e36..642c3c288 100644 --- a/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue +++ b/src/components/bpmnProcessDesigner/package/penal/task/task-components/ServiceTask.vue @@ -43,22 +43,34 @@ - - - + + + - +
+ + + 编辑 + +
@@ -66,24 +78,34 @@ - + - + - + - + + + + diff --git a/src/views/mp/messageTemplate/index.vue b/src/views/mp/messageTemplate/index.vue new file mode 100644 index 000000000..1bac3f7e7 --- /dev/null +++ b/src/views/mp/messageTemplate/index.vue @@ -0,0 +1,142 @@ + + + diff --git a/src/views/mp/template/MsgTemplate.vue b/src/views/mp/template/MsgTemplate.vue deleted file mode 100644 index da6447599..000000000 --- a/src/views/mp/template/MsgTemplate.vue +++ /dev/null @@ -1,333 +0,0 @@ - - - diff --git a/src/views/mp/template/MsgTemplateForm.vue b/src/views/mp/template/MsgTemplateForm.vue deleted file mode 100644 index 77d31830a..000000000 --- a/src/views/mp/template/MsgTemplateForm.vue +++ /dev/null @@ -1,335 +0,0 @@ - - - - - diff --git a/src/views/mp/template/MsgTemplateLog.vue b/src/views/mp/template/MsgTemplateLog.vue deleted file mode 100644 index e3fd9234c..000000000 --- a/src/views/mp/template/MsgTemplateLog.vue +++ /dev/null @@ -1,214 +0,0 @@ - - - diff --git a/src/views/mp/template/MsgTemplateLogForm.vue b/src/views/mp/template/MsgTemplateLogForm.vue deleted file mode 100644 index 1266806f5..000000000 --- a/src/views/mp/template/MsgTemplateLogForm.vue +++ /dev/null @@ -1,144 +0,0 @@ - - \ No newline at end of file diff --git a/src/views/mp/template/MsgTemplateSend.vue b/src/views/mp/template/MsgTemplateSend.vue deleted file mode 100644 index 286be80e9..000000000 --- a/src/views/mp/template/MsgTemplateSend.vue +++ /dev/null @@ -1,82 +0,0 @@ - - - - - From 521911ca7e5678dd9f7ad5e1f1940de404dfba3b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 26 Nov 2025 18:56:46 +0800 Subject: [PATCH 070/136] =?UTF-8?q?feat=EF=BC=9A=E3=80=90mp=E3=80=91?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E2=80=9C=E6=A8=A1=E7=89=88=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E2=80=9D=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 14bb23a2e..295946d97 100644 --- a/README.md +++ b/README.md @@ -200,18 +200,19 @@ ### 微信公众号 -| | 功能 | 描述 | -|-----|--------|-------------------------------| -| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 | -| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 | -| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 | -| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 | -| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 | -| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 | -| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 | -| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 | -| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 | -| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 | +| | 功能 | 描述 | +|----|--------|-------------------------------| +| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 | +| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 | +| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 | +| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 | +| 🚀 | 模版消息 | 配置和发送模版消息,用于向粉丝推送通知类消息 | +| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 | +| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 | +| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 | +| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 | +| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 | +| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 | ### 商城系统 From 01a804631e39f0882d9aa39737bc5d335422d133 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 26 Nov 2025 19:04:47 +0800 Subject: [PATCH 071/136] =?UTF-8?q?feat=EF=BC=9A=E3=80=90mp=E3=80=91?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E2=80=9C=E6=A8=A1=E7=89=88=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E2=80=9D=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mp/messageTemplate/index.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/mp/messageTemplate/index.vue b/src/views/mp/messageTemplate/index.vue index 1bac3f7e7..d163db7b7 100644 --- a/src/views/mp/messageTemplate/index.vue +++ b/src/views/mp/messageTemplate/index.vue @@ -1,4 +1,6 @@ From 3a6454cfc170158a7034654cd7d84aed0e6a7947 Mon Sep 17 00:00:00 2001 From: zhanglc Date: Tue, 9 Dec 2025 21:50:54 +0800 Subject: [PATCH 085/136] =?UTF-8?q?feat:=20=E3=80=90bpm=E3=80=91bpmn?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E5=99=A8:=20=E4=B8=9A=E5=8A=A1=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E6=B5=81=E7=A8=8B=E6=B7=BB=E5=8A=A0=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E5=8F=91=E8=B5=B7=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/bpm/model/index.ts | 1 + src/views/bpm/oa/leave/create.vue | 26 +++++++++++++++++++++++++ src/views/bpm/oa/leave/index.vue | 19 ++++++++++++++++++ src/views/bpm/processInstance/index.vue | 21 ++++++++++++-------- 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/src/api/bpm/model/index.ts b/src/api/bpm/model/index.ts index 63b6af6ad..6d2b4d224 100644 --- a/src/api/bpm/model/index.ts +++ b/src/api/bpm/model/index.ts @@ -6,6 +6,7 @@ export type ProcessDefinitionVO = { deploymentTIme: string suspensionState: number formType?: number + formCustomCreatePath?: string } export type ModelVO = { diff --git a/src/views/bpm/oa/leave/create.vue b/src/views/bpm/oa/leave/create.vue index cf4a13e2a..4c7b336e4 100644 --- a/src/views/bpm/oa/leave/create.vue +++ b/src/views/bpm/oa/leave/create.vue @@ -79,6 +79,7 @@ defineOptions({ name: 'BpmOALeaveCreate' }) const message = useMessage() // 消息弹窗 const { delView } = useTagsViewStore() // 视图操作 const { push, currentRoute } = useRouter() // 路由 +const { query } = useRoute() // 查询参数 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formData = ref({ @@ -190,6 +191,26 @@ const daysDifference = () => { return Math.floor(diffTime / oneDay) } +/** 获取请假数据,用于重新发起时自动填充 */ +const getLeaveData = async (id: number) => { + try { + formLoading.value = true + const data = await LeaveApi.getLeave(id) + if (!data) { + message.error('重新发起请假失败,原因:请假数据不存在') + return + } + formData.value = { + type: data.type, + reason: data.reason, + startTime: data.startTime, + endTime: data.endTime + } + } finally { + formLoading.value = false + } +} + /** 初始化 */ onMounted(async () => { // TODO @小北:这里可以简化,统一通过 getApprovalDetail 处理么? @@ -205,6 +226,11 @@ onMounted(async () => { processDefinitionId.value = processDefinitionDetail.id startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks + // 如果有业务编号,说明是重新发起,需要加载原有数据 + if (query.id) { + await getLeaveData(Number(query.id)) + } + // 审批相关:加载最新的审批详情,主要用于节点预测 await getApprovalDetail() }) diff --git a/src/views/bpm/oa/leave/index.vue b/src/views/bpm/oa/leave/index.vue index 1d1d42740..9fab923a0 100644 --- a/src/views/bpm/oa/leave/index.vue +++ b/src/views/bpm/oa/leave/index.vue @@ -140,6 +140,15 @@ > 取消 + + 重新发起 + @@ -206,6 +215,16 @@ const handleCreate = () => { router.push({ name: 'OALeaveCreate' }) } +/** 重新发起操作 */ +const handleReCreate = (row: LeaveApi.LeaveVO) => { + router.push({ + name: 'OALeaveCreate', + query: { + id: row.id + } + }) +} + /** 详情操作 */ const handleDetail = (row: LeaveApi.LeaveVO) => { router.push({ diff --git a/src/views/bpm/processInstance/index.vue b/src/views/bpm/processInstance/index.vue index d6fc83d38..9d7d6cc72 100644 --- a/src/views/bpm/processInstance/index.vue +++ b/src/views/bpm/processInstance/index.vue @@ -275,21 +275,26 @@ const resetQuery = () => { /** 发起流程操作 **/ const handleCreate = async (row?: ProcessInstanceVO) => { - // 如果是【业务表单】,不支持重新发起 if (row?.id) { const processDefinitionDetail = await DefinitionApi.getProcessDefinition( row.processDefinitionId ) + //如果是【业务表单】,跳转到对应的发起界面 if (processDefinitionDetail.formType === 20) { - message.error('重新发起流程失败,原因:该流程使用业务表单,不支持重新发起') - return + await router.push({ + path: processDefinitionDetail.formCustomCreatePath, + query: { + id: row.businessKey + } + }) + } else if (processDefinitionDetail.formType === 10) { + //如果是【流程表单】,跳转到流程发起界面 + await router.push({ + name: 'BpmProcessInstanceCreate', + query: { processInstanceId: row.id } + }) } } - // 跳转发起流程界面 - await router.push({ - name: 'BpmProcessInstanceCreate', - query: { processInstanceId: row?.id } - }) } /** 查看详情 */ From 8f5f2e96b391ad5040728d2b5641bce613645397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=89=E5=B8=86?= <1048766504@qq.com> Date: Fri, 12 Dec 2025 08:47:29 +0000 Subject: [PATCH 086/136] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91=E6=96=87=E4=BB=B6=E5=88=97=E8=A1=A8:=20?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E9=93=BE=E6=8E=A5=E5=8A=9F=E8=83=BD=E5=9C=A8?= =?UTF-8?q?=E9=83=A8=E5=88=86=E6=B5=8F=E8=A7=88=E5=99=A8=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E4=B8=8B=E6=8A=A5=E9=94=99=E7=9A=84=E9=97=AE=E9=A2=98=20?= =?UTF-8?q?=E5=9C=A8=20navigator.clipboard=20=E4=B8=BA=20undefined?= =?UTF-8?q?=EF=BC=8C=E5=AF=BC=E8=87=B4=E6=97=A0=E6=B3=95=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=20writeText=20=E6=96=B9=E6=B3=95=EF=BC=8C=E6=B5=8F=E8=A7=88?= =?UTF-8?q?=E5=99=A8=E6=8A=9B=E5=87=BA=E5=BC=82=E5=B8=B8=EF=BC=9A=20Uncaug?= =?UTF-8?q?ht=20TypeError:=20Cannot=20read=20properties=20of=20undefined?= =?UTF-8?q?=20(reading=20'writeText')?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 光帆 <1048766504@qq.com> --- src/views/infra/file/index.vue | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/views/infra/file/index.vue b/src/views/infra/file/index.vue index 901139180..a0c9e258b 100644 --- a/src/views/infra/file/index.vue +++ b/src/views/infra/file/index.vue @@ -187,9 +187,29 @@ const openForm = () => { /** 复制到剪贴板方法 */ const copyToClipboard = (text: string) => { - navigator.clipboard.writeText(text).then(() => { - message.success('复制成功') - }) + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard + .writeText(text) + .then(() => { + message.success('复制成功') + }) + .catch(() => { + message.error('复制失败') + }) + } else { + // 兼容不支持 clipboard 的情况 + try { + const textarea = document.createElement('textarea') + textarea.value = text + document.body.appendChild(textarea) + textarea.select() + document.execCommand('copy') + document.body.removeChild(textarea) + message.success('复制成功') + } catch (error) { + message.error('复制失败') + } + } } /** 删除按钮操作 */ From 19f38c4a9ea29a36e22a48ae79123201cf91c8e2 Mon Sep 17 00:00:00 2001 From: z95813 <61101985+MurphyZX@users.noreply.github.com> Date: Mon, 15 Dec 2025 14:27:49 +0800 Subject: [PATCH 087/136] =?UTF-8?q?=E6=89=8B=E7=BB=AD=E8=B4=B9=E6=AF=94?= =?UTF-8?q?=E4=BE=8B=E4=B8=8D=E9=9C=80=E8=A6=81=E9=99=A4100?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/pay/order/OrderDetail.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/pay/order/OrderDetail.vue b/src/views/pay/order/OrderDetail.vue index 895bc1601..90d4ccbce 100644 --- a/src/views/pay/order/OrderDetail.vue +++ b/src/views/pay/order/OrderDetail.vue @@ -21,7 +21,7 @@ - {{ (detailData.channelFeeRate / 100.0).toFixed(2) }}% + {{ detailData.channelFeeRate.toFixed(2) }}% {{ formatDate(detailData.successTime) }} From b261ecf90f54997e92d135a7249fcecb83abaa0f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 20 Dec 2025 09:23:41 +0800 Subject: [PATCH 088/136] =?UTF-8?q?fix=EF=BC=9A=E3=80=90system=E3=80=91?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E6=97=A5=E5=BF=97=E7=9A=84=E2=80=9C=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E7=B1=BB=E5=9E=8B=E2=80=9D=E6=94=B9=E4=B8=BA=E2=80=9C?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E7=B1=BB=E5=9E=8B=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/system/loginlog/LoginLogDetail.vue | 2 +- src/views/system/loginlog/index.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/system/loginlog/LoginLogDetail.vue b/src/views/system/loginlog/LoginLogDetail.vue index ff494534a..c6deb11de 100644 --- a/src/views/system/loginlog/LoginLogDetail.vue +++ b/src/views/system/loginlog/LoginLogDetail.vue @@ -4,7 +4,7 @@ {{ detailData.id }} - + diff --git a/src/views/system/loginlog/index.vue b/src/views/system/loginlog/index.vue index 2e269ffb9..a22ebc02a 100644 --- a/src/views/system/loginlog/index.vue +++ b/src/views/system/loginlog/index.vue @@ -59,7 +59,7 @@ - + From fe0364c3e246b7b5887eb665e89c6c766da0b3b1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 20 Dec 2025 10:16:03 +0800 Subject: [PATCH 089/136] =?UTF-8?q?fix:=20=E3=80=90mall=E3=80=91=E9=97=A8?= =?UTF-8?q?=E5=BA=97=E6=8F=90=E6=8F=90=E4=B8=AD=EF=BC=8C=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=91=98=E5=B7=A5=E9=80=89=E6=8B=A9=E8=A1=A8=E5=8D=95=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E7=9A=84=20roleId=20=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../delivery/pickUpStore/components/StoreStaffTableSelect.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/views/mall/trade/delivery/pickUpStore/components/StoreStaffTableSelect.vue b/src/views/mall/trade/delivery/pickUpStore/components/StoreStaffTableSelect.vue index 4256fbc28..c5acda31e 100644 --- a/src/views/mall/trade/delivery/pickUpStore/components/StoreStaffTableSelect.vue +++ b/src/views/mall/trade/delivery/pickUpStore/components/StoreStaffTableSelect.vue @@ -160,7 +160,6 @@ const queryParams = reactive({ mobile: undefined, status: undefined, deptId: undefined, - roleId: 5, createTime: [] }) const queryFormRef = ref() // 搜索的表单 From d492aeb56d004884b18980c7a87fca4cac018648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B0=B4=E4=BA=BA?= <417693680@qq.com> Date: Sat, 20 Dec 2025 03:22:34 +0000 Subject: [PATCH 090/136] =?UTF-8?q?update=20src/views/bpm/processInstance/?= =?UTF-8?q?detail/ProcessInstanceOperationButton.vue.=20=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=B0=8F=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 水人 <417693680@qq.com> --- .../processInstance/detail/ProcessInstanceOperationButton.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue index 1b7d5c73d..53b10bdfc 100644 --- a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue +++ b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue @@ -582,7 +582,7 @@ const approveReasonForm = reactive({ const approveReasonRule = computed(() => { return { reason: [ - { required: reasonRequire.value, message: nodeTypeName + '意见不能为空', trigger: 'blur' } + { required: reasonRequire.value, message: nodeTypeName.value + '意见不能为空', trigger: 'blur' } ], signPicUrl: [{ required: true, message: '签名不能为空', trigger: 'change' }], nextAssignees: [{ required: true, message: '审批人不能为空', trigger: 'blur' }] From 932255bd140144cd3d2ae8c06f0ea2bd18458f2c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 20 Dec 2025 20:48:34 +0800 Subject: [PATCH 091/136] =?UTF-8?q?fix=EF=BC=9A=E3=80=90system=E3=80=91?= =?UTF-8?q?=E9=82=AE=E7=AE=B1=E7=A7=BB=E9=99=A4=E6=97=A0=E7=94=A8=E7=9A=84?= =?UTF-8?q?=20remark=20=E5=AD=97=E6=AE=B5=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E8=A1=A8=E5=8D=95=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/system/mail/template/index.ts | 4 +--- src/views/system/mail/template/MailTemplateForm.vue | 11 +++-------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/api/system/mail/template/index.ts b/src/api/system/mail/template/index.ts index 6e1d083f2..d340f8a24 100644 --- a/src/api/system/mail/template/index.ts +++ b/src/api/system/mail/template/index.ts @@ -1,16 +1,14 @@ import request from '@/config/axios' export interface MailTemplateVO { - id: number + id?: number name: string code: string accountId: number nickname: string title: string content: string - params: string status: number - remark: string } export interface MailSendReqVO { diff --git a/src/views/system/mail/template/MailTemplateForm.vue b/src/views/system/mail/template/MailTemplateForm.vue index faf2020b4..cae5f9d53 100644 --- a/src/views/system/mail/template/MailTemplateForm.vue +++ b/src/views/system/mail/template/MailTemplateForm.vue @@ -43,9 +43,6 @@ - - - From 46602fec26bb2bf10ba852ad01f4f28bdaf6040b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 27 Dec 2025 11:39:24 +0800 Subject: [PATCH 094/136] =?UTF-8?q?feat=EF=BC=9A=E3=80=90bpm=E3=80=91?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=9A=E5=8A=A1=E8=A1=A8=E5=8D=95=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E6=B7=BB=E5=8A=A0=E9=87=8D=E6=96=B0=E5=8F=91=E8=B5=B7?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/bpm/oa/leave/create.vue | 4 ++-- src/views/bpm/processInstance/index.vue | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/bpm/oa/leave/create.vue b/src/views/bpm/oa/leave/create.vue index 4c7b336e4..b64f4ca7b 100644 --- a/src/views/bpm/oa/leave/create.vue +++ b/src/views/bpm/oa/leave/create.vue @@ -192,7 +192,7 @@ const daysDifference = () => { } /** 获取请假数据,用于重新发起时自动填充 */ -const getLeaveData = async (id: number) => { +const getDetail = async (id: number) => { try { formLoading.value = true const data = await LeaveApi.getLeave(id) @@ -228,7 +228,7 @@ onMounted(async () => { // 如果有业务编号,说明是重新发起,需要加载原有数据 if (query.id) { - await getLeaveData(Number(query.id)) + await getDetail(Number(query.id)) } // 审批相关:加载最新的审批详情,主要用于节点预测 diff --git a/src/views/bpm/processInstance/index.vue b/src/views/bpm/processInstance/index.vue index 9d7d6cc72..1a8ef8900 100644 --- a/src/views/bpm/processInstance/index.vue +++ b/src/views/bpm/processInstance/index.vue @@ -279,7 +279,7 @@ const handleCreate = async (row?: ProcessInstanceVO) => { const processDefinitionDetail = await DefinitionApi.getProcessDefinition( row.processDefinitionId ) - //如果是【业务表单】,跳转到对应的发起界面 + // 如果是【业务表单】,跳转到对应的发起界面 if (processDefinitionDetail.formType === 20) { await router.push({ path: processDefinitionDetail.formCustomCreatePath, From 678f3c60cf37fc23716b05e94194b6efe04bd5fd Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 28 Dec 2025 11:09:56 +0800 Subject: [PATCH 095/136] =?UTF-8?q?(=E3=80=83'=E2=96=BD'=E3=80=83)=20v2025?= =?UTF-8?q?.12=20=E5=8F=91=E5=B8=83=EF=BC=9A=E6=9E=81=E5=A4=A7=E6=9E=81?= =?UTF-8?q?=E5=A4=A7=E5=AE=8C=E5=96=84=20vben5=20=E7=9A=84=20antd=E3=80=81?= =?UTF-8?q?vben=20=E7=89=88=E6=9C=AC=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20admin=20uniapp=20vue3=20=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index daad2678a..1a12256e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yudao-ui-admin-vue3", - "version": "2025.11-snapshot", + "version": "2025.12-snapshot", "description": "基于vue3、vite4、element-plus、typesScript", "author": "xingyu", "private": false, From 2a9d07f514f45227c7ba1241322880ab3c89c8f0 Mon Sep 17 00:00:00 2001 From: preschooler Date: Sun, 28 Dec 2025 20:40:06 +0800 Subject: [PATCH 096/136] =?UTF-8?q?=F0=9F=90=9E=20fix:=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=AF=8C=E6=96=87=E6=9C=AC=E7=BC=96=E8=BE=91=E5=99=A8?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E4=B8=8A=E4=BC=A0=E5=90=8C=E4=B8=80=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E9=97=AE=E9=A2=98=E3=80=81=E6=B7=BB=E5=8A=A0=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E7=9B=AE=E5=BD=95=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Editor/src/Editor.vue | 35 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/components/Editor/src/Editor.vue b/src/components/Editor/src/Editor.vue index 36fe1650f..30146be37 100644 --- a/src/components/Editor/src/Editor.vue +++ b/src/components/Editor/src/Editor.vue @@ -28,7 +28,8 @@ const props = defineProps({ default: () => undefined }, readonly: propTypes.bool.def(false), - modelValue: propTypes.string.def('') + modelValue: propTypes.string.def(''), + directory: propTypes.string.def('editor-default') }) const emit = defineEmits(['change', 'update:modelValue']) @@ -115,9 +116,9 @@ const editorConfig = computed((): IEditorConfig => { ['uploadImage']: { server: getUploadUrl(), // 单个文件的最大体积限制,默认为 2M - maxFileSize: 5 * 1024 * 1024, + maxFileSize: 10 * 1024 * 1024, // 最多可上传几个文件,默认为 100 - maxNumberOfFiles: 10, + maxNumberOfFiles: 100, // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 [] allowedFileTypes: ['image/*'], @@ -133,6 +134,19 @@ const editorConfig = computed((): IEditorConfig => { // form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image fieldName: 'file', + // 附加参数 + meta: { + directory: `${props.directory}-image` + }, + metaWithUrl: false, + + // uppy 配置项 + uppyConfig: { + onBeforeFileAdded: (newFile: any) => { + newFile.id = `${newFile.id}-${Date.now()}` + return newFile + } + }, // 上传之前触发 onBeforeUpload(file: File) { @@ -163,7 +177,7 @@ const editorConfig = computed((): IEditorConfig => { ['uploadVideo']: { server: getUploadUrl(), // 单个文件的最大体积限制,默认为 10M - maxFileSize: 10 * 1024 * 1024, + maxFileSize: 1024 * 1024 * 1024, // 最多可上传几个文件,默认为 100 maxNumberOfFiles: 10, // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 [] @@ -181,6 +195,19 @@ const editorConfig = computed((): IEditorConfig => { // form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image fieldName: 'file', + // 附加参数 + meta: { + directory: `${props.directory}-video` + }, + metaWithUrl: false, + + // uppy 配置项 + uppyConfig: { + onBeforeFileAdded: (newFile: any) => { + newFile.id = `${newFile.id}-${Date.now()}` + return newFile + } + }, // 上传之前触发 onBeforeUpload(file: File) { From 9af4cad55246c416f4b40adb0e5a5e120e97ba50 Mon Sep 17 00:00:00 2001 From: preschooler Date: Sun, 28 Dec 2025 21:15:44 +0800 Subject: [PATCH 097/136] =?UTF-8?q?=F0=9F=90=9E=20fix:=20=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0=E6=97=B6=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=20Content-Type=EF=BC=8C=E5=90=A6=E5=88=99=E4=BC=9A?= =?UTF-8?q?=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UploadFile/src/useUpload.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/UploadFile/src/useUpload.ts b/src/components/UploadFile/src/useUpload.ts index 8f316eb59..dddd15961 100644 --- a/src/components/UploadFile/src/useUpload.ts +++ b/src/components/UploadFile/src/useUpload.ts @@ -1,5 +1,9 @@ import * as FileApi from '@/api/infra/file' -import { UploadRawFile, UploadRequestOptions, UploadProgressEvent } from 'element-plus/es/components/upload/src/upload' +import { + UploadRawFile, + UploadRequestOptions, + UploadProgressEvent +} from 'element-plus/es/components/upload/src/upload' import axios, { AxiosProgressEvent } from 'axios' /** @@ -19,7 +23,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 + upEvt.percent = evt.progress ? evt.progress * 100 : 0 options.onProgress(upEvt) // 触发 el-upload 的 on-progress } @@ -33,7 +37,7 @@ export const useUpload = (directory?: string) => { return axios .put(presignedInfo.uploadUrl, options.file, { headers: { - 'Content-Type': options.file.type + 'Content-Type': options.file.type || 'application/octet-stream' }, onUploadProgress: uploadProgressHandler }) @@ -80,7 +84,7 @@ function createFile(vo: FileApi.FilePresignedUrlRespVO, file: UploadRawFile, fil url: vo.url, path: vo.path, name: fileName, - type: file.type, + type: file.type || 'application/octet-stream', size: file.size } FileApi.createFile(fileVo) From ee7c73b4751f341a3c91388f781a227e83fe0d82 Mon Sep 17 00:00:00 2001 From: preschooler Date: Sun, 28 Dec 2025 21:18:03 +0800 Subject: [PATCH 098/136] =?UTF-8?q?=F0=9F=8E=88=20perf:=20=E6=96=B0?= =?UTF-8?q?=E5=BC=80=E6=A0=87=E7=AD=BE=E6=97=B6=E3=80=81=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=E6=97=B6=EF=BC=8C=E6=BB=9A=E5=8A=A8=E6=9D=A1?= =?UTF-8?q?=E5=9B=9E=E5=88=B0=E9=A1=B6=E9=83=A8=EF=BC=8C=E5=90=A6=E5=88=99?= =?UTF-8?q?=E4=BC=9A=E4=BF=9D=E7=95=99=E4=B8=8A=E6=AC=A1=E6=A0=87=E7=AD=BE?= =?UTF-8?q?=E7=9A=84=E6=BB=9A=E5=8A=A8=E4=BD=8D=E7=BD=AE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/index.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/router/index.ts b/src/router/index.ts index e80dae08e..4e861c6c5 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -8,7 +8,15 @@ const router = createRouter({ history: createWebHistory(import.meta.env.VITE_BASE_PATH), // createWebHashHistory URL带#,createWebHistory URL不带# strict: true, routes: remainingRouter as RouteRecordRaw[], - scrollBehavior: () => ({ left: 0, top: 0 }) + scrollBehavior: () => { + // 新开标签时、返回标签时,滚动条回到顶部,否则会保留上次标签的滚动位置。 + const scrollbarWrap = document.querySelector('.v-layout-content-scrollbar .el-scrollbar__wrap') + if (scrollbarWrap) { + // scrollbarWrap.scrollTo({ left: 0, top: 0, behavior: 'auto' }) + scrollbarWrap.scrollTop = 0 + } + return { left: 0, top: 0 } + } }) export const resetRouter = (): void => { From 394c94b67c07fb3065ccef22378e2b5c1042118d Mon Sep 17 00:00:00 2001 From: preschooler Date: Sun, 28 Dec 2025 22:14:42 +0800 Subject: [PATCH 099/136] =?UTF-8?q?=F0=9F=8E=88=20perf:=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20copy=20=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/MarkdownView/index.vue | 2 +- src/layout/components/Setting/src/Setting.vue | 3 +- .../index/components/message/MessageList.vue | 2 +- src/views/ai/write/index/components/Right.vue | 2 +- src/views/infra/build/index.vue | 2 +- src/views/infra/codegen/PreviewCode.vue | 2 +- src/views/infra/file/index.vue | 33 ++++++------------- .../device/detail/DeviceDetailsHeader.vue | 15 ++++++--- .../device/detail/DeviceDetailsInfo.vue | 15 ++++++--- .../product/detail/ProductDetailsHeader.vue | 15 ++++++--- 10 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/components/MarkdownView/index.vue b/src/components/MarkdownView/index.vue index 74764d592..86fc93917 100644 --- a/src/components/MarkdownView/index.vue +++ b/src/components/MarkdownView/index.vue @@ -17,7 +17,7 @@ const props = defineProps({ }) const message = useMessage() // 消息弹窗 -const { copy } = useClipboard() // 初始化 copy 到粘贴板 +const { copy } = useClipboard({ legacy: true }) // 初始化 copy 到粘贴板 const contentRef = ref() const md = new MarkdownIt({ diff --git a/src/layout/components/Setting/src/Setting.vue b/src/layout/components/Setting/src/Setting.vue index 2973674ba..92ecf4149 100644 --- a/src/layout/components/Setting/src/Setting.vue +++ b/src/layout/components/Setting/src/Setting.vue @@ -109,6 +109,7 @@ watch( // 拷贝 const copyConfig = async () => { const { copy, copied, isSupported } = useClipboard({ + legacy: true, source: ` // 面包屑 breadcrumb: ${appStore.getBreadcrumb}, @@ -296,7 +297,7 @@ const clear = () => { $prefix-cls: #{$namespace}-setting; .#{$prefix-cls} { + z-index: 1200; /* 修正没有z-index会被表格层覆盖,值不要超过4000 */ border-radius: 6px 0 0 6px; - z-index: 1200;/*修正没有z-index会被表格层覆盖,值不要超过4000*/ } diff --git a/src/views/ai/chat/index/components/message/MessageList.vue b/src/views/ai/chat/index/components/message/MessageList.vue index 77b41c796..da5c338a6 100644 --- a/src/views/ai/chat/index/components/message/MessageList.vue +++ b/src/views/ai/chat/index/components/message/MessageList.vue @@ -126,7 +126,7 @@ import userAvatarDefaultImg from '@/assets/imgs/avatar.gif' import roleAvatarDefaultImg from '@/assets/ai/gpt.svg' const message = useMessage() // 消息弹窗 -const { copy } = useClipboard() // 初始化 copy 到粘贴板 +const { copy } = useClipboard({ legacy: true }) // 初始化 copy 到粘贴板 const userStore = useUserStore() // 判断“消息列表”滚动的位置(用于判断是否需要滚动到消息最下方) diff --git a/src/views/ai/write/index/components/Right.vue b/src/views/ai/write/index/components/Right.vue index d0aada5df..1eb66a077 100644 --- a/src/views/ai/write/index/components/Right.vue +++ b/src/views/ai/write/index/components/Right.vue @@ -45,7 +45,7 @@ import { useClipboard } from '@vueuse/core' const message = useMessage() // 消息弹窗 -const { copied, copy } = useClipboard() // 粘贴板 +const { copied, copy } = useClipboard({ legacy: true }) // 粘贴板 const props = defineProps({ content: { diff --git a/src/views/infra/build/index.vue b/src/views/infra/build/index.vue index 260b8b7a0..191fc90fe 100644 --- a/src/views/infra/build/index.vue +++ b/src/views/infra/build/index.vue @@ -136,7 +136,7 @@ const makeTemplate = () => { /** 复制 **/ const copy = async (text: string) => { const textToCopy = JSON.stringify(text, null, 2) - const { copy, copied, isSupported } = useClipboard({ source: textToCopy }) + const { copy, copied, isSupported } = useClipboard({ legacy: true, source: textToCopy }) if (!isSupported) { message.error(t('common.copyError')) } else { diff --git a/src/views/infra/codegen/PreviewCode.vue b/src/views/infra/codegen/PreviewCode.vue index b6a307d23..819fca6c0 100644 --- a/src/views/infra/codegen/PreviewCode.vue +++ b/src/views/infra/codegen/PreviewCode.vue @@ -180,7 +180,7 @@ const handleFiles = (datas: CodegenApi.CodegenPreviewVO[]) => { /** 复制 **/ const copy = async (text: string) => { - const { copy, copied, isSupported } = useClipboard({ source: text }) + const { copy, copied, isSupported } = useClipboard({ legacy: true, source: text }) if (!isSupported) { message.error(t('common.copyError')) return diff --git a/src/views/infra/file/index.vue b/src/views/infra/file/index.vue index a0c9e258b..e431218da 100644 --- a/src/views/infra/file/index.vue +++ b/src/views/infra/file/index.vue @@ -136,6 +136,7 @@ import { fileSizeFormatter } from '@/utils' import { dateFormatter } from '@/utils/formatTime' import * as FileApi from '@/api/infra/file' import FileForm from './FileForm.vue' +import { useClipboard } from '@vueuse/core' defineOptions({ name: 'InfraFile' }) @@ -186,29 +187,15 @@ const openForm = () => { } /** 复制到剪贴板方法 */ -const copyToClipboard = (text: string) => { - if (navigator.clipboard && window.isSecureContext) { - navigator.clipboard - .writeText(text) - .then(() => { - message.success('复制成功') - }) - .catch(() => { - message.error('复制失败') - }) - } else { - // 兼容不支持 clipboard 的情况 - try { - const textarea = document.createElement('textarea') - textarea.value = text - document.body.appendChild(textarea) - textarea.select() - document.execCommand('copy') - document.body.removeChild(textarea) - message.success('复制成功') - } catch (error) { - message.error('复制失败') - } +const copyToClipboard = async (text: string) => { + const { copy, copied, isSupported } = useClipboard({ legacy: true, source: text }) + if (!isSupported) { + message.error(t('common.copyError')) + return + } + await copy() + if (unref(copied)) { + message.success(t('common.copySuccess')) } } diff --git a/src/views/iot/device/device/detail/DeviceDetailsHeader.vue b/src/views/iot/device/device/detail/DeviceDetailsHeader.vue index 6fc876bc8..c6d031fdb 100644 --- a/src/views/iot/device/device/detail/DeviceDetailsHeader.vue +++ b/src/views/iot/device/device/detail/DeviceDetailsHeader.vue @@ -39,8 +39,10 @@ import DeviceForm from '@/views/iot/device/device/DeviceForm.vue' import { ProductVO } from '@/api/iot/product/product' import { DeviceVO } from '@/api/iot/device/device' +import { useClipboard } from '@vueuse/core' const message = useMessage() +const { t } = useI18n() // 国际化 const router = useRouter() const { product, device } = defineProps<{ product: ProductVO; device: DeviceVO }>() @@ -54,11 +56,14 @@ const openForm = (type: string, id?: number) => { /** 复制到剪贴板方法 */ const copyToClipboard = async (text: string) => { - try { - await navigator.clipboard.writeText(text) - message.success('复制成功') - } catch (error) { - message.error('复制失败') + const { copy, copied, isSupported } = useClipboard({ legacy: true, source: text }) + if (!isSupported) { + message.error(t('common.copyError')) + return + } + await copy() + if (unref(copied)) { + message.success(t('common.copySuccess')) } } diff --git a/src/views/iot/device/device/detail/DeviceDetailsInfo.vue b/src/views/iot/device/device/detail/DeviceDetailsInfo.vue index 930adb678..e3beda752 100644 --- a/src/views/iot/device/device/detail/DeviceDetailsInfo.vue +++ b/src/views/iot/device/device/detail/DeviceDetailsInfo.vue @@ -140,8 +140,10 @@ import { DeviceVO } from '@/api/iot/device/device' import { DeviceApi, IotDeviceAuthInfoVO } from '@/api/iot/device/device' import Map from '@/components/Map/index.vue' import { ref, computed } from 'vue' +import { useClipboard } from '@vueuse/core' const message = useMessage() // 消息提示 +const { t } = useI18n() // 国际化 const { product, device } = defineProps<{ product: ProductVO; device: DeviceVO }>() // 定义 Props const emit = defineEmits(['refresh']) // 定义 Emits @@ -165,11 +167,14 @@ const getLocationString = () => { /** 复制到剪贴板方法 */ const copyToClipboard = async (text: string) => { - try { - await navigator.clipboard.writeText(text) - message.success('复制成功') - } catch (error) { - message.error('复制失败') + const { copy, copied, isSupported } = useClipboard({ legacy: true, source: text }) + if (!isSupported) { + message.error(t('common.copyError')) + return + } + await copy() + if (unref(copied)) { + message.success(t('common.copySuccess')) } } diff --git a/src/views/iot/product/product/detail/ProductDetailsHeader.vue b/src/views/iot/product/product/detail/ProductDetailsHeader.vue index 3eb999fa4..311900c2a 100644 --- a/src/views/iot/product/product/detail/ProductDetailsHeader.vue +++ b/src/views/iot/product/product/detail/ProductDetailsHeader.vue @@ -54,18 +54,23 @@ diff --git a/src/views/iot/rule/data/sink/config/WebSocketConfigForm.vue b/src/views/iot/rule/data/sink/config/WebSocketConfigForm.vue new file mode 100644 index 000000000..0da9034b1 --- /dev/null +++ b/src/views/iot/rule/data/sink/config/WebSocketConfigForm.vue @@ -0,0 +1,117 @@ + + diff --git a/src/views/iot/rule/data/sink/config/index.ts b/src/views/iot/rule/data/sink/config/index.ts index b3927dc94..f04808f58 100644 --- a/src/views/iot/rule/data/sink/config/index.ts +++ b/src/views/iot/rule/data/sink/config/index.ts @@ -1,4 +1,6 @@ import HttpConfigForm from './HttpConfigForm.vue' +import TcpConfigForm from './TcpConfigForm.vue' +import WebSocketConfigForm from './WebSocketConfigForm.vue' import MqttConfigForm from './MqttConfigForm.vue' import RocketMQConfigForm from './RocketMQConfigForm.vue' import KafkaMQConfigForm from './KafkaMQConfigForm.vue' @@ -7,6 +9,8 @@ import RedisStreamConfigForm from './RedisStreamConfigForm.vue' export { HttpConfigForm, + TcpConfigForm, + WebSocketConfigForm, MqttConfigForm, RocketMQConfigForm, KafkaMQConfigForm, From f986ff001703aedaa4dada396d6d45d287b1c5b0 Mon Sep 17 00:00:00 2001 From: zsc <957966054@qq.com> Date: Fri, 16 Jan 2026 13:09:58 +0800 Subject: [PATCH 104/136] =?UTF-8?q?bugfix-Map=E5=9C=B0=E5=9B=BE=E7=BB=84?= =?UTF-8?q?=E4=BB=B6bug=E4=BF=AE=E5=A4=8D=201.=20=E9=A6=96=E6=AC=A1?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96center=E5=9D=90=E6=A0=87=E4=B8=8D?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E9=97=AE=E9=A2=98=202.=20=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E8=AF=8D=E6=90=9C=E7=B4=A2=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Map/index.vue | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/components/Map/index.vue b/src/components/Map/index.vue index 7b9ae1cff..86010b268 100644 --- a/src/components/Map/index.vue +++ b/src/components/Map/index.vue @@ -71,6 +71,16 @@ const props = defineProps({ center: propTypes.string.def('') }) +watch( + () => props.center, + (newVal, oldVal) => { + if (newVal) { + // 当 center 变化时 重新加载mark点 + regeoCode(newVal) + } + } +) + /** 加载百度地图 */ const loadMap = () => { state.address = '' @@ -101,11 +111,6 @@ const loadMap = () => { regeoCode(state.lonLat) }) } - - // TODO @super:这里加一行注释 - if (props.center) { - regeoCode(props.center) - } } } @@ -155,8 +160,8 @@ const autoSearch = (queryValue: string) => { state.loading = false const temp: any[] = [] - if (results && results.getPoi) { - const pois = results.getPoi() + if (results && results._pois) { + const pois = results._pois pois.forEach((p: any) => { const point = p.point if (point && point.lng && point.lat) { From 8587a549c692778b7b9a08e83efc4d139e583bce Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 17 Jan 2026 19:36:51 +0800 Subject: [PATCH 105/136] =?UTF-8?q?feat=EF=BC=9A=E3=80=90iot=E3=80=91modbu?= =?UTF-8?q?s-tcp=20=E5=8D=8F=E8=AE=AE=E6=8E=A5=E5=85=A5=2020%=EF=BC=9A?= =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0=EF=BC=8C=E5=9F=BA=E4=BA=8E?= =?UTF-8?q?=20dynamic-forging-wigderson.md=20=E8=A7=84=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/iot/device/modbus/config/index.ts | 29 ++ src/api/iot/device/modbus/point/index.ts | 48 +++ .../device/detail/DeviceModbusConfig.vue | 262 +++++++++++++++ .../device/detail/DeviceModbusConfigForm.vue | 160 +++++++++ .../device/detail/DeviceModbusPointForm.vue | 315 ++++++++++++++++++ src/views/iot/device/device/detail/index.vue | 13 + src/views/iot/utils/constants.ts | 68 ++++ 7 files changed, 895 insertions(+) create mode 100644 src/api/iot/device/modbus/config/index.ts create mode 100644 src/api/iot/device/modbus/point/index.ts create mode 100644 src/views/iot/device/device/detail/DeviceModbusConfig.vue create mode 100644 src/views/iot/device/device/detail/DeviceModbusConfigForm.vue create mode 100644 src/views/iot/device/device/detail/DeviceModbusPointForm.vue diff --git a/src/api/iot/device/modbus/config/index.ts b/src/api/iot/device/modbus/config/index.ts new file mode 100644 index 000000000..fa0e0a802 --- /dev/null +++ b/src/api/iot/device/modbus/config/index.ts @@ -0,0 +1,29 @@ +import request from '@/config/axios' + +/** Modbus 连接配置 VO */ +export interface DeviceModbusConfigVO { + id?: number // 主键 + deviceId: number // 设备编号 + ip: string // Modbus 服务器 IP 地址 + port: number // Modbus 服务器端口 + slaveId: number // 从站地址 + timeout: number // 连接超时时间,单位:毫秒 + retryInterval: number // 重试间隔,单位:毫秒 + status: number // 状态 +} + +/** Modbus 连接配置 API */ +export const DeviceModbusConfigApi = { + /** 获取设备的 Modbus 连接配置 */ + getModbusConfig: async (deviceId: number) => { + return await request.get({ + url: `/iot/device-modbus-config/get`, + params: { deviceId } + }) + }, + + /** 保存 Modbus 连接配置 */ + saveModbusConfig: async (data: DeviceModbusConfigVO) => { + return await request.post({ url: `/iot/device-modbus-config/save`, data }) + } +} diff --git a/src/api/iot/device/modbus/point/index.ts b/src/api/iot/device/modbus/point/index.ts new file mode 100644 index 000000000..5dc05d1c1 --- /dev/null +++ b/src/api/iot/device/modbus/point/index.ts @@ -0,0 +1,48 @@ +import request from '@/config/axios' + +/** Modbus 点位配置 VO */ +export interface DeviceModbusPointVO { + id?: number // 主键 + deviceId: number // 设备编号 + thingModelId: number // 物模型属性编号 + identifier: string // 属性标识符 + name: string // 属性名称 + functionCode: number // Modbus 功能码 + registerAddress: number // 寄存器起始地址 + registerCount: number // 寄存器数量 + byteOrder: string // 字节序 + rawDataType: string // 原始数据类型 + scale: number // 缩放因子 + pollInterval: number // 轮询间隔,单位:毫秒 + status: number // 状态 +} + +/** Modbus 点位配置 API */ +export const DeviceModbusPointApi = { + /** 获取设备的 Modbus 点位分页 */ + getModbusPointPage: async (params: any) => { + return await request.get({ url: `/iot/device-modbus-point/page`, params }) + }, + + /** 获取 Modbus 点位详情 */ + getModbusPoint: async (id: number) => { + return await request.get({ + url: `/iot/device-modbus-point/get?id=${id}` + }) + }, + + /** 创建 Modbus 点位配置 */ + createModbusPoint: async (data: DeviceModbusPointVO) => { + return await request.post({ url: `/iot/device-modbus-point/create`, data }) + }, + + /** 更新 Modbus 点位配置 */ + updateModbusPoint: async (data: DeviceModbusPointVO) => { + return await request.put({ url: `/iot/device-modbus-point/update`, data }) + }, + + /** 删除 Modbus 点位配置 */ + deleteModbusPoint: async (id: number) => { + return await request.delete({ url: `/iot/device-modbus-point/delete?id=${id}` }) + } +} diff --git a/src/views/iot/device/device/detail/DeviceModbusConfig.vue b/src/views/iot/device/device/detail/DeviceModbusConfig.vue new file mode 100644 index 000000000..d380fb2dc --- /dev/null +++ b/src/views/iot/device/device/detail/DeviceModbusConfig.vue @@ -0,0 +1,262 @@ + + + + diff --git a/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue b/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue new file mode 100644 index 000000000..0e9572a60 --- /dev/null +++ b/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue @@ -0,0 +1,160 @@ + + + + diff --git a/src/views/iot/device/device/detail/DeviceModbusPointForm.vue b/src/views/iot/device/device/detail/DeviceModbusPointForm.vue new file mode 100644 index 000000000..db0b7b523 --- /dev/null +++ b/src/views/iot/device/device/detail/DeviceModbusPointForm.vue @@ -0,0 +1,315 @@ + + + + diff --git a/src/views/iot/device/device/detail/index.vue b/src/views/iot/device/device/detail/index.vue index 3ec756b5c..5c44c94b4 100644 --- a/src/views/iot/device/device/detail/index.vue +++ b/src/views/iot/device/device/detail/index.vue @@ -36,6 +36,18 @@ @success="getDeviceData" /> + + + @@ -50,6 +62,7 @@ import DeviceDetailsThingModel from './DeviceDetailsThingModel.vue' import DeviceDetailsMessage from './DeviceDetailsMessage.vue' import DeviceDetailsSimulator from './DeviceDetailsSimulator.vue' import DeviceDetailConfig from './DeviceDetailConfig.vue' +import DeviceModbusConfig from './DeviceModbusConfig.vue' defineOptions({ name: 'IoTDeviceDetail' }) diff --git a/src/views/iot/utils/constants.ts b/src/views/iot/utils/constants.ts index fe6db1881..259f5dcef 100644 --- a/src/views/iot/utils/constants.ts +++ b/src/views/iot/utils/constants.ts @@ -541,3 +541,71 @@ export const JSON_PARAMS_EXAMPLE_VALUES = { [IoTDataSpecsDataTypeEnum.ARRAY]: { display: '[]', value: [] }, DEFAULT: { display: '""', value: '' } } as const + +// ========== Modbus 相关常量 ========== + +/** Modbus 功能码枚举 */ +export const ModbusFunctionCodeEnum = { + READ_COILS: 1, // 读线圈 + READ_DISCRETE_INPUTS: 2, // 读离散输入 + READ_HOLDING_REGISTERS: 3, // 读保持寄存器 + READ_INPUT_REGISTERS: 4 // 读输入寄存器 +} as const + +/** Modbus 功能码选项 */ +export const ModbusFunctionCodeOptions = [ + { value: 1, label: '01 - 读线圈 (Coils)', description: '可读写布尔值' }, + { value: 2, label: '02 - 读离散输入 (Discrete Inputs)', description: '只读布尔值' }, + { value: 3, label: '03 - 读保持寄存器 (Holding Registers)', description: '可读写16位数据' }, + { value: 4, label: '04 - 读输入寄存器 (Input Registers)', description: '只读16位数据' } +] + +/** Modbus 原始数据类型枚举 */ +export const ModbusRawDataTypeEnum = { + INT16: 'INT16', + UINT16: 'UINT16', + INT32: 'INT32', + UINT32: 'UINT32', + FLOAT: 'FLOAT', + DOUBLE: 'DOUBLE', + BOOLEAN: 'BOOLEAN', + STRING: 'STRING' +} as const + +/** Modbus 原始数据类型选项 */ +export const ModbusRawDataTypeOptions = [ + { value: 'INT16', label: 'INT16', description: '有符号16位整数', registerCount: 1 }, + { value: 'UINT16', label: 'UINT16', description: '无符号16位整数', registerCount: 1 }, + { value: 'INT32', label: 'INT32', description: '有符号32位整数', registerCount: 2 }, + { value: 'UINT32', label: 'UINT32', description: '无符号32位整数', registerCount: 2 }, + { value: 'FLOAT', label: 'FLOAT', description: '32位浮点数', registerCount: 2 }, + { value: 'DOUBLE', label: 'DOUBLE', description: '64位浮点数', registerCount: 4 }, + { value: 'BOOLEAN', label: 'BOOLEAN', description: '布尔值', registerCount: 1 }, + { value: 'STRING', label: 'STRING', description: '字符串', registerCount: 0 } +] + +/** Modbus 字节序选项 - 16位 */ +export const ModbusByteOrder16Options = [ + { value: 'AB', label: 'AB', description: '大端序' }, + { value: 'BA', label: 'BA', description: '小端序' } +] + +/** Modbus 字节序选项 - 32位 */ +export const ModbusByteOrder32Options = [ + { value: 'ABCD', label: 'ABCD', description: '大端序' }, + { value: 'CDAB', label: 'CDAB', description: '大端字交换' }, + { value: 'DCBA', label: 'DCBA', description: '小端序' }, + { value: 'BADC', label: 'BADC', description: '小端字交换' } +] + +/** 根据数据类型获取字节序选项 */ +export const getByteOrderOptions = (rawDataType: string) => { + if (['INT32', 'UINT32', 'FLOAT'].includes(rawDataType)) { + return ModbusByteOrder32Options + } + if (rawDataType === 'DOUBLE') { + // 64位暂时复用32位字节序 + return ModbusByteOrder32Options + } + return ModbusByteOrder16Options +} From 56a25258ee7f29935173716b7a2c95e59f7f3e01 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 17 Jan 2026 23:34:43 +0800 Subject: [PATCH 106/136] =?UTF-8?q?feat=EF=BC=9A=E3=80=90iot=E3=80=91modbu?= =?UTF-8?q?s-tcp=20=E5=8D=8F=E8=AE=AE=E6=8E=A5=E5=85=A5=2040%=EF=BC=9A?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=83=A8=E5=88=86=E8=A1=A8=E5=8D=95=E7=9A=84?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=EF=BC=8C=E5=9F=BA=E4=BA=8E=20dreamy-stirring?= =?UTF-8?q?-kazoo.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../device/detail/DeviceModbusConfig.vue | 40 ++-- .../device/detail/DeviceModbusConfigForm.vue | 43 ++-- .../device/detail/DeviceModbusPointForm.vue | 200 +++++++----------- 3 files changed, 113 insertions(+), 170 deletions(-) diff --git a/src/views/iot/device/device/detail/DeviceModbusConfig.vue b/src/views/iot/device/device/detail/DeviceModbusConfig.vue index d380fb2dc..67fea1c28 100644 --- a/src/views/iot/device/device/detail/DeviceModbusConfig.vue +++ b/src/views/iot/device/device/detail/DeviceModbusConfig.vue @@ -39,6 +39,7 @@ 点位配置 + 新增点位 @@ -106,16 +107,16 @@ @@ -149,6 +150,7 @@ import { ThingModelData } from '@/api/iot/thingmodel' import { DeviceModbusConfigApi, DeviceModbusConfigVO } from '@/api/iot/device/modbus/config' import { DeviceModbusPointApi, DeviceModbusPointVO } from '@/api/iot/device/modbus/point' import { ModbusFunctionCodeOptions } from '@/views/iot/utils/constants' +import { DICT_TYPE } from '@/utils/dict' import DeviceModbusConfigForm from './DeviceModbusConfigForm.vue' import DeviceModbusPointForm from './DeviceModbusPointForm.vue' @@ -163,16 +165,7 @@ const props = defineProps<{ const message = useMessage() // ======================= 连接配置 ======================= -// TODO @AI:默认应该都是空的 -const modbusConfig = ref({ - deviceId: props.device.id, - ip: '', - port: 502, - slaveId: 1, - timeout: 3000, - retryInterval: 1000, - status: 0 // TODO @AI:使用 CommonStatus; -}) +const modbusConfig = ref({} as DeviceModbusConfigVO) /** 获取连接配置 */ const getModbusConfig = async () => { @@ -245,13 +238,16 @@ const handleEditPoint = (row: DeviceModbusPointVO) => { } /** 删除点位 */ -const handleDeletePoint = async (id: number) => { - // TODO @AI:最好点位的名字带上。参考别的模块; - // TODO @AI:参考别的注释。 - await message.confirm('确定要删除该点位配置吗?') - await DeviceModbusPointApi.deleteModbusPoint(id) - message.success('删除成功') - await getPointPage() +const handleDeletePoint = async (id: number, name: string) => { + try { + // 删除的二次确认 + await message.delConfirm('确定要删除点位【' + name + '】吗?') + // 发起删除 + await DeviceModbusPointApi.deleteModbusPoint(id) + message.success('删除成功') + // 刷新列表 + await getPointPage() + } catch {} } /** 初始化 */ diff --git a/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue b/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue index 0e9572a60..cb17230e0 100644 --- a/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue +++ b/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue @@ -11,9 +11,10 @@ - + - - + - -
单位:毫秒
-
- + - -
单位:毫秒
-
- + @@ -74,6 +69,7 @@ From 972eb47610f25d70f436f29cdae656b9ecedc864 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 18 Jan 2026 00:07:23 +0800 Subject: [PATCH 107/136] =?UTF-8?q?feat=EF=BC=9A=E3=80=90iot=E3=80=91modbu?= =?UTF-8?q?s-tcp=20=E5=8D=8F=E8=AE=AE=E6=8E=A5=E5=85=A5=20100%=EF=BC=9A?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E5=AE=9E=E7=8E=B0=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/iot/device/modbus/point/index.ts | 12 +-- .../device/detail/DeviceModbusConfig.vue | 28 ++++--- .../device/detail/DeviceModbusConfigForm.vue | 29 ++++--- .../device/detail/DeviceModbusPointForm.vue | 77 ++++++++++++------- 4 files changed, 87 insertions(+), 59 deletions(-) diff --git a/src/api/iot/device/modbus/point/index.ts b/src/api/iot/device/modbus/point/index.ts index 5dc05d1c1..0ae51bb94 100644 --- a/src/api/iot/device/modbus/point/index.ts +++ b/src/api/iot/device/modbus/point/index.ts @@ -4,14 +4,14 @@ import request from '@/config/axios' export interface DeviceModbusPointVO { id?: number // 主键 deviceId: number // 设备编号 - thingModelId: number // 物模型属性编号 + thingModelId?: number // 物模型属性编号 identifier: string // 属性标识符 name: string // 属性名称 - functionCode: number // Modbus 功能码 - registerAddress: number // 寄存器起始地址 - registerCount: number // 寄存器数量 - byteOrder: string // 字节序 - rawDataType: string // 原始数据类型 + functionCode?: number // Modbus 功能码 + registerAddress?: number // 寄存器起始地址 + registerCount?: number // 寄存器数量 + byteOrder?: string // 字节序 + rawDataType?: string // 原始数据类型 scale: number // 缩放因子 pollInterval: number // 轮询间隔,单位:毫秒 status: number // 状态 diff --git a/src/views/iot/device/device/detail/DeviceModbusConfig.vue b/src/views/iot/device/device/detail/DeviceModbusConfig.vue index 67fea1c28..f9682f040 100644 --- a/src/views/iot/device/device/detail/DeviceModbusConfig.vue +++ b/src/views/iot/device/device/detail/DeviceModbusConfig.vue @@ -5,7 +5,9 @@
连接配置 - 编辑 + + 编辑 +
@@ -26,9 +28,7 @@ {{ modbusConfig.retryInterval ? `${modbusConfig.retryInterval} ms` : '-' }} - - {{ modbusConfig.status === 0 ? '启用' : '禁用' }} - +
@@ -37,9 +37,8 @@
点位配置 - + - 新增点位
@@ -112,9 +111,20 @@ diff --git a/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue b/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue index cb17230e0..b59a7b507 100644 --- a/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue +++ b/src/views/iot/device/device/detail/DeviceModbusConfigForm.vue @@ -1,6 +1,6 @@ diff --git a/src/views/iot/device/device/detail/DeviceDetailsInfo.vue b/src/views/iot/device/device/detail/DeviceDetailsInfo.vue index a441093c3..b03c0d3e7 100644 --- a/src/views/iot/device/device/detail/DeviceDetailsInfo.vue +++ b/src/views/iot/device/device/detail/DeviceDetailsInfo.vue @@ -2,81 +2,45 @@ diff --git a/src/views/iot/device/device/detail/index.vue b/src/views/iot/device/device/detail/index.vue index 3ec756b5c..f8949ecd5 100644 --- a/src/views/iot/device/device/detail/index.vue +++ b/src/views/iot/device/device/detail/index.vue @@ -17,7 +17,13 @@ :thing-model-list="thingModelList" /> - + + + @@ -50,6 +56,7 @@ import DeviceDetailsThingModel from './DeviceDetailsThingModel.vue' import DeviceDetailsMessage from './DeviceDetailsMessage.vue' import DeviceDetailsSimulator from './DeviceDetailsSimulator.vue' import DeviceDetailConfig from './DeviceDetailConfig.vue' +import DeviceDetailsSubDevice from './DeviceDetailsSubDevice.vue' defineOptions({ name: 'IoTDeviceDetail' }) From 2076a27a262b9af9985e97c307c1679a9eb6a6e7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 22 Jan 2026 09:53:01 +0800 Subject: [PATCH 115/136] =?UTF-8?q?feat(iot):=E3=80=90=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=EF=BC=9A30%=E3=80=91=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BD=91=E5=85=B3=E8=AE=BE=E5=A4=87=E7=BB=91=E5=AE=9A=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=EF=BC=88=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=EF=BC=89?= =?UTF-8?q?=EF=BC=8C=E5=9F=BA=E4=BA=8E=20optimized-pondering-dragon.md=20?= =?UTF-8?q?=E8=A7=84=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/iot/device/device/index.ts | 9 +- src/api/iot/product/product/index.ts | 4 +- .../device/detail/DeviceDetailsSubDevice.vue | 136 ++++++++++++------ 3 files changed, 100 insertions(+), 49 deletions(-) diff --git a/src/api/iot/device/device/index.ts b/src/api/iot/device/device/index.ts index be5193c6d..d41429e0b 100644 --- a/src/api/iot/device/device/index.ts +++ b/src/api/iot/device/device/index.ts @@ -178,11 +178,8 @@ export const DeviceApi = { }) }, - // 获取可绑定到网关的子设备列表 - getBindableSubDeviceList: async (gatewayId?: number) => { - return await request.get({ - url: `/iot/device/bindable-sub-device-list`, - params: { gatewayId } - }) + // 获取未绑定网关的子设备分页 + getUnboundSubDevicePage: async (params: any) => { + return await request.get({ url: `/iot/device/unbound-sub-device-page`, params }) } } diff --git a/src/api/iot/product/product/index.ts b/src/api/iot/product/product/index.ts index a34efaeb9..d491d3b28 100644 --- a/src/api/iot/product/product/index.ts +++ b/src/api/iot/product/product/index.ts @@ -68,8 +68,8 @@ export const ProductApi = { }, // 查询产品(精简)列表 - getSimpleProductList() { - return request.get({ url: '/iot/product/simple-list' }) + getSimpleProductList(deviceType?: number) { + return request.get({ url: '/iot/product/simple-list', params: { deviceType } }) }, // 根据 ProductKey 获取产品信息 diff --git a/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue index 16a406c4f..d24ad2995 100644 --- a/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue +++ b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue @@ -62,40 +62,82 @@
- - - - - - - - - - - + + + + + + + + + + + + + 搜索 + + + 重置 + + + + + + + + + + + + + + + + + + + + + From 69d8224305b9eb17274acf5b64fdb7362833eb73 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 22 Jan 2026 09:55:00 +0800 Subject: [PATCH 116/136] =?UTF-8?q?feat(iot):=E3=80=90=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=EF=BC=9A30%=E3=80=91=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BD=91=E5=85=B3=E8=AE=BE=E5=A4=87=E7=BB=91=E5=AE=9A=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=EF=BC=88=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=EF=BC=89?= =?UTF-8?q?=EF=BC=8C=E5=9F=BA=E4=BA=8E=20optimized-pondering-dragon.md=20?= =?UTF-8?q?=E8=A7=84=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/components/ProductSelect.vue | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/views/iot/product/product/components/ProductSelect.vue diff --git a/src/views/iot/product/product/components/ProductSelect.vue b/src/views/iot/product/product/components/ProductSelect.vue new file mode 100644 index 000000000..fa288fa18 --- /dev/null +++ b/src/views/iot/product/product/components/ProductSelect.vue @@ -0,0 +1,65 @@ + + + + From dae0437ccd13a8a105bce8228ed44f58acaa3da4 Mon Sep 17 00:00:00 2001 From: LIUCX Date: Fri, 23 Jan 2026 11:06:34 +0800 Subject: [PATCH 117/136] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E7=99=BB=E9=99=86?= =?UTF-8?q?=E8=BF=87=E6=9C=9F=E5=90=8E=E5=8A=A0=E5=AF=86=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E4=BA=8C=E6=AC=A1=E5=8A=A0=E5=AF=86=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/axios/service.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/config/axios/service.ts b/src/config/axios/service.ts index 9214bf8ea..dfaea75d6 100644 --- a/src/config/axios/service.ts +++ b/src/config/axios/service.ts @@ -85,7 +85,7 @@ service.interceptors.request.use( } } // 是否 API 加密 - if ((config!.headers || {}).isEncrypt) { + if ((config!.headers || {}).isEncrypt && !(config!.headers || {}).isEncrypted) { try { // 加密请求数据 if (config.data) { @@ -169,6 +169,9 @@ service.interceptors.response.use( cb() }) requestList = [] + if ((config!.headers || {}).isEncrypt){ + (config!.headers || {}).isEncrypted = true + } return service(config) } catch (e) { // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。 From 2a0ce1fb66d1e47e15ccb3b0fb72b31d0929a88f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 25 Jan 2026 16:58:05 +0800 Subject: [PATCH 118/136] =?UTF-8?q?feat(iot):=E3=80=90=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=EF=BC=9A80%=E3=80=91=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E7=9A=84=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=EF=BC=88=E5=B7=B2=E6=B5=8B=E8=AF=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/iot/device/device/index.ts | 5 +- src/api/iot/product/product/index.ts | 2 + src/views/iot/device/device/DeviceForm.vue | 19 ------- .../device/detail/DeviceDetailsSubDevice.vue | 6 +-- src/views/iot/product/product/ProductForm.vue | 20 ++++++- .../product/detail/ProductDetailsInfo.vue | 35 ++++++++++++ src/views/iot/utils/constants.ts | 53 ++++++++++++++++++- 7 files changed, 112 insertions(+), 28 deletions(-) diff --git a/src/api/iot/device/device/index.ts b/src/api/iot/device/device/index.ts index d41429e0b..cf9f6d62d 100644 --- a/src/api/iot/device/device/index.ts +++ b/src/api/iot/device/device/index.ts @@ -21,7 +21,6 @@ export interface DeviceVO { mqttClientId: string // MQTT 客户端 ID mqttUsername: string // MQTT 用户名 mqttPassword: string // MQTT 密码 - authType: string // 认证类型 latitude?: number // 设备位置的纬度 longitude?: number // 设备位置的经度 areaId: number // 地区编码 @@ -161,12 +160,12 @@ export const DeviceApi = { }, // 绑定子设备到网关 - bindDeviceGateway: async (data: { ids: number[]; gatewayId: number }) => { + bindDeviceGateway: async (data: { subIds: number[]; gatewayId: number }) => { return await request.put({ url: `/iot/device/bind-gateway`, data }) }, // 解绑子设备与网关 - unbindDeviceGateway: async (data: { ids: number[] }) => { + unbindDeviceGateway: async (data: { subIds: number[]; gatewayId: number }) => { return await request.put({ url: `/iot/device/unbind-gateway`, data }) }, diff --git a/src/api/iot/product/product/index.ts b/src/api/iot/product/product/index.ts index d491d3b28..ba465efdc 100644 --- a/src/api/iot/product/product/index.ts +++ b/src/api/iot/product/product/index.ts @@ -5,6 +5,8 @@ export interface ProductVO { id: number // 产品编号 name: string // 产品名称 productKey: string // 产品标识 + productSecret?: string // 产品密钥 + registerEnabled?: boolean // 动态注册 protocolId: number // 协议编号 categoryId: number // 产品所属品类标识符 categoryName?: string // 产品所属品类名称 diff --git a/src/views/iot/device/device/DeviceForm.vue b/src/views/iot/device/device/DeviceForm.vue index 0ddbf2afb..fe75eed53 100644 --- a/src/views/iot/device/device/DeviceForm.vue +++ b/src/views/iot/device/device/DeviceForm.vue @@ -30,20 +30,6 @@ :disabled="formType === 'update'" />
- - - - - @@ -114,7 +100,6 @@ const formData = ref({ deviceName: undefined, nickname: undefined, picUrl: undefined, - gatewayId: undefined, deviceType: undefined as number | undefined, serialNumber: undefined, longitude: undefined as number | string | undefined, @@ -222,7 +207,6 @@ const formRules = reactive({ }) const formRef = ref() // 表单 Ref const products = ref([]) // 产品列表 -const gatewayDevices = ref([]) // 网关设备列表 const deviceGroups = ref([]) /** 打开弹窗 */ @@ -242,8 +226,6 @@ const open = async (type: string, id?: number) => { } } - // 加载网关设备列表 - gatewayDevices.value = await DeviceApi.getSimpleDeviceList(DeviceTypeEnum.GATEWAY) // 加载产品列表 products.value = await ProductApi.getSimpleProductList() // 加载设备分组列表 @@ -283,7 +265,6 @@ const resetForm = () => { deviceName: undefined, nickname: undefined, picUrl: undefined, - gatewayId: undefined, deviceType: undefined, serialNumber: undefined, longitude: undefined, diff --git a/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue index d24ad2995..290cd5cca 100644 --- a/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue +++ b/src/views/iot/device/device/detail/DeviceDetailsSubDevice.vue @@ -225,7 +225,7 @@ const handleBindSubmit = async () => { bindFormLoading.value = true try { await DeviceApi.bindDeviceGateway({ - ids: bindSelectedIds.value, + subIds: bindSelectedIds.value, gatewayId: props.gatewayId }) message.success('绑定成功') @@ -240,7 +240,7 @@ const handleBindSubmit = async () => { const handleUnbind = async (id: number) => { try { await message.confirm('确定要解绑该子设备吗?') - await DeviceApi.unbindDeviceGateway({ ids: [id] }) + await DeviceApi.unbindDeviceGateway({ subIds: [id], gatewayId: props.gatewayId }) message.success('解绑成功') await getSubDeviceList() } catch {} @@ -250,7 +250,7 @@ const handleUnbind = async (id: number) => { const handleUnbindBatch = async () => { try { await message.confirm(`确定要解绑选中的 ${selectedIds.value.length} 个子设备吗?`) - await DeviceApi.unbindDeviceGateway({ ids: selectedIds.value }) + await DeviceApi.unbindDeviceGateway({ subIds: selectedIds.value, gatewayId: props.gatewayId }) message.success('批量解绑成功') selectedIds.value = [] await getSubDeviceList() diff --git a/src/views/iot/product/product/ProductForm.vue b/src/views/iot/product/product/ProductForm.vue index 32a7b2221..9cb7c5099 100644 --- a/src/views/iot/product/product/ProductForm.vue +++ b/src/views/iot/product/product/ProductForm.vue @@ -75,6 +75,20 @@
+ + + + @@ -120,7 +134,8 @@ const formData = ref({ description: undefined, deviceType: undefined, netType: undefined, - codecType: CodecTypeEnum.ALINK + codecType: CodecTypeEnum.ALINK, + registerEnabled: false }) const formRules = reactive({ productKey: [{ required: true, message: 'ProductKey 不能为空', trigger: 'blur' }], @@ -194,7 +209,8 @@ const resetForm = () => { description: undefined, deviceType: undefined, netType: undefined, - codecType: CodecTypeEnum.ALINK + codecType: CodecTypeEnum.ALINK, + registerEnabled: false } formRef.value?.resetFields() } diff --git a/src/views/iot/product/product/detail/ProductDetailsInfo.vue b/src/views/iot/product/product/detail/ProductDetailsInfo.vue index feb7eb5dc..38c523b6e 100644 --- a/src/views/iot/product/product/detail/ProductDetailsInfo.vue +++ b/src/views/iot/product/product/detail/ProductDetailsInfo.vue @@ -21,6 +21,28 @@ > + + + {{ product.registerEnabled ? '已开启' : '已关闭' }} + + + +
+ {{ secretVisible ? product.productSecret : '******' }} + + + + + + +
+
{{ product.description }} @@ -29,6 +51,19 @@ import { DICT_TYPE } from '@/utils/dict' import { DeviceTypeEnum, ProductVO } from '@/api/iot/product/product' import { formatDate } from '@/utils/formatTime' +import { useClipboard } from '@vueuse/core' const { product } = defineProps<{ product: ProductVO }>() + +const message = useMessage() +const secretVisible = ref(false) +const { copy } = useClipboard() + +/** 复制产品密钥 */ +const copySecret = async () => { + if (product.productSecret) { + await copy(product.productSecret) + message.success('复制成功') + } +} diff --git a/src/views/iot/utils/constants.ts b/src/views/iot/utils/constants.ts index f25bb7de5..8b9c6722a 100644 --- a/src/views/iot/utils/constants.ts +++ b/src/views/iot/utils/constants.ts @@ -24,7 +24,41 @@ export const IotDeviceMessageMethodEnum = { // ========== 设备状态 ========== STATE_UPDATE: { method: 'thing.state.update', - name: '设备状态变更', + name: '设备状态更新', + upstream: true + }, + + // ========== 拓扑管理 ========== + TOPO_ADD: { + method: 'thing.topo.add', + name: '添加拓扑关系', + upstream: true + }, + TOPO_DELETE: { + method: 'thing.topo.delete', + name: '删除拓扑关系', + upstream: true + }, + TOPO_GET: { + method: 'thing.topo.get', + name: '获取拓扑关系', + upstream: true + }, + TOPO_CHANGE: { + method: 'thing.topo.change', + name: '拓扑关系变更通知', + upstream: false + }, + + // ========== 设备注册 ========== + DEVICE_REGISTER: { + method: 'thing.auth.register', + name: '设备动态注册', + upstream: true + }, + SUB_DEVICE_REGISTER: { + method: 'thing.auth.register.sub', + name: '子设备动态注册', upstream: true }, @@ -39,6 +73,11 @@ export const IotDeviceMessageMethodEnum = { name: '属性设置', upstream: false }, + PROPERTY_PACK_POST: { + method: 'thing.event.property.pack.post', + name: '批量上报(属性 + 事件 + 子设备)', + upstream: true + }, // ========== 设备事件 ========== EVENT_POST: { @@ -59,6 +98,18 @@ export const IotDeviceMessageMethodEnum = { method: 'thing.config.push', name: '配置推送', upstream: false + }, + + // ========== OTA 固件 ========== + OTA_UPGRADE: { + method: 'thing.ota.upgrade', + name: 'OTA 固件信息推送', + upstream: false + }, + OTA_PROGRESS: { + method: 'thing.ota.progress', + name: 'OTA 升级进度上报', + upstream: true } } From 38e9298fa6a04567d04e506f644e3a1297ac1da3 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 25 Jan 2026 17:41:47 +0800 Subject: [PATCH 119/136] =?UTF-8?q?feat(iot):=E3=80=90=E5=9C=BA=E6=99=AF?= =?UTF-8?q?=E8=81=94=E5=8A=A8=E3=80=91=E5=AE=9A=E6=97=B6=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E6=9D=A1=E4=BB=B6=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configs/TimerConditionGroupConfig.vue | 174 ++++++++++++++++++ .../scene/form/sections/TriggerSection.vue | 18 +- .../scene/form/selectors/PropertySelector.vue | 21 ++- 3 files changed, 207 insertions(+), 6 deletions(-) create mode 100644 src/views/iot/rule/scene/form/configs/TimerConditionGroupConfig.vue diff --git a/src/views/iot/rule/scene/form/configs/TimerConditionGroupConfig.vue b/src/views/iot/rule/scene/form/configs/TimerConditionGroupConfig.vue new file mode 100644 index 000000000..da9c3b3be --- /dev/null +++ b/src/views/iot/rule/scene/form/configs/TimerConditionGroupConfig.vue @@ -0,0 +1,174 @@ + + + + diff --git a/src/views/iot/rule/scene/form/sections/TriggerSection.vue b/src/views/iot/rule/scene/form/sections/TriggerSection.vue index 144d53c66..e3a4ecfa2 100644 --- a/src/views/iot/rule/scene/form/sections/TriggerSection.vue +++ b/src/views/iot/rule/scene/form/sections/TriggerSection.vue @@ -90,6 +90,12 @@ />
+ + + @@ -115,8 +121,9 @@ diff --git a/src/components/FormCreate/src/components/useApiSelect.tsx b/src/components/FormCreate/src/components/useApiSelect.tsx index 89a7e8dbb..25e9d70d2 100644 --- a/src/components/FormCreate/src/components/useApiSelect.tsx +++ b/src/components/FormCreate/src/components/useApiSelect.tsx @@ -2,6 +2,7 @@ import request from '@/config/axios' import { isEmpty } from '@/utils/is' import { ApiSelectProps } from '@/components/FormCreate/src/type' import { jsonParse } from '@/utils' +import { useUserStoreWithOut } from '@/store/modules/user' export const useApiSelect = (option: ApiSelectProps) => { return defineComponent({ @@ -61,13 +62,62 @@ export const useApiSelect = (option: ApiSelectProps) => { returnType: { type: String, default: 'id' + }, + // 是否默认选中当前用户(仅 UserSelect 使用) + defaultCurrentUser: { + type: Boolean, + default: false } }, - setup(props) { + setup(props, { emit }) { const attrs = useAttrs() const options = ref([]) // 下拉数据 const loading = ref(false) // 是否正在从远程获取数据 const queryParam = ref() // 当前输入的值 + + // 检查是否有有效的预设值 + const hasValidPresetValue = (): boolean => { + const value = attrs.modelValue + if (value === undefined || value === null || value === '') { + return false + } + if (Array.isArray(value)) { + return value.length > 0 + } + return true + } + + // 设置默认当前用户(仅当 defaultCurrentUser 为 true 且无预设值时) + const setDefaultCurrentUser = () => { + console.log('[UserSelect] setDefaultCurrentUser called, defaultCurrentUser:', props.defaultCurrentUser) + + // 仅当组件名为 UserSelect 且 defaultCurrentUser 为 true 时处理 + if (option.name !== 'UserSelect' || !props.defaultCurrentUser) { + console.log('[UserSelect] skip - not UserSelect or defaultCurrentUser is false') + return + } + + // 检查是否已有预设值(预设值优先级高于默认当前用户) + if (hasValidPresetValue()) { + console.log('[UserSelect] has preset value, skip:', attrs.modelValue) + return + } + + // 获取当前用户 ID + const userStore = useUserStoreWithOut() + const user = userStore.getUser + const currentUserId = user?.id + + console.log('[UserSelect] current user:', user, 'userId:', currentUserId) + + if (currentUserId) { + // 根据多选/单选模式设置默认值 + const defaultValue = props.multiple ? [currentUserId] : currentUserId + console.log('[UserSelect] setting default value:', defaultValue) + emit('update:modelValue', defaultValue) + } + } + const getOptions = async () => { options.value = [] // 接口选择器 @@ -188,6 +238,8 @@ export const useApiSelect = (option: ApiSelectProps) => { onMounted(async () => { await getOptions() + // 设置默认当前用户(在数据加载完成后) + setDefaultCurrentUser() }) const buildSelect = () => { diff --git a/src/components/FormCreate/src/config/useSelectRule.ts b/src/components/FormCreate/src/config/useSelectRule.ts index e1d77fbcc..c95e047db 100644 --- a/src/components/FormCreate/src/config/useSelectRule.ts +++ b/src/components/FormCreate/src/config/useSelectRule.ts @@ -19,13 +19,24 @@ export const useSelectRule = (option: SelectRuleOption) => { name, event: option.event, rule() { - return { + // 构建基础规则 + const baseRule: any = { type: name, field: generateUUID(), title: label, info: '', $required: false } + // 将自定义 props 的默认值添加到 rule 的 props 中 + if (option.props && option.props.length > 0) { + baseRule.props = {} + option.props.forEach((prop: any) => { + if (prop.field && prop.value !== undefined) { + baseRule.props[prop.field] = prop.value + } + }) + } + return baseRule }, props(_, { t }) { if (!option.props) { diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts index 4e87e432e..5769b4baa 100644 --- a/src/components/FormCreate/src/useFormCreateDesigner.ts +++ b/src/components/FormCreate/src/useFormCreateDesigner.ts @@ -52,7 +52,15 @@ export const useFormCreateDesigner = async (designer: Ref) => { const userSelectRule = useSelectRule({ name: 'UserSelect', label: '用户选择器', - icon: 'icon-user-o' + icon: 'icon-user-o', + props: [ + { + type: 'switch', + field: 'defaultCurrentUser', + title: '默认选中当前用户', + value: true + } + ] }) const deptSelectRule = useSelectRule({ name: 'DeptSelect', @@ -68,6 +76,12 @@ export const useFormCreateDesigner = async (designer: Ref) => { { label: '部门编号', value: 'id' }, { label: '部门名称', value: 'name' } ] + }, + { + type: 'switch', + field: 'defaultCurrentDept', + title: '默认选中当前部门', + value: true } ] }) diff --git a/src/plugins/formCreate/index.ts b/src/plugins/formCreate/index.ts index 01a57bebc..6e0a971fe 100644 --- a/src/plugins/formCreate/index.ts +++ b/src/plugins/formCreate/index.ts @@ -68,6 +68,7 @@ import { UploadFile, UploadImg, UploadImgs } from '@/components/UploadFile' import { useApiSelect } from '@/components/FormCreate' import { Editor } from '@/components/Editor' import DictSelect from '@/components/FormCreate/src/components/DictSelect.vue' +import DeptSelect from '@/components/FormCreate/src/components/DeptSelect.vue' const UserSelect = useApiSelect({ name: 'UserSelect', @@ -75,12 +76,6 @@ const UserSelect = useApiSelect({ valueField: 'id', url: '/system/user/simple-list' }) -const DeptSelect = useApiSelect({ - name: 'DeptSelect', - labelField: 'name', - valueField: 'id', - url: '/system/dept/simple-list' -}) const ApiSelect = useApiSelect({ name: 'ApiSelect' }) From c656f8757585e11baa44b146bb9850305115c0c1 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 28 Jan 2026 11:06:59 +0800 Subject: [PATCH 121/136] =?UTF-8?q?fix(mall):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=95=86=E5=93=81=20SKU=20=E5=90=8D=E7=A7=B0=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mall/product/spu/components/SkuList.vue | 3 +++ src/views/mall/product/spu/form/SkuForm.vue | 1 + src/views/mall/product/spu/form/index.vue | 9 ++++++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/views/mall/product/spu/components/SkuList.vue b/src/views/mall/product/spu/components/SkuList.vue index c41da4b2c..10d82319f 100644 --- a/src/views/mall/product/spu/components/SkuList.vue +++ b/src/views/mall/product/spu/components/SkuList.vue @@ -318,6 +318,7 @@ const props = defineProps({ const formData: Ref = ref() // 表单数据 const skuList = ref([ { + name: '', // SKU 名称 price: 0, // 商品价格 marketPrice: 0, // 市场价 costPrice: 0, // 成本价 @@ -449,6 +450,7 @@ const generateTableData = (propertyList: any[]) => { } for (const item of buildSkuList) { const row = { + name: '', // SKU 名称,提交时会自动使用 SPU 名称 properties: Array.isArray(item) ? item : [item], // 如果只有一个属性的话返回的是一个 property 对象 price: 0, marketPrice: 0, @@ -525,6 +527,7 @@ watch( if (props.isBatch) { skuList.value = [ { + name: '', // SKU 名称 price: 0, marketPrice: 0, costPrice: 0, diff --git a/src/views/mall/product/spu/form/SkuForm.vue b/src/views/mall/product/spu/form/SkuForm.vue index 18cd0296f..782d8ed48 100644 --- a/src/views/mall/product/spu/form/SkuForm.vue +++ b/src/views/mall/product/spu/form/SkuForm.vue @@ -173,6 +173,7 @@ const onChangeSpec = () => { // 重置sku列表 formData.skus = [ { + name: '', // SKU 名称,提交时会自动使用 SPU 名称 price: 0, marketPrice: 0, costPrice: 0, diff --git a/src/views/mall/product/spu/form/index.vue b/src/views/mall/product/spu/form/index.vue index c4e4b7b2d..bf4aa9341 100644 --- a/src/views/mall/product/spu/form/index.vue +++ b/src/views/mall/product/spu/form/index.vue @@ -62,6 +62,7 @@ import OtherForm from './OtherForm.vue' import SkuForm from './SkuForm.vue' import DeliveryForm from './DeliveryForm.vue' import { convertToInteger, floatToFixed2, formatToFraction } from '@/utils' +import { isEmpty } from '@/utils/is' defineOptions({ name: 'ProductSpuAdd' }) @@ -94,6 +95,7 @@ const formData = ref({ subCommissionType: false, // 分销类型 skus: [ { + name: '', // SKU 名称,提交时会自动使用 SPU 名称 price: 0, // 商品价格 marketPrice: 0, // 市场价 costPrice: 0, // 成本价 @@ -158,8 +160,13 @@ const submitForm = async () => { await unref(otherRef)?.validate() // 深拷贝一份, 这样最终 server 端不满足,不需要影响原始数据 const deepCopyFormData = cloneDeep(unref(formData.value)) as ProductSpuApi.Spu + // 校验商品名称不能为空(用于 SKU name) + if (isEmpty(deepCopyFormData.name)) { + message.error('商品名称不能为空') + return + } deepCopyFormData.skus!.forEach((item) => { - // 给sku name赋值 + // 给sku name赋值(使用商品名称作为 SKU 名称) item.name = deepCopyFormData.name // sku相关价格元转分 item.price = convertToInteger(item.price) From 4c5825389ea4fc750e2f09d68fd4f3e0d9da7800 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 29 Jan 2026 19:39:19 +0800 Subject: [PATCH 122/136] feat(iot): refactor TimerConditionGroupConfig and PropertySelector for improved readability --- .../form/configs/TimerConditionGroupConfig.vue | 17 +++-------------- .../scene/form/selectors/PropertySelector.vue | 4 ++-- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/views/iot/rule/scene/form/configs/TimerConditionGroupConfig.vue b/src/views/iot/rule/scene/form/configs/TimerConditionGroupConfig.vue index da9c3b3be..bd52d2e42 100644 --- a/src/views/iot/rule/scene/form/configs/TimerConditionGroupConfig.vue +++ b/src/views/iot/rule/scene/form/configs/TimerConditionGroupConfig.vue @@ -15,9 +15,7 @@ 附加条件组 定时触发时需满足以下条件 - - {{ conditionGroups?.length || 0 }} 个子条件组 - + {{ conditionGroups?.length || 0 }} 个子条件组 { if (!conditionGroups.value) { conditionGroups.value = [] } - // 检查是否达到最大条件组数量限制 if (conditionGroups.value.length >= maxGroups) { return } - // 使用 nextTick 确保响应式更新完成后再添加新的条件组 await nextTick() if (conditionGroups.value) { @@ -151,21 +147,14 @@ const addConditionGroup = async () => { } } -/** - * 移除条件组 - * @param index 条件组索引 - */ +/** 移除条件组 */ const removeConditionGroup = (index: number) => { if (conditionGroups.value) { conditionGroups.value.splice(index, 1) } } -/** - * 更新条件组 - * @param index 条件组索引 - * @param group 条件组数据 - */ +/** 更新条件组 */ const updateConditionGroup = (index: number, group: TriggerCondition[]) => { if (conditionGroups.value) { conditionGroups.value[index] = group diff --git a/src/views/iot/rule/scene/form/selectors/PropertySelector.vue b/src/views/iot/rule/scene/form/selectors/PropertySelector.vue index 66fb7b109..861f98a6b 100644 --- a/src/views/iot/rule/scene/form/selectors/PropertySelector.vue +++ b/src/views/iot/rule/scene/form/selectors/PropertySelector.vue @@ -219,13 +219,13 @@ const thingModelTSL = ref(null) // 物模型TSL数 const propertyGroups = computed(() => { const groups: { label: string; options: any[] }[] = [] - // 设备属性上报触发器 或 定时触发器(条件组中的设备属性条件) + // 设备属性上报触发器、定时触发器(条件组中的设备属性条件) if ( props.triggerType === IotRuleSceneTriggerTypeEnum.DEVICE_PROPERTY_POST || props.triggerType === IotRuleSceneTriggerTypeEnum.TIMER ) { const propertyOptions = propertyList.value.filter( - (p) => p.type === IoTThingModelTypeEnum.PROPERTY + (property) => property.type === IoTThingModelTypeEnum.PROPERTY ) if (propertyOptions.length > 0) { groups.push({ From 0ee0065c6af1d007df7b8bff33605a5703bfcf93 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 29 Jan 2026 20:18:00 +0800 Subject: [PATCH 123/136] feat: update default selection settings for current user and department --- .../FormCreate/src/components/useApiSelect.tsx | 11 +---------- .../FormCreate/src/useFormCreateDesigner.ts | 4 ++-- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/components/FormCreate/src/components/useApiSelect.tsx b/src/components/FormCreate/src/components/useApiSelect.tsx index 25e9d70d2..847eb077c 100644 --- a/src/components/FormCreate/src/components/useApiSelect.tsx +++ b/src/components/FormCreate/src/components/useApiSelect.tsx @@ -89,31 +89,22 @@ export const useApiSelect = (option: ApiSelectProps) => { // 设置默认当前用户(仅当 defaultCurrentUser 为 true 且无预设值时) const setDefaultCurrentUser = () => { - console.log('[UserSelect] setDefaultCurrentUser called, defaultCurrentUser:', props.defaultCurrentUser) - // 仅当组件名为 UserSelect 且 defaultCurrentUser 为 true 时处理 if (option.name !== 'UserSelect' || !props.defaultCurrentUser) { - console.log('[UserSelect] skip - not UserSelect or defaultCurrentUser is false') return } - // 检查是否已有预设值(预设值优先级高于默认当前用户) if (hasValidPresetValue()) { - console.log('[UserSelect] has preset value, skip:', attrs.modelValue) return } - + // 获取当前用户 ID const userStore = useUserStoreWithOut() const user = userStore.getUser const currentUserId = user?.id - - console.log('[UserSelect] current user:', user, 'userId:', currentUserId) - if (currentUserId) { // 根据多选/单选模式设置默认值 const defaultValue = props.multiple ? [currentUserId] : currentUserId - console.log('[UserSelect] setting default value:', defaultValue) emit('update:modelValue', defaultValue) } } diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts index 5769b4baa..0f528838e 100644 --- a/src/components/FormCreate/src/useFormCreateDesigner.ts +++ b/src/components/FormCreate/src/useFormCreateDesigner.ts @@ -58,7 +58,7 @@ export const useFormCreateDesigner = async (designer: Ref) => { type: 'switch', field: 'defaultCurrentUser', title: '默认选中当前用户', - value: true + value: false } ] }) @@ -81,7 +81,7 @@ export const useFormCreateDesigner = async (designer: Ref) => { type: 'switch', field: 'defaultCurrentDept', title: '默认选中当前部门', - value: true + value: false } ] }) From 04a69872113b23126849c2e60b3ff508cdba200d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 29 Jan 2026 23:49:33 +0800 Subject: [PATCH 124/136] =?UTF-8?q?(=E3=80=83'=E2=96=BD'=E3=80=83)=20v2026?= =?UTF-8?q?.01=20=E5=8F=91=E5=B8=83=EF=BC=9A=E5=A4=A7=E5=A4=A7=E5=A4=A7?= =?UTF-8?q?=E5=A4=A7=E5=AE=8C=E5=96=84=20vben5=20=E7=9A=84=20antd=E3=80=81?= =?UTF-8?q?vben=20=E7=89=88=E6=9C=AC=E7=9A=84=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20IoT=20=E5=90=84=E7=A7=8D=E6=8E=A5=E5=85=A5?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1a12256e9..8ef3b4ee6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yudao-ui-admin-vue3", - "version": "2025.12-snapshot", + "version": "2026.01-snapshot", "description": "基于vue3、vite4、element-plus、typesScript", "author": "xingyu", "private": false, From 57a77b2ba9c55ab8e8b15574ee56c103561de449 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 4 Feb 2026 22:05:03 +0800 Subject: [PATCH 125/136] =?UTF-8?q?feat=EF=BC=88iot=EF=BC=89=EF=BC=9A?= =?UTF-8?q?=E3=80=90=E5=8D=8F=E8=AE=AE=E6=94=B9=E9=80=A0=E3=80=91=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=20codecType=EF=BC=8C=E4=BD=BF=E7=94=A8=20protocolType?= =?UTF-8?q?=20=E6=9B=BF=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/iot/product/product/index.ts | 22 +++++-- src/utils/dict.ts | 3 +- src/views/iot/product/product/ProductForm.vue | 61 +++++++++++++------ .../product/detail/ProductDetailsInfo.vue | 7 ++- 4 files changed, 68 insertions(+), 25 deletions(-) diff --git a/src/api/iot/product/product/index.ts b/src/api/iot/product/product/index.ts index ba465efdc..2025884b7 100644 --- a/src/api/iot/product/product/index.ts +++ b/src/api/iot/product/product/index.ts @@ -16,7 +16,8 @@ export interface ProductVO { status: number // 产品状态 deviceType: number // 设备类型 netType: number // 联网方式 - codecType: string // 数据格式(编解码器类型) + protocolType: string // 协议类型 + serializeType: string // 序列化类型 deviceCount: number // 设备数量 createTime: Date // 创建时间 } @@ -27,9 +28,22 @@ export enum DeviceTypeEnum { GATEWAY_SUB = 1, // 网关子设备 GATEWAY = 2 // 网关设备 } -// IOT 数据格式(编解码器类型)枚举类 -export enum CodecTypeEnum { - ALINK = 'Alink' // 阿里云 Alink 协议 +// IoT 协议类型枚举 +export enum ProtocolTypeEnum { + TCP = 'tcp', + UDP = 'udp', + WEBSOCKET = 'websocket', + HTTP = 'http', + MQTT = 'mqtt', + EMQX = 'emqx', + COAP = 'coap', + MODBUS_TCP = 'modbus_tcp' +} + +// IoT 序列化类型枚举 +export enum SerializeTypeEnum { + JSON = 'json', + BINARY = 'binary' } // IoT 产品 API diff --git a/src/utils/dict.ts b/src/utils/dict.ts index b4f0c5870..e68f1384d 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -233,7 +233,8 @@ export enum DICT_TYPE { IOT_NET_TYPE = 'iot_net_type', // IOT 联网方式 IOT_PRODUCT_STATUS = 'iot_product_status', // IOT 产品状态 IOT_PRODUCT_DEVICE_TYPE = 'iot_product_device_type', // IOT 产品设备类型 - IOT_CODEC_TYPE = 'iot_codec_type', // IOT 数据格式(编解码器类型) + IOT_PROTOCOL_TYPE = 'iot_protocol_type', // IOT 协议类型 + IOT_SERIALIZE_TYPE = 'iot_serialize_type', // IOT 序列化类型 IOT_LOCATION_TYPE = 'iot_location_type', // IOT 定位类型 IOT_DEVICE_STATE = 'iot_device_state', // IOT 设备状态 IOT_THING_MODEL_TYPE = 'iot_thing_model_type', // IOT 产品功能类型 diff --git a/src/views/iot/product/product/ProductForm.vue b/src/views/iot/product/product/ProductForm.vue index 9cb7c5099..7bc3e4903 100644 --- a/src/views/iot/product/product/ProductForm.vue +++ b/src/views/iot/product/product/ProductForm.vue @@ -4,7 +4,7 @@ ref="formRef" :model="formData" :rules="formRules" - label-width="110px" + label-width="120px" v-loading="formLoading" > @@ -49,11 +49,7 @@ label="联网方式" prop="netType" > - + - - - + + + + + + + + + @@ -109,7 +125,13 @@ diff --git a/src/components/FormCreate/src/components/IframeComponent.vue b/src/components/FormCreate/src/components/IframeComponent.vue new file mode 100644 index 000000000..49ac1a883 --- /dev/null +++ b/src/components/FormCreate/src/components/IframeComponent.vue @@ -0,0 +1,102 @@ + + + + + + diff --git a/src/components/FormCreate/src/config/index.ts b/src/components/FormCreate/src/config/index.ts index b1e2ddea5..d4d384c31 100644 --- a/src/components/FormCreate/src/config/index.ts +++ b/src/components/FormCreate/src/config/index.ts @@ -4,6 +4,8 @@ import { useUploadImgsRule } from './useUploadImgsRule' import { useDictSelectRule } from './useDictSelectRule' import { useEditorRule } from './useEditorRule' import { useSelectRule } from './useSelectRule' +import { useIframeRule } from './useIframeRule' +import { useAreaSelectRule } from './useAreaSelectRule' export { useUploadFileRule, @@ -11,5 +13,7 @@ export { useUploadImgsRule, useDictSelectRule, useEditorRule, - useSelectRule + useSelectRule, + useIframeRule, + useAreaSelectRule } diff --git a/src/components/FormCreate/src/config/useAreaSelectRule.ts b/src/components/FormCreate/src/config/useAreaSelectRule.ts new file mode 100644 index 000000000..59405da6a --- /dev/null +++ b/src/components/FormCreate/src/config/useAreaSelectRule.ts @@ -0,0 +1,74 @@ +import { generateUUID } from '@/utils' +import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils' + +/** + * 省市区选择器规则 + */ +export const useAreaSelectRule = () => { + const label = '省市区选择器' + const name = 'AreaSelect' + + return { + icon: 'icon-location', + label, + name, + rule() { + return { + type: name, + field: generateUUID(), + title: label, + info: '', + $required: false + } + }, + props(_, { t }) { + return localeProps(t, name + '.props', [ + makeRequiredRule(), + { + type: 'select', + field: 'level', + title: '选择层级', + value: 3, + options: [ + { label: '省', value: 1 }, + { label: '省/市', value: 2 }, + { label: '省/市/区', value: 3 } + ], + info: '限制可选择的地区层级' + }, + { + type: 'input', + field: 'placeholder', + title: '占位符', + value: '请选择省市区' + }, + { + type: 'switch', + field: 'clearable', + title: '是否可清空', + value: true + }, + { + type: 'switch', + field: 'showAllLevels', + title: '显示完整路径', + value: true, + info: '输入框中是否显示选中值的完整路径' + }, + { + type: 'input', + field: 'separator', + title: '分隔符', + value: '/', + info: '选项分隔符' + }, + { + type: 'switch', + field: 'disabled', + title: '是否禁用', + value: false + } + ]) + } + } +} diff --git a/src/components/FormCreate/src/config/useIframeRule.ts b/src/components/FormCreate/src/config/useIframeRule.ts new file mode 100644 index 000000000..5a3e47871 --- /dev/null +++ b/src/components/FormCreate/src/config/useIframeRule.ts @@ -0,0 +1,74 @@ +import { generateUUID } from '@/utils' +import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils' + +/** + * iframe 组件规则 + */ +export const useIframeRule = () => { + const label = '网页 iframe' + const name = 'IframeComponent' + + return { + icon: 'icon-link', + label, + name, + rule() { + return { + type: name, + field: generateUUID(), + title: label, + info: '', + $required: false + } + }, + props(_, { t }) { + return localeProps(t, name + '.props', [ + makeRequiredRule(), + { + type: 'input', + field: 'url', + title: 'URL 地址', + value: '', + info: '请输入完整的 HTTP 或 HTTPS 地址' + }, + { + type: 'input', + field: 'height', + title: 'iframe 高度', + value: '500px', + info: '支持 px、%、vh 等单位' + }, + { + type: 'input', + field: 'width', + title: 'iframe 宽度', + value: '100%', + info: '支持 px、%、vw 等单位' + }, + { + type: 'select', + field: 'loading', + title: '加载方式', + value: 'lazy', + options: [ + { label: '懒加载', value: 'lazy' }, + { label: '立即加载', value: 'eager' } + ] + }, + { + type: 'switch', + field: 'allowfullscreen', + title: '允许全屏', + value: true + }, + { + type: 'input', + field: 'sandbox', + title: 'sandbox 属性', + value: '', + info: '安全沙箱限制,如:allow-scripts allow-same-origin' + } + ]) + } + } +} diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts index 5769b4baa..9658f8b76 100644 --- a/src/components/FormCreate/src/useFormCreateDesigner.ts +++ b/src/components/FormCreate/src/useFormCreateDesigner.ts @@ -4,7 +4,9 @@ import { useSelectRule, useUploadFileRule, useUploadImgRule, - useUploadImgsRule + useUploadImgsRule, + useIframeRule, + useAreaSelectRule } from './config' import { Ref } from 'vue' import { Menu } from '@/components/FormCreate/src/type' @@ -36,7 +38,9 @@ export const useFormCreateDesigner = async (designer: Ref) => { designer.value?.removeMenuItem('upload') // 移除自带的富文本组件规则,使用 editorRule 替代 designer.value?.removeMenuItem('fcEditor') - const components = [editorRule, uploadFileRule, uploadImgRule, uploadImgsRule] + const iframeRule = useIframeRule() + const areaSelectRule = useAreaSelectRule() + const components = [editorRule, uploadFileRule, uploadImgRule, uploadImgsRule, iframeRule, areaSelectRule] components.forEach((component) => { // 插入组件规则 designer.value?.addComponent(component) diff --git a/src/plugins/formCreate/index.ts b/src/plugins/formCreate/index.ts index 6e0a971fe..425930509 100644 --- a/src/plugins/formCreate/index.ts +++ b/src/plugins/formCreate/index.ts @@ -69,6 +69,8 @@ import { useApiSelect } from '@/components/FormCreate' import { Editor } from '@/components/Editor' import DictSelect from '@/components/FormCreate/src/components/DictSelect.vue' import DeptSelect from '@/components/FormCreate/src/components/DeptSelect.vue' +import IframeComponent from '@/components/FormCreate/src/components/IframeComponent.vue' +import AreaSelect from '@/components/FormCreate/src/components/AreaSelect.vue' const UserSelect = useApiSelect({ name: 'UserSelect', @@ -114,6 +116,8 @@ const components = [ DeptSelect, ApiSelect, Editor, + IframeComponent, + AreaSelect, ElCollapse, ElCollapseItem, ElCard From c153ff93c73a305637828b0a3b83906ece6e9067 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 8 Feb 2026 14:37:55 +0800 Subject: [PATCH 130/136] =?UTF-8?q?review=EF=BC=9A=E2=80=9C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20iframe=20=E5=92=8C=E7=9C=81=E5=B8=82=E5=8C=BA?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E5=99=A8=E7=BB=84=E4=BB=B6=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FormCreate/src/components/AreaSelect.vue | 39 ++++++++----------- .../src/components/IframeComponent.vue | 12 +++--- .../src/config/useAreaSelectRule.ts | 1 - .../FormCreate/src/config/useIframeRule.ts | 1 - 4 files changed, 22 insertions(+), 31 deletions(-) diff --git a/src/components/FormCreate/src/components/AreaSelect.vue b/src/components/FormCreate/src/components/AreaSelect.vue index 32ef3296a..1633d7a9d 100644 --- a/src/components/FormCreate/src/components/AreaSelect.vue +++ b/src/components/FormCreate/src/components/AreaSelect.vue @@ -18,6 +18,7 @@