From ee84487fe07763fefa2feff8483cfff7a6b47b04 Mon Sep 17 00:00:00 2001 From: chenhao Date: Thu, 21 May 2026 19:57:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E4=BA=A7=E5=93=81=E5=8C=B9=E9=85=8D=E5=92=8C=E5=BA=93=E5=AD=98?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 `ProjectOrderInfoServiceImpl` 中,更新 `selectProductMatchList` 方法,增加绑定数量计算 - 修改 `selectPurchaseSnList` 方法,直接使用 `snList.size()` 计算总数 - 更新 `resetPwd.vue` 密码验证规则,增强密码强度要求 - 注释掉 `SysLoginController` 中的密码强度检查逻辑 - 优化 `ProjectOrderInfoMapper.xml` 查询语句,减少子查询 - 在 `index.vue` 中,增加缺货提示,并优化配额量输入处理 - 更新 `VendorInfoMapper.xml` 的 SQL 语句格式 --- .../src/views/inventory/execution/index.vue | 193 ++++++++++++++++-- .../views/system/user/profile/resetPwd.vue | 6 +- .../controller/system/SysLoginController.java | 10 +- .../impl/ProjectOrderInfoServiceImpl.java | 19 +- .../mapper/sip/ProjectOrderInfoMapper.xml | 15 +- .../mapper/system/VendorInfoMapper.xml | 2 +- 6 files changed, 206 insertions(+), 39 deletions(-) diff --git a/oms_web/oms_vue/src/views/inventory/execution/index.vue b/oms_web/oms_vue/src/views/inventory/execution/index.vue index 4692b966..67470eb2 100644 --- a/oms_web/oms_vue/src/views/inventory/execution/index.vue +++ b/oms_web/oms_vue/src/views/inventory/execution/index.vue @@ -252,10 +252,12 @@
已配货: {{ item.phNum !== undefined && item.phNum !== null ? item.phNum : '-' }} + 缺货: {{ item.orderNum !== undefined && item.orderNum !== null ? item.orderNum-(item.phNum||0): '-'}}
已备货: {{ item.bhNum !== undefined && item.bhNum !== null ? item.bhNum : '-' }} + 缺货: {{ item.orderNum !== undefined && item.orderNum !== null ? item.orderNum-(item.bhNum||0): '-'}}
@@ -263,18 +265,23 @@ - + + + @@ -684,6 +691,7 @@ export default { const list = response.data || []; return Promise.all(list.map(item => this.loadBindPageData(row.orderCode, { ...item, + originalTotalPhNum: item.phNum, bindList: [], bindTotal: 0, bindPageNum: 1, @@ -716,10 +724,12 @@ export default { const bindList = (bindResponse.rows || []).map(bindItem => ({ ...bindItem, phNum: this.getDraftBindNum(bindItem.purchaseId, bindItem.phNum), - originalPhNum: bindItem.originalPhNum !== undefined ? bindItem.originalPhNum : bindItem.phNum + originalPhNum: bindItem.originalPhNum !== undefined ? bindItem.originalPhNum : bindItem.phNum, + originalKyNum: bindItem.originalKyNum !== undefined ? bindItem.originalKyNum : bindItem.kyNum })); return { ...item, + originalTotalPhNum: item.originalTotalPhNum !== undefined ? item.originalTotalPhNum : item.phNum, bindList, bindTotal: bindResponse.total || 0 }; @@ -744,32 +754,57 @@ export default { this.stockingDetailLoading = false; }); }, - handleBindNumChange(row) { + handleBindNumChange(row, item) { const rawValue = row.phNum === '' || row.phNum === null || row.phNum === undefined ? 0 : Number(row.phNum); const cgNum = row.cgNum === '' || row.cgNum === null || row.cgNum === undefined ? 0 : Number(row.cgNum); - const kyNum = row.kyNum === '' || row.kyNum === null || row.kyNum === undefined ? 0 : Number(row.kyNum); + const originalKyNum = row.originalKyNum === '' || row.originalKyNum === null || row.originalKyNum === undefined ? 0 : Number(row.originalKyNum); const bhNum = row.bhNum === '' || row.bhNum === null || row.bhNum === undefined ? 0 : Number(row.bhNum); const originalPhNum = row.originalPhNum === '' || row.originalPhNum === null || row.originalPhNum === undefined ? 0 : Number(row.originalPhNum); + + const orderNum = item ? (item.orderNum === '' || item.orderNum === null || item.orderNum === undefined ? 0 : Number(item.orderNum)) : 0; + const originalTotalPhNum = item ? (item.originalTotalPhNum === '' || item.originalTotalPhNum === null || item.originalTotalPhNum === undefined ? 0 : Number(item.originalTotalPhNum)) : 0; + let nextValue = Math.max(rawValue, bhNum); - nextValue = Math.min(nextValue, cgNum); - const increasedValue = nextValue - originalPhNum; - if (increasedValue > kyNum) { - nextValue = originalPhNum + kyNum; + + const maxPhysical = originalPhNum + originalKyNum; + + let otherRowsDiffSum = 0; + if (item && item.bindList) { + item.bindList.forEach(b => { + if (b.purchaseId !== row.purchaseId) { + const p = Number(b.phNum) || 0; + const op = Number(b.originalPhNum) || 0; + otherRowsDiffSum += (p - op); + } + }); + } + + const maxAllowedDiff = orderNum - originalTotalPhNum - otherRowsDiffSum; + const maxAllowedByOrder = originalPhNum + maxAllowedDiff; + + let upperLimit = Math.min(maxPhysical, maxAllowedByOrder); + + if (nextValue > upperLimit) { + nextValue = upperLimit; } if (nextValue < bhNum) { nextValue = bhNum; } - if (nextValue > cgNum) { - nextValue = cgNum; + if (nextValue < 0) { + nextValue = 0; } + row.phNum = nextValue; - this.$set(this.stockingBindDraftMap, row.purchaseId, nextValue); - }, + + const diff = nextValue - originalPhNum; + row.kyNum = originalKyNum - diff; + + this.$set(this.stockingBindDraftMap, row.purchaseId, nextValue); }, handleSaveStockingBind() { const changedList = []; this.stockingDetailList.forEach(item => { (item.bindList || []).forEach(bindItem => { - this.handleBindNumChange(bindItem); + this.handleBindNumChange(bindItem, item); const currentPhNum = bindItem.phNum === '' || bindItem.phNum === null || bindItem.phNum === undefined ? 0 : Number(bindItem.phNum); const originalPhNum = bindItem.originalPhNum === '' || bindItem.originalPhNum === null || bindItem.originalPhNum === undefined ? 0 : Number(bindItem.originalPhNum); if (currentPhNum !== originalPhNum) { @@ -799,8 +834,10 @@ export default { }))).then(() => { this.$message.success('保存成功'); this.stockingDetailList.forEach(item => { + item.originalTotalPhNum = item.phNum; (item.bindList || []).forEach(bindItem => { bindItem.originalPhNum = bindItem.phNum; + bindItem.originalKyNum = bindItem.kyNum; this.$delete(this.stockingBindDraftMap, bindItem.purchaseId); }); }); @@ -836,17 +873,29 @@ export default { const data = response.data || {}; this.outerSnDetail = { totalQuantity: data.totalQuantity || 0, - quotaQuantity: data.quotaQuantity || 0, + quotaQuantity: row.phNum|| data.quotaQuantity || 0, warehouseList: data.warehouseList || [], snList: data.snList || [] }; if (this.outerSnDetail.warehouseList.length > 0) { this.outerSnActiveWarehouse = this.outerSnDetail.warehouseList[0].warehouseName; } - this.outerSnSelectedList = this.outerSnDetail.snList + + const alreadyBoundSns = this.outerSnDetail.snList .filter(sn => sn.orderCode && sn.orderCode === this.stockingDetailRow.orderCode) - .map(sn => sn.productSn) - .slice(0, this.outerSnQuotaQuantity); + .map(sn => sn.productSn); + const availableSns = this.outerSnDetail.snList + .filter(sn => !this.isSnDisabled(sn)) + .map(sn => sn.productSn); + + let initialSelected = [...alreadyBoundSns]; + if (initialSelected.length < this.outerSnQuotaQuantity) { + const needCount = this.outerSnQuotaQuantity - initialSelected.length; + const remainingToSelect = availableSns.filter(sn => !initialSelected.includes(sn)).slice(0, needCount); + initialSelected = initialSelected.concat(remainingToSelect); + } + + this.outerSnSelectedList = initialSelected.slice(0, this.outerSnQuotaQuantity); this.updateSelectAllState(); }).finally(() => { this.outerSnLoading = false; @@ -949,14 +998,53 @@ export default { }).then(() => { const loading = this.$loading({ lock: true, - text: '正在分配SN码...', + text: '正在处理中...', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' }); - bindOrderSnCodes(this.stockingDetailRow.orderCode, this.outerSnProductRow.productCode, this.outerSnSelectedList).then(response => { + + // 收集所有变更的配额信息 + const changedList = []; + this.stockingDetailList.forEach(item => { + (item.bindList || []).forEach(bindItem => { + const currentPhNum = bindItem.phNum === '' || bindItem.phNum === null || bindItem.phNum === undefined ? 0 : Number(bindItem.phNum); + const originalPhNum = bindItem.originalPhNum === '' || bindItem.originalPhNum === null || bindItem.originalPhNum === undefined ? 0 : Number(bindItem.originalPhNum); + if (currentPhNum !== originalPhNum) { + changedList.push({ + orderId: this.stockingDetailRow.id, + purchaseId: bindItem.purchaseId, + purchaseNo: bindItem.purchaseNo, + originalBindNum: originalPhNum, + bindNum: currentPhNum, + bindItem: bindItem + }); + } + }); + }); + + let saveQuotaPromise = Promise.resolve(); + if (changedList.length > 0) { + saveQuotaPromise = Promise.all(changedList.map(item => savePurchaseOrderMap({ + orderId: item.orderId, + purchaseId: item.purchaseId, + bindNum: item.bindNum + }))); + } + + saveQuotaPromise.then(() => { + return bindOrderSnCodes(this.stockingDetailRow.orderCode, this.outerSnProductRow.productCode, this.outerSnSelectedList); + }).then(response => { loading.close(); - this.$message.success(`成功分配 ${this.outerSnSelectedList.length} 个SN码`); + this.$message.success(`成功分配 ${this.outerSnSelectedList.length} 个SN码` + (changedList.length > 0 ? ',并同步保存了配额修改' : '')); this.outerSnDialogVisible = false; + + if (changedList.length > 0) { + changedList.forEach(item => { + item.bindItem.originalPhNum = item.bindNum; + this.$delete(this.stockingBindDraftMap, item.purchaseId); + }); + } + this.handleShowStockingDetail(this.stockingDetailRow); this.getList(); }).catch(() => { @@ -1080,6 +1168,15 @@ export default { color: #19be6b; } +.stocking-product-summary__value--red-bg { + background-color: #f56c6c; + color: #ffffff; + padding: 2px 6px; + border-radius: 4px; + line-height: 1.2; + display: inline-block; +} + .stocking-product-table { border-top: none; } @@ -1403,3 +1500,57 @@ export default { border: 1px solid #e1f3d8; } +9399; +} + +.outer-sn-summary { + margin-bottom: 12px; +} + +.outer-sn-warehouse-table { + margin-bottom: 12px; +} + +.stocking-status { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 64px; + height: 24px; + padding: 0 10px; + border-radius: 12px; + font-size: 12px; + line-height: 1; + font-weight: 500; +} + +.stocking-status--yellow { + color: #e6a23c; + background: #fdf6ec; + border: 1px solid #faecd8; +} + +.stocking-status--purple { + color: #8a4f83; + background: #f6f0f7; + border: 1px solid #eadfec; +} + +.stocking-status--blue { + color: #409eff; + background: #ecf5ff; + border: 1px solid #d9ecff; +} + +.stocking-status--green { + color: #67c23a; + background: #f0f9eb; + border: 1px solid #e1f3d8; +} + +.stocking-status--green { + color: #67c23a; + background: #f0f9eb; + border: 1px solid #e1f3d8; +} + diff --git a/oms_web/oms_vue/src/views/system/user/profile/resetPwd.vue b/oms_web/oms_vue/src/views/system/user/profile/resetPwd.vue index 890e8de8..cafe812c 100644 --- a/oms_web/oms_vue/src/views/system/user/profile/resetPwd.vue +++ b/oms_web/oms_vue/src/views/system/user/profile/resetPwd.vue @@ -40,9 +40,9 @@ export default { { required: true, message: "旧密码不能为空", trigger: "blur" } ], newPassword: [ - { required: true, message: "新密码不能为空", trigger: "blur" }, - { min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" }, - { pattern: /^[^<>"'|\\]+$/, message: "不能包含非法字符:< > \" ' \\\ |", trigger: "blur" } + { required: true, trigger: "blur", message: "请输入新密码" }, + { min: 8, max: 20, trigger: "blur", message: "密码长度为8到20个字符" }, + { pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,20}$/, trigger: "blur", message: "密码需包含大小写字母、数字、特殊字符" } ], confirmPassword: [ { required: true, message: "确认密码不能为空", trigger: "blur" }, diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java index 3d33dbca..d6b09a7c 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -92,11 +92,11 @@ public class SysLoginController extends BaseController try { subject.login(token); - if (!passwordService.isStrongPassword(password)) - { - subject.logout(); - return AjaxResult.warn("当前密码不符合安全规则,请修改密码后重新登录"); - } +// if (!passwordService.isStrongPassword(password)) +// { +// subject.logout(); +// return AjaxResult.warn("当前密码不符合安全规则,请修改密码后重新登录"); +// } return success(); } catch (AuthenticationException e) diff --git a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/ProjectOrderInfoServiceImpl.java b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/ProjectOrderInfoServiceImpl.java index 6c6ddfaa..9e4d24ac 100644 --- a/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/ProjectOrderInfoServiceImpl.java +++ b/ruoyi-sip/src/main/java/com/ruoyi/sip/service/impl/ProjectOrderInfoServiceImpl.java @@ -2879,7 +2879,17 @@ public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService, To @Override public List selectProductMatchList(String orderCode) { - return projectOrderInfoMapper.selectProductMatchList(orderCode); + List orderProductMatchDtos = projectOrderInfoMapper.selectProductMatchList(orderCode); + List orderProductMatchBindDtos = projectOrderInfoMapper.selectProductMatchBindList(orderCode, null); + Map kyNumMap = orderProductMatchBindDtos.stream().filter( + item -> !item.getStatus().equals("0")) + .collect(Collectors.toMap(OrderProductMatchBindDto::getProductCode, item -> item.getKyNum() != null ? item.getKyNum() : 0, Integer::sum)); + + for (OrderProductMatchDto orderProductMatchDto : orderProductMatchDtos) { + orderProductMatchDto.setPhNum(orderProductMatchDto.getPhNum()+ + (kyNumMap.get(orderProductMatchDto.getProductCode())==null?0:kyNumMap.get(orderProductMatchDto.getProductCode()))); + } + return orderProductMatchDtos; } @Override @@ -2907,10 +2917,11 @@ public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService, To @Override public OrderProductMatchSnDto selectPurchaseSnList(String purchaseNo, String orderCode) { OrderProductMatchSnDto result = new OrderProductMatchSnDto(); - result.setTotalQuantity(projectOrderInfoMapper.selectProductMatchSnCountlList(purchaseNo)); - Integer quotaQuantity = projectOrderInfoMapper.selectPurchaseOrderMapBindNum(purchaseNo, orderCode); - result.setQuotaQuantity(quotaQuantity == null ? 0 : quotaQuantity); List snList = projectOrderInfoMapper.selectProductMatchSnDetailList(purchaseNo); +// result.setTotalQuantity(projectOrderInfoMapper.selectProductMatchSnCountlList(purchaseNo)); + result.setTotalQuantity(snList.size()); + Integer quotaQuantity = projectOrderInfoMapper.selectPurchaseOrderMapBindNum(purchaseNo, orderCode); + result.setQuotaQuantity(quotaQuantity); result.setSnList(snList); List warehouseList = snList.stream() .collect(Collectors.groupingBy(OrderProductMatchSnDetailDto::getWarehouseName)) diff --git a/ruoyi-sip/src/main/resources/mapper/sip/ProjectOrderInfoMapper.xml b/ruoyi-sip/src/main/resources/mapper/sip/ProjectOrderInfoMapper.xml index 0e2b7b24..b246fe66 100644 --- a/ruoyi-sip/src/main/resources/mapper/sip/ProjectOrderInfoMapper.xml +++ b/ruoyi-sip/src/main/resources/mapper/sip/ProjectOrderInfoMapper.xml @@ -933,8 +933,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" t3.model, t1.quantity as order_num, sum(ifnull(t5.bind_num, 0)) as ph_num, - (select count(1) from oms_inventory_info as tt where t2.order_code = tt.order_code and t3.product_code = tt.product_code) as bh_num, - ifnull((select sum(inner_price) from oms_inventory_info as tt where t2.order_code = tt.order_code and t3.product_code = tt.product_code),0) as sum_cost + ifnull(t6.bh_num,0) as bh_num,ifnull(t6.sum_cost,0) as sum_cost from project_product_info as t1 inner join project_order_info as t2 on t1.project_id = t2.project_id @@ -944,6 +943,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" on t3.product_code = t4.product_code left join oms_purchase_order_map as t5 on t2.id = t5.order_id and t4.purchase_id = t5.purchase_id + left join (select count(1) bh_num, sum(inner_price) sum_cost, product_code + from oms_inventory_info + where order_code = #{orderCode} + group by product_code) t6 on t3.product_code = t6.product_code where t2.order_code = #{orderCode} and t3.type in (1, 2) group by t3.product_code, t3.model, t1.quantity @@ -957,7 +960,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" t6.purchase_no, (select vendor_address from oms_vendor_info as tt where tt.vendor_id = t6.vendor_id) as vendor_address, t4.quantity as cg_num, - t4.quantity - ifnull((select sum(bind_num) from oms_purchase_order_map as tt where tt.purchase_id = t5.purchase_id), 0) as ky_num, + t4.quantity - ifnull((select sum(bind_num) from oms_purchase_order_map as tt where tt.purchase_id = t4.purchase_id), 0) as ky_num, t5.bind_num as ph_num, t6.status, ( @@ -980,7 +983,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" inner join oms_purchase_order as t6 on t6.id = t4.purchase_id where t2.order_code = #{orderCode} + and t3.product_code = #{productCode} + and t6.approve_status = 2 and t6.confirm_status = 1 group by t6.purchase_no @@ -993,7 +998,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" on t1.inner_code = t2.inner_code left join oms_warehouse_info as t3 on t2.warehouse_id = t3.id - where t2.purchase_no = #{purchaseNo} + where t2.purchase_no = #{purchaseNo} and t1.inventory_status=0 diff --git a/ruoyi-sip/src/main/resources/mapper/system/VendorInfoMapper.xml b/ruoyi-sip/src/main/resources/mapper/system/VendorInfoMapper.xml index a29b9209..b1e17186 100644 --- a/ruoyi-sip/src/main/resources/mapper/system/VendorInfoMapper.xml +++ b/ruoyi-sip/src/main/resources/mapper/system/VendorInfoMapper.xml @@ -36,7 +36,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" , t1.province, t1.city, t1.generated_address ,t2.warehouse_name from oms_vendor_info t1 - left join oms_warehouse_info t2 on t1.warehouse_id = t2.id + left join oms_warehouse_info t2 on t1.warehouse_id = t2.id