gaoluyang
5 天以前 3712799f3e0904f28e4268deaa5c5ee291323c38
军泰伟业
1.迁移仓库
已添加29个文件
已重命名1个文件
已修改102个文件
已删除121个文件
14090 ■■■■■ 文件已修改
multiple/assets/favicon/AYNMico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/BDSMico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/BHMY.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/CJNYico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/CMNYico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/DHDCico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/DHHBico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/DZYSico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/HCKXico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/HCMYico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/HGJJico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/HHKJIco.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/HSMYico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/HSXico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/HXGYico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/HXSJico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/HYSNico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/JLMYico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/JLSNico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/JMSLico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/JSMYico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/JSYNYico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/JTWYico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/JYHJico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/JZYJico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/LQMico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/MKZSico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/MXSCIco.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/NYDLico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/PHMKico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/QLMCico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/RTSWico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/RZNY.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/SQDico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/TJKHico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/TJXM.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/TYMKico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/WDSYico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/XYHBico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/ZDXMico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/ZQHXico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/ZYRQico.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/AYNMLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/BDSMLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/BHMYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/CJNYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/CMNYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/DHDCLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/DHHBLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/DZYSLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/HCKXLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/HCMYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/HGJJLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/HHKJLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/HSMYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/HSXLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/HXGYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/HXSJLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/HYSNLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/JLMYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/JLSNLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/JMSLLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/JSMYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/JSYNYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/JTWYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/JYHJLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/JZYJLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/LQMLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/MKZSLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/MXSCLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/NYDLLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/PHMKLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/QLMCLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/RTSWLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/RZNYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/SQDLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/TJKHLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/TJXMLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/TYMKLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/WDSYLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/XYHBLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/ZDXMLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/ZGLTLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/ZQHXLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/ZYRQLogo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/南通云从工业互联网有限公司.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/新缆(江苏)数字科技有限公司.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/logo/芯导软件(江苏)有限公司.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/BDSMView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/DHDCView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/DHHBView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/DZYSView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/HCKXView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/HCMYView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/HGJJView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/HHKJView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/HSMYView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/HSXView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/HXGYView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/HXSJView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/HYSNView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/JLSNView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/JMSLView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/JTWYView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/JZYJView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/LQMView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/MKZSView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/MXSCBack.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/NYDLView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/PHMKView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/QLMCView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/RTSWView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/RZNYView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/SQDView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/TJKHView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/TJXMView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/TYMKView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/WDSYView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/XYHBView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/ZDXMView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/ZQHXView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/screen/ZYRQView.png 补丁 | 查看 | 原始文档 | blame | 历史
multiple/config.json 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/basicData/productModel.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/equipmentManagement/calibration.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockIn.js 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/inventoryManagement/stockOut.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/personnelManagement/employeeRecord.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/personnelManagement/payrollManagement.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/personnelManagement/scheduling.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/processRoute.js 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/processRouteItem.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productProcessRoute.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productStructure.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionOrder.js 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionProcess.js 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionProductInput.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionProductMain.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionProductOutput.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionReporting.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/workOrder.js 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/system/user.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/viewIndex.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/PIMTable/PIMTable.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/PIMTable/Pagination.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Upload/FileUpload.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/customerFile/index.vue 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/ImportExcel/index.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/ProductSelectDialog.vue 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/index.vue 90 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/supplierManage/index.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue 131 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/fileList.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/approvalProcess/index.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/collaborativeApproval/noticeManagement/index.vue 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/expiryAfterSales/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/customerService/feedbackRegistration/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/calibration/index.vue 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/components/formDia.vue 382 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/index.vue 509 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/index.vue 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/components/formDia.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/measurementEquipment/index.vue 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Form/MaintainForm.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Form/RepairForm.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/Modal/RepairModal.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/repair/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/MaintenanceForm.vue 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Form/PlanForm.vue 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/Modal/PlanModal.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/upkeep/index.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/expenseManagement/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/financialStatements/index.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/financialManagement/revenueManagement/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index.vue 134 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/dispatchLog/index.vue 162 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/issueManagement/index.vue 195 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/components/formDia.vue 615 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/components/formDiaManual.vue 165 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/components/formDiaProduct.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/receiptManagement/index.vue 374 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/components/FormDiaManual.vue 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/stockManagement/index.vue 338 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/contractManagement/filesDia.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/contractManagement/index.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/dimission/components/formDia.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/employeeRecord/components/formDia.vue 166 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/onboarding/components/formDia.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/onboarding/components/formDiaXJHT.vue 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/onboarding/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/payrollManagement/components/formDia.vue 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/payrollManagement/index.vue 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/scheduling/index.vue 307 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/invoiceEntry/components/Modal.vue 916 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/invoiceEntry/index.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentEntry/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentHistory/index.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentLedger/index.vue 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementInvoiceLedger/fileList.vue 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementInvoiceLedger/index.vue 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementReport/index.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/operationScheduling/components/formDia.vue 121 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/operationScheduling/index.vue 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/Edit.vue 168 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/ItemsForm.vue 531 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/New.vue 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/index.vue 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productStructure/StructureEdit.vue 304 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productStructure/index.vue 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionCosting/index.vue 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionDispatching/components/autoDispatchDia.vue 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionDispatching/components/formDia.vue 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionDispatching/index.vue 464 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/ProcessRouteItemForm.vue 555 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 385 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionProcess/Edit.vue 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionProcess/New.vue 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionProcess/index.vue 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/Input.vue 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/Output.vue 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/components/formDia.vue 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/index.vue 877 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrder/index.vue 559 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/components/filesDia.vue 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/components/formDia.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/finalInspection/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/components/filesDia.vue 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/components/formDia.vue 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/processInspection/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/components/filesDia.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/rawMaterialInspection/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/dataDashboard/index.vue 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/projectProfit/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/indicatorStats/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/invoiceLedger/fileList.vue 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/invoiceLedger/index.vue 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/invoiceRegistration/index.vue 1256 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/orderManagement/index.vue 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/paymentShipping/index.vue 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPayment/index.vue 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPaymentHistory/index.vue 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/fileList.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 214 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesQuotation/index.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
multiple/assets/favicon/AYNMico.ico
Binary files differ
multiple/assets/favicon/BDSMico.ico
Binary files differ
multiple/assets/favicon/BHMY.ico
Binary files differ
multiple/assets/favicon/CJNYico.ico
Binary files differ
multiple/assets/favicon/CMNYico.ico
Binary files differ
multiple/assets/favicon/DHDCico.ico
Binary files differ
multiple/assets/favicon/DHHBico.ico
Binary files differ
multiple/assets/favicon/DZYSico.ico
Binary files differ
multiple/assets/favicon/HCKXico.ico
Binary files differ
multiple/assets/favicon/HCMYico.ico
Binary files differ
multiple/assets/favicon/HGJJico.ico
Binary files differ
multiple/assets/favicon/HHKJIco.ico
Binary files differ
multiple/assets/favicon/HSMYico.ico
Binary files differ
multiple/assets/favicon/HSXico.ico
Binary files differ
multiple/assets/favicon/HXGYico.ico
Binary files differ
multiple/assets/favicon/HXSJico.ico
Binary files differ
multiple/assets/favicon/HYSNico.ico
Binary files differ
multiple/assets/favicon/JLMYico.ico
Binary files differ
multiple/assets/favicon/JLSNico.ico
Binary files differ
multiple/assets/favicon/JMSLico.ico
Binary files differ
multiple/assets/favicon/JSMYico.ico
Binary files differ
multiple/assets/favicon/JSYNYico.ico
Binary files differ
multiple/assets/favicon/JTWYico.ico
multiple/assets/favicon/JYHJico.ico
Binary files differ
multiple/assets/favicon/JZYJico.ico
Binary files differ
multiple/assets/favicon/LQMico.ico
Binary files differ
multiple/assets/favicon/MKZSico.ico
Binary files differ
multiple/assets/favicon/MXSCIco.ico
Binary files differ
multiple/assets/favicon/NYDLico.ico
Binary files differ
multiple/assets/favicon/PHMKico.ico
Binary files differ
multiple/assets/favicon/QLMCico.ico
Binary files differ
multiple/assets/favicon/RTSWico.ico
Binary files differ
multiple/assets/favicon/RZNY.ico
Binary files differ
multiple/assets/favicon/SQDico.ico
Binary files differ
multiple/assets/favicon/TJKHico.ico
Binary files differ
multiple/assets/favicon/TJXM.ico
Binary files differ
multiple/assets/favicon/TYMKico.ico
Binary files differ
multiple/assets/favicon/WDSYico.ico
Binary files differ
multiple/assets/favicon/XYHBico.ico
Binary files differ
multiple/assets/favicon/ZDXMico.ico
Binary files differ
multiple/assets/favicon/ZQHXico.ico
Binary files differ
multiple/assets/favicon/ZYRQico.ico
Binary files differ
multiple/assets/favicon/favicon.ico
Binary files differ
multiple/assets/logo/AYNMLogo.png
Binary files differ
multiple/assets/logo/BDSMLogo.png
Binary files differ
multiple/assets/logo/BHMYLogo.png
Binary files differ
multiple/assets/logo/CJNYLogo.png
Binary files differ
multiple/assets/logo/CMNYLogo.png
Binary files differ
multiple/assets/logo/DHDCLogo.png
Binary files differ
multiple/assets/logo/DHHBLogo.png
Binary files differ
multiple/assets/logo/DZYSLogo.png
Binary files differ
multiple/assets/logo/HCKXLogo.png
Binary files differ
multiple/assets/logo/HCMYLogo.png
Binary files differ
multiple/assets/logo/HGJJLogo.png
Binary files differ
multiple/assets/logo/HHKJLogo.png
Binary files differ
multiple/assets/logo/HSMYLogo.png
Binary files differ
multiple/assets/logo/HSXLogo.png
Binary files differ
multiple/assets/logo/HXGYLogo.png
Binary files differ
multiple/assets/logo/HXSJLogo.png
Binary files differ
multiple/assets/logo/HYSNLogo.png
Binary files differ
multiple/assets/logo/JLMYLogo.png
Binary files differ
multiple/assets/logo/JLSNLogo.png
Binary files differ
multiple/assets/logo/JMSLLogo.png
Binary files differ
multiple/assets/logo/JSMYLogo.png
Binary files differ
multiple/assets/logo/JSYNYLogo.png
Binary files differ
multiple/assets/logo/JTWYLogo.png
multiple/assets/logo/JYHJLogo.png
Binary files differ
multiple/assets/logo/JZYJLogo.png
Binary files differ
multiple/assets/logo/LQMLogo.png
Binary files differ
multiple/assets/logo/MKZSLogo.png
Binary files differ
multiple/assets/logo/MXSCLogo.png
Binary files differ
multiple/assets/logo/NYDLLogo.png
Binary files differ
multiple/assets/logo/PHMKLogo.png
Binary files differ
multiple/assets/logo/QLMCLogo.png
Binary files differ
multiple/assets/logo/RTSWLogo.png
Binary files differ
multiple/assets/logo/RZNYLogo.png
Binary files differ
multiple/assets/logo/SQDLogo.png
Binary files differ
multiple/assets/logo/TJKHLogo.png
Binary files differ
multiple/assets/logo/TJXMLogo.png
Binary files differ
multiple/assets/logo/TYMKLogo.png
Binary files differ
multiple/assets/logo/WDSYLogo.png
Binary files differ
multiple/assets/logo/XYHBLogo.png
Binary files differ
multiple/assets/logo/ZDXMLogo.png
Binary files differ
multiple/assets/logo/ZGLTLogo.png
Binary files differ
multiple/assets/logo/ZQHXLogo.png
Binary files differ
multiple/assets/logo/ZYRQLogo.png
Binary files differ
multiple/assets/logo/logo.png
Binary files differ
multiple/assets/logo/ÄÏÍ¨ÔÆ´Ó¹¤Òµ»¥ÁªÍøÓÐÏÞ¹«Ë¾.png
Binary files differ
multiple/assets/logo/ÐÂÀ£¨½­ËÕ£©Êý×ֿƼ¼ÓÐÏÞ¹«Ë¾.png
Binary files differ
multiple/assets/logo/оµ¼Èí¼þ£¨½­ËÕ£©ÓÐÏÞ¹«Ë¾.png
Binary files differ
multiple/assets/screen/BDSMView.png
Binary files differ
multiple/assets/screen/DHDCView.png
Binary files differ
multiple/assets/screen/DHHBView.png
Binary files differ
multiple/assets/screen/DZYSView.png
Binary files differ
multiple/assets/screen/HCKXView.png
Binary files differ
multiple/assets/screen/HCMYView.png
Binary files differ
multiple/assets/screen/HGJJView.png
Binary files differ
multiple/assets/screen/HHKJView.png
Binary files differ
multiple/assets/screen/HSMYView.png
Binary files differ
multiple/assets/screen/HSXView.png
Binary files differ
multiple/assets/screen/HXGYView.png
Binary files differ
multiple/assets/screen/HXSJView.png
Binary files differ
multiple/assets/screen/HYSNView.png
Binary files differ
multiple/assets/screen/JLSNView.png
Binary files differ
multiple/assets/screen/JMSLView.png
Binary files differ
multiple/assets/screen/JTWYView.png

multiple/assets/screen/JZYJView.png
Binary files differ
multiple/assets/screen/LQMView.png
Binary files differ
multiple/assets/screen/MKZSView.png
Binary files differ
multiple/assets/screen/MXSCBack.png
Binary files differ
multiple/assets/screen/NYDLView.png
Binary files differ
multiple/assets/screen/PHMKView.png
Binary files differ
multiple/assets/screen/QLMCView.png
Binary files differ
multiple/assets/screen/RTSWView.png
Binary files differ
multiple/assets/screen/RZNYView.png
Binary files differ
multiple/assets/screen/SQDView.png
Binary files differ
multiple/assets/screen/TJKHView.png
Binary files differ
multiple/assets/screen/TJXMView.png
Binary files differ
multiple/assets/screen/TYMKView.png
Binary files differ
multiple/assets/screen/WDSYView.png
Binary files differ
multiple/assets/screen/XYHBView.png
Binary files differ
multiple/assets/screen/ZDXMView.png
Binary files differ
multiple/assets/screen/ZQHXView.png
Binary files differ
multiple/assets/screen/ZYRQView.png
Binary files differ
multiple/config.json
@@ -1,21 +1,21 @@
{
  "default": {
    "env": {
      "VITE_APP_TITLE": "芯导云(管理信息系统)"
      "VITE_APP_TITLE": "军泰伟业信息管理系统"
    },
    "screen": "screen/SQDView.png",
    "logo": "logo/SQDLogo.png",
    "favicon": "favicon/SQDico.ico"
    "screen": "screen/JTWYView.png",
    "logo": "logo/JTWYLogo.png",
    "favicon": "favicon/JTWYico.ico"
  },
  "SQD": {
  "JTWY": {
    "env": {
      "VITE_APP_TITLE": "天津双奇点管理系统",
      "VITE_BASE_API": "http://114.132.189.42:9042",
      "VITE_JAVA_API": "http://114.132.189.42:9044"
      "VITE_APP_TITLE": "军泰伟业信息管理系统",
      "VITE_BASE_API": "http://114.132.189.42:9123",
      "VITE_JAVA_API": "http://114.132.189.42:9031"
    },
    "screen": "screen/SQDView.png",
    "logo": "logo/SQDLogo.png",
    "favicon": "favicon/SQDico.ico"
    "screen": "screen/JTWYView.png",
    "logo": "logo/JTWYLogo.png",
    "favicon": "favicon/JTWYico.ico"
  },
  "screen": "/src/assets/images/login-background.png",
  "logo": "/src/assets/logo/logo.png",
package.json
@@ -31,7 +31,6 @@
    "fuse.js": "6.6.2",
    "js-beautify": "1.14.11",
    "js-cookie": "3.0.5",
    "jsbarcode": "^3.12.1",
    "jsencrypt": "3.3.2",
    "nprogress": "0.2.0",
    "pinia": "2.1.7",
src/api/basicData/productModel.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,9 @@
import request from "@/utils/request.js";
export function productModelList(query) {
    return request({
        url: '/basic/product/pageModel',
        method: 'get',
        params: query
    })
}
src/api/equipmentManagement/calibration.js
@@ -24,4 +24,12 @@
    method: "post",
    data: query,
  });
}
// åˆ é™¤è®°å½•
export function ledgerRecordDelete(ids) {
  return request({
    url: "/measuringInstrumentLedgerRecord/delete",
    method: "delete",
    data: ids,
  });
}
src/api/inventoryManagement/stockIn.js
@@ -117,8 +117,8 @@
// åˆ é™¤è‡ªå®šä¹‰å…¥åº“信息
export function delStockInCustom(ids) {
    return request({
        url: '/stockin/deleteCustom',
        method: 'delete',
        url: '/stockin/delteCustom',
        method: 'post',
        data: ids
    })
}
@@ -136,23 +136,6 @@
export function selectProductRecordListByPuechaserId(query) {
    return request({
        url: '/stockin/productlist',
        method: 'get',
        params: query
    })
}
// è‡ªå®šä¹‰å…¥åº“-详情
export function detailManagementByCustom(query) {
    return request({
        url: '/stockin/detailManagementByCustom',
        method: 'get',
        params: query
    })
}
// é‡‡è´­å…¥åº“-详情
export function stockinDetail(query) {
    return request({
        url: '/stockin/detail',
        method: 'get',
        params: query
    })
src/api/inventoryManagement/stockOut.js
@@ -1,6 +1,6 @@
import request from "@/utils/request";
//查询出库列表
// å‡ºåº“台账-采购出库查询出库列表
export const getStockOutPage = (params) => {
    return request({
        url: "/stockmanagement/listPage",
@@ -35,13 +35,3 @@
        data: ids
    })
}
//导出出库信息
export const exportStockOut = (query) => {
    return request({
        url: '/stockmanagement/export',
        method: 'get',
        params: query,
        responseType: 'blob'
    })
}
src/api/personnelManagement/employeeRecord.js
@@ -15,4 +15,13 @@
        method: 'get',
        params: query,
    })
}
// å¯¼å‡ºåˆåŒå‰¯æœ¬
export function staffOnJobExportCopy(data) {
    return request({
        url: '/staff/staffOnJob/exportCopy',
        method: 'post',
        data: data,
    })
}
src/api/personnelManagement/payrollManagement.js
@@ -32,4 +32,20 @@
    method: "delete",
    data: query,
  });
}
// å¯¼å…¥
export function importData(query) {
  return request({
    url: "/compensationPerformance/importData",
    method: "post",
    data: query,
  });
}
// ä¸‹è½½æ¨¡ç‰ˆ
export function exportTemplate(query) {
  return request({
    url: "/compensationPerformance/exportTemplate",
    method: "post",
    data: query,
  });
}
src/api/personnelManagement/scheduling.js
@@ -30,3 +30,10 @@
        data: data
    })
}
export function getCurrentUserLatestScheduling(){
    return request({
        url: "/staff/staffScheduling/getCurrentUserLatestScheduling",
        method: "get"
    })
}
src/api/productionManagement/processRoute.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
// å·¥è‰ºè·¯çº¿é¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function listPage(query) {
  return request({
    url: "/processRoute/page",
    method: "get",
    params: query,
  });
}
export function add(data) {
  return request({
    url: "/processRoute",
    method: "post",
    data: data,
  });
}
export function del(ids) {
  return request({
    url: '/processRoute/' + ids,
    method: 'delete',
  })
}
export function update(data) {
  return request({
    url: '/processRoute',
    method: 'put',
    data: data,
  })
}
src/api/productionManagement/processRouteItem.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
// å·¥è‰ºè·¯çº¿é¡¹ç›®é¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ—表查询
export function findProcessRouteItemList(query) {
    return request({
        url: "/processRouteItem/list",
        method: "get",
        params: query,
    });
}
export function addOrUpdateProcessRouteItem(data) {
    return request({
        url: "/processRouteItem",
        method: "post",
        data: data,
    });
}
src/api/productionManagement/productProcessRoute.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
// å·¥è‰ºè·¯çº¿é¡¹ç›®é¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ—表查询
export function findProductProcessRouteItemList(query) {
    return request({
        url: "/productProcessRoute/list",
        method: "get",
        params: query,
    });
}
export function addOrUpdateProductProcessRouteItem(data) {
    return request({
        url: "/productProcessRoute/updateRouteItem",
        method: "post",
        data: data,
    });
}
// åˆ é™¤å®¢æˆ·æ¡£æ¡ˆ
export function deleteRouteItem(ids) {
    return request({
        url: '/productProcessRoute/deleteRouteItem',
        method: 'delete',
        data: ids
    })
}
src/api/productionManagement/productStructure.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
// äº§å“ç»“构页面接口
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function queryList(id) {
  return request({
    url: "/productStructure/listByproductModelId/" + id,
    method: "get",
  });
}
export function add(data) {
  return request({
    url: "/productStructure",
    method: "post",
    data: data,
  });
}
src/api/productionManagement/productionOrder.js
@@ -9,6 +9,43 @@
    params: query,
  });
}
export function productOrderListPage(query) {
  return request({
    url: "/productOrder/page",
    method: "get",
    params: query,
  });
}
// èŽ·å–ç‚’æœºæ­£åœ¨å·¥ä½œé‡æ•°æ®
export function schedulingList(query) {
  return request({
    url: "/salesLedger/scheduling/list",
    method: "get",
    params: query,
  });
}
// ä¿å­˜ç‚’机设置
export function addSpeculatTrading(data) {
  return request({
    url: "/salesLedger/scheduling/addSpeculatTrading",
    method: "post",
    data: data,
  });
}
// ä¿®æ”¹ç‚’机设置
export function updateSpeculatTrading(data) {
  return request({
    url: "/salesLedger/scheduling/updateSpeculatTrading",
    method: "post",
    data: data,
  });
}
// ç”Ÿäº§æ´¾å·¥
export function productionDispatch(query) {
  return request({
@@ -16,4 +53,38 @@
    method: "post",
    data: query,
  });
}
// è‡ªåŠ¨æ´¾å·¥
export function productionDispatchList(query) {
  return request({
    url: "/salesLedger/scheduling/productionDispatchList",
    method: "post",
    data: query,
  });
}
// æŸ¥è¯¢æŸè€—率
export function getLossRate() {
  return request({
    url: "/salesLedger/scheduling/loss",
    method: "get",
  });
}
// æ–°å¢žæŸè€—率
export function addLossRate(data) {
  return request({
    url: "/salesLedger/scheduling/addLoss",
    method: "post",
    data: data,
  });
}
// ä¿®æ”¹æŸè€—率
export function updateLossRate(data) {
  return request({
    url: "/salesLedger/scheduling/updateLoss",
    method: "post",
    data: data,
  });
}
src/api/productionManagement/productionProcess.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
// å·¥åºé¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function listPage(query) {
  return request({
    url: "/productProcess/listPage",
    method: "get",
    params: query,
  });
}
export function processList(query) {
  return request({
    url: "/productProcess/list",
    method: "get",
    params: query,
  });
}
export function add(data) {
  return request({
    url: "/productProcess",
    method: "post",
    data: data,
  });
}
export function del(data) {
  return request({
    url: '/productProcess/batchDelete',
    method: 'delete',
    data: data,
  })
}
export function update(data) {
  return request({
    url: '/productProcess/update',
    method: 'put',
    data: data,
  })
}
// å·¥åºæŸ¥è¯¢
export function list() {
    return request({
        url: "/productProcess/list",
        method: "get",
    });
}
src/api/productionManagement/productionProductInput.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
// ç”Ÿäº§æŠ•入页面接口
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function productionProductInputListPage(query) {
    return request({
        url: "/productionProductInput/listPage",
        method: "get",
        params: query,
    });
}
src/api/productionManagement/productionProductMain.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
// ç”Ÿäº§æŠ¥å·¥é¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function productionProductMainListPage(query) {
    return request({
        url: "/productionProductMain/listPage",
        method: "get",
        params: query,
    });
}
src/api/productionManagement/productionProductOutput.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
// ç”Ÿäº§äº§å‡ºé¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function productionProductOutputListPage(query) {
    return request({
        url: "/productionProductOutput/listPage",
        method: "get",
        params: query,
    });
}
src/api/productionManagement/productionReporting.js
@@ -32,4 +32,12 @@
    method: "post",
    data: query,
  });
}
}
// ç”Ÿäº§æŠ¥å·¥-删除
export function productionReportDelete(query) {
  return request({
    url: "/productionProductMain/delete",
    method: "delete",
    data: query,
  });
}
src/api/productionManagement/workOrder.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
import request from "@/utils/request";
export function productWorkOrderPage(query) {
  return request({
    url: "/productWorkOrder/page",
    method: "get",
    params: query,
  });
}
export function updateProductWorkOrder(data) {
  return request({
    url: "/productWorkOrder/updateProductWorkOrder",
    method: "post",
    data: data,
  });
}
export function addProductMain(data) {
  return request({
    url: "/productionProductMain/addProductMain",
    method: "post",
    data: data,
  });
}
src/api/system/user.js
@@ -8,6 +8,13 @@
    method: 'get',
    params: query
  })
}// æŸ¥è¯¢ç”¨æˆ·åˆ—表
export function listAll(query) {
  return request({
    url: '/system/user//listAll',
    method: 'get',
    params: query
  })
}
// æŸ¥è¯¢ç”¨æˆ·è¯¦ç»†
src/api/viewIndex.js
@@ -44,4 +44,18 @@
        url: '/sales/ledger/getAmountHalfYear',
        method: 'get'
    })
}
}
// ååŒå¾…审批和报修待办事项
export const approveAndDeviceTodos = () => {
    return request({
        url: '/home/approveAndDeviceTodos',
        method: 'get'
    })
}
export const noticesCount = () => {
    return request({
        url: '/home/noticesCount',
        method: 'get'
    })
}
src/components/PIMTable/PIMTable.vue
@@ -204,7 +204,6 @@
    </el-table-column>
  </el-table>
  <pagination
        v-if="isShowPage"
    :total="page.total"
    :layout="page.layout"
    :page="page.current"
@@ -267,10 +266,6 @@
  isSelection: {
    type: Boolean,
    default: false,
  },
    isShowPage: {
    type: Boolean,
    default: true,
  },
  isShowSummary: {
    type: Boolean,
src/components/PIMTable/Pagination.vue
@@ -71,6 +71,7 @@
})
const handleSizeChange = (val) => {
    console.log('handleSizeChange', val)
  if (currentPage.value * val > props.total) {
    currentPage.value = 1
  }
@@ -81,6 +82,7 @@
}
const handleCurrentChange = (val) => {
    console.log('handleCurrentChange---', val)
  emit('pagination', { page: val, limit: pageSize.value })
  if (props.autoScroll) {
    scrollTo(0, 800)
src/components/Upload/FileUpload.vue
@@ -6,7 +6,7 @@
});
const props = defineProps({
  // downloadTemplate: Function,
  downloadTemplate: Function,
  showTips: Boolean,
  accept: {
    type: String,
@@ -31,7 +31,7 @@
    default: 1,
  },
});
const emits = defineEmits(["success", "remove",  "downloadTemplate"]);
const emits = defineEmits(["success", "remove"]);
const uploadRef = ref();
const fileList = ref([]);
@@ -85,7 +85,7 @@
          type="primary"
          link
          class="reset-margin"
          @click="emits('downloadTemplate')"
          @click="props.downloadTemplate()"
        >
          <span style="font-size: 12px; font-weight: normal">下载模板</span>
        </el-button>
src/views/basicData/customerFile/index.vue
@@ -147,8 +147,10 @@
              <el-select
                v-model="form.maintainer"
                placeholder="请选择"
                filterable
                default-first-option
                :reserve-keyword="false"
                clearable
                disabled
              >
                <el-option
                  v-for="item in userList"
@@ -305,10 +307,7 @@
        type: "text",
        clickFun: (row) => {
          openForm("edit", row);
        },
                disabled: (row) => {
                    return row.maintainer !== userStore.nickName
                }
        }
      },
    ],
  },
@@ -558,12 +557,6 @@
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
    const unauthorizedData = selectedRows.value.filter(item => item.maintainer !== userStore.nickName);
    if (unauthorizedData.length > 0) {
      proxy.$modal.msgWarning("不可删除他人维护的数据");
      return;
    }
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
src/views/basicData/product/ImportExcel/index.vue
@@ -5,13 +5,12 @@
  <el-dialog v-model="upload.open" :title="upload.title">
    <FileUpload
      ref="fileUploadRef"
      accept=".xlsx,.xls"
      accept=".xlsx, .xls"
      :headers="upload.headers"
      :action="upload.url + '?updateSupport=' + upload.updateSupport"
      :disabled="upload.isUploading"
      :showTip="true"
      :showTip="false"
      @success="handleFileSuccess"
      @downloadTemplate="downloadTemplate"
    />
    <template #footer>
      <div class="dialog-footer">
@@ -23,12 +22,11 @@
</template>
<script setup>
import { reactive, ref, getCurrentInstance } from "vue";
import { reactive } from "vue";
import { getToken } from "@/utils/auth.js";
import { FileUpload } from "@/components/Upload";
import { ElMessage } from "element-plus";
const { proxy } = getCurrentInstance();
defineOptions({
  name: "产品维护导入",
});
@@ -46,18 +44,12 @@
  headers: { Authorization: "Bearer " + getToken() },
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/system/supplier/import",
  updateSupport: true,
});
// ç‚¹å‡»å¯¼å…¥
const handleImport = () => {
  upload.open = true;
  upload.title = "产品导入";
};
// ä¸‹è½½æ¨¡æ¿
const downloadTemplate = () =>{
  proxy.download("/basic/product/downloadTemplate", {}, "产品导入模板.xlsx");
}
const submitFileForm = () => {
  fileUploadRef.value.uploadApi();
src/views/basicData/product/ProductSelectDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,163 @@
<template>
  <el-dialog
      v-model="visible"
      title="选择产品"
      width="900px"
      destroy-on-close
      :close-on-click-modal="false"
  >
    <el-form :inline="true" :model="query" class="mb-2">
      <el-form-item label="产品大类">
        <el-input
            v-model="query.productName"
            placeholder="输入产品大类"
            clearable
            @keyup.enter="onSearch"
        />
      </el-form-item>
      <el-form-item label="型号名称">
        <el-input
            v-model="query.model"
            placeholder="输入型号名称"
            clearable
            @keyup.enter="onSearch"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="onSearch">搜索</el-button>
        <el-button @click="onReset">重置</el-button>
      </el-form-item>
    </el-form>
    <!-- åˆ—表 -->
    <el-table
        v-loading="loading"
        :data="tableData"
        height="420"
        highlight-current-row
        row-key="id"
        @selection-change="handleSelectionChange"
    >
      <el-table-column type="selection" width="55" />
      <el-table-column type="index" label="#" width="60"/>
      <el-table-column prop="productName" label="产品大类" min-width="160"/>
      <el-table-column prop="model" label="型号名称" min-width="200"/>
      <el-table-column prop="unit" label="单位" min-width="160"/>
    </el-table>
    <div class="mt-3 flex justify-end">
      <el-pagination
          background
          layout="total, sizes, prev, pager, next, jumper"
          :total="total"
          v-model:page-size="page.pageSize"
          v-model:current-page="page.pageNum"
          :page-sizes="[10, 20, 50, 100]"
          @size-change="onPageChange"
          @current-change="onPageChange"
      />
    </div>
    <template #footer>
      <el-button @click="close()">取消</el-button>
      <el-button type="primary" :disabled="multipleSelection.length === 0" @click="onConfirm">
        ç¡®å®š
      </el-button>
    </template>
  </el-dialog>
</template>
<script setup lang="ts">
import {computed, onMounted, reactive, ref, watch} from "vue";
import {ElMessage} from "element-plus";
import {productModelList} from '@/api/basicData/productModel'
export type ProductRow = {
  id: number;
  productName: string;
  model: string;
  unit?: string;
};
const props = defineProps<{
  modelValue: boolean;
}>();
const emit = defineEmits(['update:modelValue', 'confirm']);
const visible = computed({
  get: () => props.modelValue,
  set: (v) => emit("update:modelValue", v),
});
const query = reactive({
  productName: "",
  model: "",
});
const page = reactive({
  pageNum: 1,
  pageSize: 10,
});
const loading = ref(false);
const tableData = ref<ProductRow[]>([]);
const total = ref(0);
const multipleSelection = ref<ProductRow[]>([])
function close() {
  visible.value = false;
}
const handleSelectionChange = (val: ProductRow[]) => {
  multipleSelection.value = val
}
function onSearch() {
  page.pageNum = 1;
  loadData();
}
function onReset() {
  query.productName = "";
  query.model = "";
  page.pageNum = 1;
  loadData();
}
function onPageChange() {
  loadData();
}
function onConfirm() {
  if (multipleSelection.value.length === 0) {
    ElMessage.warning("请选择一条产品");
    return;
  }
  emit("confirm", multipleSelection.value);
  close();
}
async function loadData() {
  loading.value = true;
  try {
    multipleSelection.value = []; // ç¿»é¡µ/搜索后清空选择更符合预期
    const res = await productModelList({
      productName: query.productName.trim(),
      model: query.model.trim(),
      current: page.pageNum,
      size: page.pageSize,
    });
    tableData.value = res.records;
    total.value = res.total;
  } finally {
    loading.value = false;
  }
}
onMounted(() => {
  loadData()
})
</script>
src/views/basicData/product/index.vue
@@ -76,7 +76,7 @@
    <div class="right">
      <div style="margin-bottom: 10px" v-if="isShowButton">
        <el-button type="primary" @click="openModelDia('add')">
          æ–°å¢žäº§å“é«˜åº¦
          æ–°å¢žè§„格型号
        </el-button>
        <ImportExcel @uploadSuccess="getModelList" />
        <el-button
@@ -129,7 +129,7 @@
    </el-dialog>
    <el-dialog
      v-model="modelDia"
      title="产品高度"
      title="规格型号"
      width="400px"
      @close="closeModelDia"
      @keydown.enter.prevent
@@ -143,10 +143,10 @@
      >
        <el-row>
          <el-col :span="24">
            <el-form-item label="产品高度:" prop="model">
            <el-form-item label="规格型号:" prop="model">
              <el-input
                v-model="modelForm.model"
                placeholder="请输入产品高度"
                placeholder="请输入规格型号(g*袋数)"
                clearable
                @keydown.enter.prevent
              />
@@ -155,16 +155,46 @@
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="高度单位:" prop="unit">
            <el-form-item label="其他规格型号:" prop="otherModel">
              <el-input
                v-model="modelForm.unit"
                placeholder="请输入高度单位"
                v-model="modelForm.otherModel"
                placeholder="请输入其他规格型号"
                clearable
                @keydown.enter.prevent
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="单位:" prop="unit">
              <el-input
                v-model="modelForm.unit"
                placeholder="请输入单位"
                clearable
                @keydown.enter.prevent
              />
            </el-form-item>
          </el-col>
        </el-row>
<!--        <el-row>-->
<!--          <el-col :span="24">-->
<!--            <el-form-item label="绑定机器:" prop="speculativeTradingName">-->
<!--              <el-select-->
<!--                v-model="modelForm.speculativeTradingName"-->
<!--                placeholder="请选择绑定机器"-->
<!--                multiple-->
<!--                clearable-->
<!--                style="width: 100%"-->
<!--              >-->
<!--                <el-option label="炒机1" value="炒机1" />-->
<!--                <el-option label="炒机2" value="炒机2" />-->
<!--                <el-option label="炒机3" value="炒机3" />-->
<!--                <el-option label="炒机4" value="炒机4" />-->
<!--              </el-select>-->
<!--            </el-form-item>-->
<!--          </el-col>-->
<!--        </el-row>-->
      </el-form>
      <template #footer>
        <div class="dialog-footer">
@@ -205,13 +235,17 @@
const expandedKeys = ref([]);
const tableColumn = ref([
  {
    label: "产品高度",
    label: "规格型号",
    prop: "model",
  },
  {
    label: "高度单位",
    label: "单位",
    prop: "unit",
  },
  // {
  //   label: "绑定机器",
  //   prop: "speculativeTradingName",
  // },
  {
    dataType: "action",
    label: "操作",
@@ -244,12 +278,22 @@
    productName: [{ required: true, message: "请输入", trigger: "blur" }],
  },
  modelForm: {
    otherModel:'',
    model: "",
    unit: "",
    speculativeTradingName: [],
  },
  modelRules: {
    model: [{ required: true, message: "请输入", trigger: "blur" }],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
    model: [
      // { required: true, message: "请输入", trigger: "blur" },
      {
        pattern: /^[0-9*]*$/,
        message: "只能输入数字和*号",
        trigger: "blur"
      }
    ],
    // unit: [{ required: true, message: "请输入", trigger: "blur" }],
    // speculativeTradingName: [{ required: false, message: "请选择绑定机器", trigger: "change" }],
  },
});
const { form, rules, modelForm, modelRules } = toRefs(data);
@@ -281,15 +325,20 @@
    form.value.productName = data.productName;
  }
};
// æ‰“开产品高度弹框
// æ‰“开规格型号弹框
const openModelDia = (type, data) => {
  modelOperationType.value = type;
  modelDia.value = true;
  modelForm.value.model = "";
  modelForm.value.model = "";
  modelForm.value.unit = "";
  modelForm.value.speculativeTradingName = [];
  modelForm.value.id = "";
  if (type === "edit") {
    modelForm.value = { ...data };
    // å¦‚果后端返回的是字符串,需要转换为数组
    if (data.speculativeTradingName && typeof data.speculativeTradingName === 'string') {
      modelForm.value.speculativeTradingName = data.speculativeTradingName.split(',');
    }
  }
};
// æäº¤äº§å“åç§°ä¿®æ”¹
@@ -358,8 +407,19 @@
const submitModelForm = () => {
  proxy.$refs.modelFormRef.validate((valid) => {
    if (valid) {
      modelForm.value.productId = currentId.value;
      addOrEditProductModel(modelForm.value).then((res) => {
      let _modelForm = { ...modelForm.value };
      if(_modelForm.otherModel){
        _modelForm.model = _modelForm.otherModel;
      }
      delete _modelForm.otherModel;
      // å°†é€‰ä¸­çš„æœºå™¨æ•°ç»„转换为逗号分隔的字符串
      const submitData = {
        ..._modelForm,
        productId: currentId.value,
        speculativeTradingName: modelForm.value.speculativeTradingName.join(',')
      };
      addOrEditProductModel(submitData).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeModelDia();
        getModelList();
src/views/basicData/supplierManage/index.vue
@@ -141,7 +141,9 @@
                v-model="form.maintainUserId"
                placeholder="请选择"
                clearable
                disabled
                filterable
                default-first-option
                :reserve-keyword="false"
              >
                <el-option
                  v-for="item in userList"
@@ -299,9 +301,6 @@
        clickFun: (row) => {
          openForm("edit", row);
        },
                disabled: (row) => {
                    return row.maintainUserName !== userStore.nickName
                }
      },
    ],
  },
@@ -496,12 +495,6 @@
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.maintainUserName !== userStore.nickName);
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -121,33 +121,16 @@
      <template #footer v-if="operationType === 'approval'">
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm(2)">不通过</el-button>
          <el-button type="primary" @click="openSignatureDialog(1)">通过</el-button>
          <el-button type="primary" @click="submitForm(1)">通过</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- ç”µå­ç­¾åå¼¹çª—(vue3-signature-pad) -->
    <el-dialog v-model="signatureDialogVisible" title="电子签名" width="600px" append-to-body>
            <vueEsign
                ref="esign"
                class="mySign"
                :width="800"
                :height="300"
                :isCrop="isCrop"
                :lineWidth="lineWidth"
                :lineColor="lineColor"
            />
      <div style="margin-top:10px;">
        <el-button @click="clearSignature">清除</el-button>
        <el-button type="primary" @click="confirmSignature">确定</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script setup>
import { getCurrentInstance, reactive, ref, toRefs } from "vue";
import vueEsign from "vue-esign";
import {
    approveProcessDetails,
    getDept,
@@ -156,7 +139,6 @@
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
import { getToken } from "@/utils/auth";
const emit = defineEmits(['close'])
const { proxy } = getCurrentInstance()
@@ -178,21 +160,6 @@
    },
});
const { form } = toRefs(data);
const signatureDialogVisible = ref(false);
const signatureImg = ref('');
let submitStatus = null; // ä¸´æ—¶å­˜å‚¨é€šè¿‡/不通过状态
const isCrop = ref("");
const esign = ref(null);
const lineWidth = ref(0);
const lineColor = ref("#000000");
// ä¸Šä¼ é…ç½®
const upload = reactive({
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
});
// èŠ‚ç‚¹æ ‡é¢˜
const getNodeTitle = (index, len) => {
@@ -248,77 +215,10 @@
        productOptions.value = res.data;
    });
};
// æ‰“开签名弹窗
const openSignatureDialog = (status) => {
  submitStatus = status;
  signatureDialogVisible.value = true;
};
// æ¸…除签名
const clearSignature = () => {
    esign.value.reset();
};
// ç¡®è®¤ç­¾å
const confirmSignature = () => {
    esign.value.generate().then((res) => {
        console.log(res);
        // å°†base64转换为二进制
        const base64Data = res.split(',')[1]; // ç§»é™¤data:image/png;base64,前缀
        const binaryString = atob(base64Data);
        const bytes = new Uint8Array(binaryString.length);
        for (let i = 0; i < binaryString.length; i++) {
            bytes[i] = binaryString.charCodeAt(i);
        }
        signatureImg.value = bytes;
        // åˆ›å»ºæ–‡ä»¶å¯¹è±¡ç”¨äºŽä¸Šä¼ 
        const blob = new Blob([bytes], { type: 'image/png' });
        const file = new File([blob], 'signature.png', { type: 'image/png' });
        // åˆ›å»ºFormData
        const formData = new FormData();
        formData.append('file', file);
        // ä¸Šä¼ ç­¾åå›¾ç‰‡
        fetch(upload.url, {
            method: 'POST',
            headers: upload.headers,
            body: formData
        })
        .then(response => response.json())
        .then(data => {
            if (data.code === 200) {
                console.log('data---', data)
                let tempFileIds = [];
                tempFileIds.push(data.data.tempId);
                signatureDialogVisible.value = false;
                clearSignature();
                // åªæœ‰é€šè¿‡æ—¶æ‰ä¼ é€’签名文件ID
                if (submitStatus === 1) {
                    submitForm(submitStatus, tempFileIds);
                } else {
                    submitForm(submitStatus);
                }
            } else {
                proxy.$modal.msgError("签名图片上传失败:" + data.msg);
            }
        })
        .catch(error => {
            console.error('上传失败:', error);
            proxy.$modal.msgError("签名图片上传失败");
        });
    }).catch((err) => {
        console.log(err);
        proxy.$modal.msgWarning("请先签名!");
    })
};
// æäº¤å®¡æ‰¹
const submitForm = (status, tempFileIds) => {
const submitForm = (status) => {
  const filteredActivities = activities.value.filter(activity => activity.isShen);
  filteredActivities[0].approveNodeStatus = status;
  // åªæœ‰é€šè¿‡æ—¶æ‰éœ€è¦ç­¾å
  if (status === 1 && tempFileIds) {
    filteredActivities[0].tempFileIds = tempFileIds;
  }
  // åˆ¤æ–­æ˜¯å¦ä¸ºæœ€åŽä¸€æ­¥
  const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length-1;
  updateApproveNode({ ...filteredActivities[0], isLast }).then(() => {
src/views/collaborativeApproval/approvalProcess/components/infoFormDia.vue
@@ -16,19 +16,20 @@
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="申请部门:" prop="approveDeptId">
                            <el-select
                                disabled
                                v-model="form.approveDeptId"
                                placeholder="选择部门"
                            >
                                <el-option
                                    v-for="user in productOptions"
                                    :key="user.deptId"
                                    :label="user.deptName"
                                    :value="user.deptId"
                                />
                            </el-select>
            <el-form-item label="申请部门:" prop="approveDeptName">
              <el-input v-model="form.approveDeptName" placeholder="请输入" clearable/>
<!--                            <el-select-->
<!--                                disabled-->
<!--                                v-model="form.approveDeptId"-->
<!--                                placeholder="选择部门"-->
<!--                            >-->
<!--                                <el-option-->
<!--                                    v-for="user in productOptions"-->
<!--                                    :key="user.deptId"-->
<!--                                    :label="user.deptName"-->
<!--                                    :value="user.deptId"-->
<!--                                />-->
<!--                            </el-select>-->
            </el-form-item>
          </el-col>
        </el-row>
@@ -36,6 +37,63 @@
          <el-col :span="24">
            <el-form-item :label="props.approveType == 5 ? '采购说明:' : '审批事由:'" prop="approveReason">
              <el-input v-model="form.approveReason" placeholder="请输入" clearable type="textarea" />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- è¯·å‡æ—¶é—´ï¼ˆä»…当 approveType ä¸º 2 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row :gutter="30" v-if="props.approveType == 2">
          <el-col :span="12">
            <el-form-item label="请假开始时间:" prop="startDate">
              <el-date-picker
                  v-model="form.startDate"
                  type="date"
                  placeholder="请选择开始日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="请假结束时间:" prop="endDate">
              <el-date-picker
                  v-model="form.endDate"
                  type="date"
                  placeholder="请选择结束日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- æŠ¥é”€é‡‘额(仅当 approveType ä¸º 4 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row v-if="props.approveType == 4">
          <el-col :span="24">
            <el-form-item label="报销金额:" prop="price">
              <el-input-number
                  v-model="form.price"
                  placeholder="请输入报销金额"
                  :min="0"
                  :precision="2"
                  :step="0.01"
                  style="width: 100%"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- å‡ºå·®åœ°ç‚¹ï¼ˆä»…当 approveType ä¸º 3 æ—¶æ˜¾ç¤ºï¼‰ -->
        <el-row v-if="props.approveType == 3">
          <el-col :span="24">
            <el-form-item label="出差地点:" prop="location">
              <el-input
                  v-model="form.location"
                  placeholder="请输入出差地点"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
@@ -88,6 +146,9 @@
                            <el-select
                                v-model="form.approveUser"
                                placeholder="选择人员"
                filterable
                default-first-option
                :reserve-keyword="false"
                            >
                                <el-option
                                    v-for="user in userList"
@@ -172,18 +233,27 @@
    approveId: "",
    approveUser: "",
        approveDeptId: "",
    approveDeptName: "",
    approveReason: "",
    checkResult: "",
    tempFileIds: [],
    approverList: [] // æ–°å¢žå­—段,存储所有节点的审批人id
    approverList: [], // æ–°å¢žå­—段,存储所有节点的审批人id
    startDate: "", // è¯·å‡å¼€å§‹æ—¶é—´
    endDate: "", // è¯·å‡ç»“束时间
    price: null, // æŠ¥é”€é‡‘额
    location: "" // å‡ºå·®åœ°ç‚¹
  },
  rules: {
    approveTime: [{ required: false, message: "请输入", trigger: "change" },],
    approveId: [{ required: false, message: "请输入", trigger: "blur" }],
    approveUser: [{ required: false, message: "请输入", trigger: "blur" }],
        approveDeptId: [{ required: true, message: "请输入", trigger: "blur" }],
    approveDeptName: [{ required: true, message: "请输入", trigger: "blur" }],
    approveReason: [{ required: true, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
    startDate: [{ required: true, message: "请选择请假开始时间", trigger: "change" }],
    endDate: [{ required: true, message: "请选择请假结束时间", trigger: "change" }],
    price: [{ required: true, message: "请输入报销金额", trigger: "blur" }],
    location: [{ required: true, message: "请输入出差地点", trigger: "blur" }],
  },
});
const { form, rules } = toRefs(data);
@@ -211,7 +281,6 @@
// æ‰“开弹框
const openDialog = (type, row) => {
  console.log('openDialog', type, row)
  operationType.value = type;
  dialogFormVisible.value = true;
    userListNoPageByTenantId().then((res) => {
@@ -278,6 +347,36 @@
    proxy.$modal.msgError("请为所有审批节点选择审批人!")
    return
  }
  // å½“ approveType ä¸º 2 æ—¶ï¼Œæ ¡éªŒè¯·å‡æ—¶é—´
  if (props.approveType == 2) {
    if (!form.value.startDate) {
      proxy.$modal.msgError("请选择请假开始时间!")
      return
    }
    if (!form.value.endDate) {
      proxy.$modal.msgError("请选择请假结束时间!")
      return
    }
    // æ ¡éªŒç»“束时间不能早于开始时间
    if (new Date(form.value.endDate) < new Date(form.value.startDate)) {
      proxy.$modal.msgError("请假结束时间不能早于开始时间!")
      return
    }
  }
  // å½“ approveType ä¸º 3 æ—¶ï¼Œæ ¡éªŒå‡ºå·®åœ°ç‚¹
  if (props.approveType == 3) {
    if (!form.value.location || form.value.location.trim() === '') {
      proxy.$modal.msgError("请输入出差地点!")
      return
    }
  }
  // å½“ approveType ä¸º 4 æ—¶ï¼Œæ ¡éªŒæŠ¥é”€é‡‘额
  if (props.approveType == 4) {
    if (!form.value.price || form.value.price <= 0) {
      proxy.$modal.msgError("请输入有效的报销金额!")
      return
    }
  }
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      if (operationType.value === "add" || currentApproveStatus.value == 3) {
src/views/collaborativeApproval/approvalProcess/fileList.vue
@@ -2,10 +2,11 @@
  <el-dialog v-model="dialogVisible" title="附件" width="40%" :before-close="handleClose">
    <el-table :data="tableData" border height="40vh">
      <el-table-column label="附件名称" prop="name" min-width="400" show-overflow-tooltip />
      <el-table-column fixed="right" label="操作" width="100" align="center">
      <el-table-column fixed="right" label="操作" width="150" align="center">
        <template #default="scope">
          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">下载</el-button>
          <el-button link type="primary" size="small" @click="lookFile(scope.row)">预览</el-button>
          <el-button link type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
@@ -16,6 +17,8 @@
<script setup>
import { ref } from 'vue'
import filePreview from '@/components/filePreview/index.vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import { delCommonFile } from '@/api/publicApi/commonFile.js'
const dialogVisible = ref(false)
const tableData = ref([])
@@ -35,6 +38,27 @@
const lookFile = (row) => {
  filePreviewRef.value.open(row.url)
}
// åˆ é™¤é™„ä»¶
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除附件"${row.name}"吗?`, '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    delCommonFile([row.id]).then(() => {
      ElMessage.success('删除成功')
      // ä»Žåˆ—表中移除已删除的附件
      const index = tableData.value.findIndex(item => item.id === row.id)
      if (index !== -1) {
        tableData.value.splice(index, 1)
      }
    }).catch(() => {
      ElMessage.error('删除失败')
    })
  }).catch(() => {
    ElMessage.info('已取消删除')
  })
}
defineExpose({
  open
})
src/views/collaborativeApproval/approvalProcess/index.vue
@@ -130,13 +130,18 @@
    width: 120
  },
  {
    label: "申请日期",
    prop: "approveTime",
    label: "金额(元)",
    prop: "price",
    width: 120
  },
  {
    label: props.approveType === 2 ? "开始日期" : "申请日期",
    prop: props.approveType === 2 ? "startDate" : "approveTime",
        width: 200
  },
  {
    label: "结束日期",
    prop: "approveOverTime",
    prop: props.approveType === 2 ? "endDate" : "approveOverTime",
    width: 120
  },
  {
@@ -157,7 +162,6 @@
        clickFun: (row) => {
          openForm("edit", row);
        },
                disabled: (row) => row.approveStatus == 2 || row.approveStatus == 1 || row.approveStatus == 4
      },
      {
        name: "审核",
src/views/collaborativeApproval/noticeManagement/index.vue
@@ -10,16 +10,17 @@
    <!-- é€šçŸ¥å…¬å‘Šæ¿ -->
    <div class="notice-board">
      <!-- æ”¾å‡é€šçŸ¥åŒºåŸŸ -->
      <div class="notice-section" v-if="holidayNoticeCount > 0">
      <!-- ç»Ÿä¸€é€šçŸ¥åŒºåŸŸ -->
      <div class="notice-section" v-if="totalNoticeCount > 0">
        <div class="section-header">
          <h3>📅 æ”¾å‡é€šçŸ¥</h3>
          <span class="section-count">{{ holidayNoticeCount }}条</span>
          <h3>� é€šçŸ¥å…¬å‘Š</h3>
          <span class="section-count">{{ totalNoticeCount }}条</span>
        </div>
        <div class="notice-cards">
          <!-- æ”¾å‡é€šçŸ¥ -->
          <div
              v-for="notice in holidayNotices"
              :key="notice.id"
              :key="'holiday-' + notice.id"
              class="notice-card holiday-card"
              :class="{ 'urgent': notice.priority === '3' }"
          >
@@ -40,6 +41,9 @@
            </div>
            <div class="card-footer">
              <div class="card-meta">
                <span class="type" :class="'type-' + notice.type">
                  {{ notice.type }}
                </span>
                <span class="priority" :class="'priority-' + notice.priority">
                  {{ getPriorityText(notice.priority) }}
                </span>
@@ -59,27 +63,11 @@
              <span>{{ notice.remark }}</span>
            </div>
          </div>
        </div>
      </div>
      <pagination
          v-if="holidayNoticePage.total > 0"
          :total="holidayNoticePage.total"
          :page="holidayNoticePage.current"
          :limit="holidayNoticePage.size"
          @pagination="handleHolidayNoticeCurrentChange"
      />
      <!-- è®¾å¤‡ç»´ä¿®é€šçŸ¥åŒºåŸŸ -->
      <div class="notice-section" v-if="maintenanceNoticeCount > 0">
        <div class="section-header">
          <h3>🔧 è®¾å¤‡ç»´ä¿®é€šçŸ¥</h3>
          <span class="section-count">{{ maintenanceNoticeCount }}条</span>
        </div>
        <div class="notice-cards">
          <!-- è®¾å¤‡ç»´ä¿®é€šçŸ¥ -->
          <div
              v-for="notice in maintenanceNotices"
              :key="notice.id"
              :key="'maintenance-' + notice.id"
              class="notice-card maintenance-card"
              :class="{ 'urgent': notice.priority === '3' }"
          >
@@ -122,12 +110,13 @@
        </div>
      </div>
      <!-- ç»Ÿä¸€åˆ†é¡µ -->
      <pagination
          v-if="maintenanceNoticePage.total > 0"
          :total="maintenanceNoticePage.total"
          :page="maintenanceNoticePage.current"
          :limit="maintenanceNoticePage.size"
          @pagination="handleMaintenanceNoticeCurrentChange"
          v-if="(holidayNoticePage.total + maintenanceNoticePage.total) > 0"
          :total="holidayNoticePage.total + maintenanceNoticePage.total"
          :page="Math.max(holidayNoticePage.current, maintenanceNoticePage.current)"
          :limit="Math.max(holidayNoticePage.size, maintenanceNoticePage.size)"
          @pagination="handleCurrentChange"
      />
      <!-- ç©ºçŠ¶æ€ -->
@@ -153,10 +142,7 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="公告类型" prop="type">
              <el-select v-model="form.type" placeholder="请选择公告类型" style="width: 100%">
                <el-option label="放假通知" :value="1"/>
                <el-option label="设备维修通知" :value="2"/>
              </el-select>
                            <el-input v-model="form.type" placeholder="请输入公告标题"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -419,16 +405,10 @@
  });
};
const holidayNoticeCount = ref()
const maintenanceNoticeCount = ref()
const totalNoticeCount = ref(0)
const fetchCount = () => {
  getCount().then(res => {
    holidayNoticeCount.value = res.data.filter(item => {
      return item.type === 1
    })[0].count;
    maintenanceNoticeCount.value = res.data.filter(item => {
      return item.type === 2
    })[0].count;
    totalNoticeCount.value = res.data.reduce((total, item) => total + item.count, 0);
  });
}
@@ -447,7 +427,7 @@
})
const fetchHolidayNotices = () => {
  listNotice({...holidayNoticePage.value, type: 1}).then(res => {
  listNotice({...holidayNoticePage.value}).then(res => {
    holidayNotices.value = res.data.records
    holidayNoticePage.value.total = res.data.total
  });
@@ -460,15 +440,12 @@
  });
};
const handleHolidayNoticeCurrentChange = (val) => {
const handleCurrentChange = (val) => {
  holidayNoticePage.value.size = val.limit
  holidayNoticePage.value.current = val.page
  fetchHolidayNotices()
};
const handleMaintenanceNoticeCurrentChange = (val) => {
  maintenanceNoticePage.value.size = val.limit
  maintenanceNoticePage.value.current = val.page
  fetchHolidayNotices()
  fetchMaintenanceNotices()
};
@@ -624,7 +601,6 @@
  color: #606266;
  line-height: 1.6;
  font-size: 14px;
  word-wrap: break-word;
}
.card-footer {
@@ -639,11 +615,21 @@
  gap: 8px;
}
.priority, .status {
.type, .priority, .status {
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 500;
}
.type-1 {
  background: #f0f9ff;
  color: #0369a1;
}
.type-2 {
  background: #fef3c7;
  color: #d97706;
}
.priority-1 {
@@ -703,15 +689,6 @@
  font-size: 12px;
  color: #606266;
  border-left: 3px solid #409eff;
}
.card-remark span {
  flex: 1;
  min-width: 0;
  word-wrap: break-word;
  word-break: break-all;
  white-space: normal;
  line-height: 1.5;
}
.empty-state {
src/views/customerService/expiryAfterSales/index.vue
@@ -59,7 +59,7 @@
                <template #operation="{ row }">
                    <el-button type="primary" link @click="openForm('view', row)">查看</el-button>
                    <el-button type="primary" link @click="openForm('edit', row)" v-if="row.status === 1">编辑</el-button>
                    <el-button type="primary" link @click="openForm('edit', row)">编辑</el-button>
                </template>
            </PIMTable>
        </div>
src/views/customerService/feedbackRegistration/index.vue
@@ -120,9 +120,6 @@
                clickFun: (row) => {
                    openForm("edit", row);
                },
                disabled: (row) => {
                    return row.checkUserId !== userStore.id || row.status !== 1
                }
            },
        ],
    },
@@ -172,12 +169,6 @@
const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.checkUserId !== userStore.id);
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
        ids = selectedRows.value.map((item) => item.id);
    } else {
        proxy.$modal.msgWarning("请选择数据");
src/views/equipmentManagement/calibration/index.vue
@@ -57,10 +57,10 @@
<script setup>
import {onMounted, ref} from "vue";
import {ElMessageBox} from "element-plus";
import {ElMessageBox, ElMessage} from "element-plus";
import useUserStore from "@/store/modules/user.js";
import CalibrationDia from "@/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue";
import {ledgerRecordListPage} from "@/api/equipmentManagement/calibration.js";
import {ledgerRecordListPage, ledgerRecordDelete} from "@/api/equipmentManagement/calibration.js";
const { proxy } = getCurrentInstance();
const userStore = useUserStore()
@@ -134,6 +134,7 @@
    {
        dataType: "action",
        label: "操作",
        width: 100,
        align: "center",
        fixed: 'right',
        operation: [
@@ -143,9 +144,16 @@
                clickFun: (row) => {
                    openCalibrationDia("edit", row);
                },
                disabled: (row) => {
                    return row.userId !== userStore.id
                }
            },
            {
                name: "删除",
                type: "text",
                style: {
                    color: "#F56C6C"
                },
                clickFun: (row) => {
                    handleDelete(row);
                },
            },
        ],
    },
@@ -194,6 +202,26 @@
    })
}
// åˆ é™¤è®°å½•
const handleDelete = (row) => {
    ElMessageBox.confirm(`确认删除计量器具编号为"${row.code}"的检定记录吗?`, "删除确认", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            ledgerRecordDelete([row.id]).then(() => {
                ElMessage.success("删除成功");
                getList();
            }).catch(() => {
                ElMessage.error("删除失败");
            });
        })
        .catch(() => {
            proxy.$modal.msg("已取消删除");
        });
};
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
src/views/equipmentManagement/inspectionManagement/components/formDia.vue
@@ -1,102 +1,116 @@
<template>
  <div>
    <el-dialog :title="operationType === 'add' ? '新增巡检任务' : '编辑巡检任务'"
               v-model="dialogVisitable" width="800px" @close="cancel">
      <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
        <el-row>
          <el-col :span="12">
            <el-form-item label="设备名称" prop="taskId">
              <el-select v-model="form.taskId" @change="setDeviceModel">
                <el-option
                  v-for="(item, index) in deviceOptions"
                  :key="index"
                  :label="item.deviceName"
                  :value="item.id"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="巡检人" prop="inspector">
              <el-select v-model="form.inspector" placeholder="请选择" multiple clearable>
                <el-option v-for="item in userList" :label="item.nickName" :value="item.userId" :key="item.userId"/>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="备注" prop="remarks">
              <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="任务频率" prop="frequencyType">
              <el-select v-model="form.frequencyType" placeholder="请选择" clearable>
                <el-option label="每日" value="DAILY"/>
                <el-option label="每周" value="WEEKLY"/>
                <el-option label="每月" value="MONTHLY"/>
                <!-- <el-option label="季度" value="QUARTERLY"/> -->
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-time-picker v-model="form.frequencyDetail" placeholder="选择时间" format="HH:mm"
                              value-format="HH:mm" />
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-select v-model="form.week" placeholder="请选择" clearable style="width: 50%">
                <el-option label="周一" value="MON"/>
                <el-option label="周二" value="TUE"/>
                <el-option label="周三" value="WED"/>
                <el-option label="周四" value="THU"/>
                <el-option label="周五" value="FRI"/>
                <el-option label="周六" value="SAT"/>
                <el-option label="周日" value="SUN"/>
              </el-select>
              <el-time-picker v-model="form.time" placeholder="选择时间" format="HH:mm"
                              value-format="HH:mm"  style="width: 50%"/>
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-date-picker
                  v-model="form.frequencyDetail"
                  type="datetime"
                  clearable
                  placeholder="选择开始日期"
                  format="DD,HH:mm"
                  value-format="DD,HH:mm"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
            <el-form-item label="日期" prop="frequencyDetail">
              <el-date-picker
                  v-model="form.frequencyDetail"
                  type="datetime"
                  clearable
                  placeholder="选择开始日期"
                  format="MM,DD,HH:mm"
                  value-format="MM,DD,HH:mm"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="cancel">取消</el-button>
          <el-button type="primary" @click="submitForm">保存</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
    <div>
        <el-dialog :title="operationType === 'add' ? '新增巡检任务' : '编辑巡检任务'"
                             v-model="dialogVisitable" width="800px" @close="cancel">
            <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="设备名称" prop="taskId">
                            <el-select v-model="form.taskId" @change="setDeviceModel" filterable>
                                <el-option
                                    v-for="(item, index) in deviceOptions"
                                    :key="index"
                                    :label="item.deviceName"
                                    :value="item.id"
                                ></el-option>
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="巡检人" prop="inspector">
                            <el-select v-model="form.inspector"                 filterable
                                                 default-first-option
                                                 :reserve-keyword="false" placeholder="请选择" multiple clearable>
                                <el-option v-for="item in userList" :label="item.nickName" :value="item.userId" :key="item.userId"/>
                            </el-select>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="备注" prop="remarks">
                            <el-input v-model="form.remarks" placeholder="请输入备注" type="textarea" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="登记时间" prop="dateStr">
                            <el-date-picker
                                v-model="form.dateStr"
                                type="date"
                                placeholder="选择登记日期"
                                format="YYYY-MM-DD"
                                value-format="YYYY-MM-DD"
                                style="width: 100%"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-col :span="12">
                        <el-form-item label="任务频率" prop="frequencyType">
                            <el-select v-model="form.frequencyType" placeholder="请选择" clearable>
                                <el-option label="每日" value="DAILY"/>
                                <el-option label="每周" value="WEEKLY"/>
                                <el-option label="每月" value="MONTHLY"/>
                                <!-- <el-option label="季度" value="QUARTERLY"/> -->
                            </el-select>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'DAILY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-time-picker v-model="form.frequencyDetail" placeholder="选择时间" format="HH:mm"
                                                            value-format="HH:mm" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'WEEKLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-select v-model="form.week" placeholder="请选择" clearable style="width: 50%">
                                <el-option label="周一" value="MON"/>
                                <el-option label="周二" value="TUE"/>
                                <el-option label="周三" value="WED"/>
                                <el-option label="周四" value="THU"/>
                                <el-option label="周五" value="FRI"/>
                                <el-option label="周六" value="SAT"/>
                                <el-option label="周日" value="SUN"/>
                            </el-select>
                            <el-time-picker v-model="form.time" placeholder="选择时间" format="HH:mm"
                                                            value-format="HH:mm"  style="width: 50%"/>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'MONTHLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-date-picker
                                v-model="form.frequencyDetail"
                                type="datetime"
                                clearable
                                placeholder="选择开始日期"
                                format="DD,HH:mm"
                                value-format="DD,HH:mm"
                            />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12" v-if="form.frequencyType === 'QUARTERLY' && form.frequencyType">
                        <el-form-item label="日期" prop="frequencyDetail">
                            <el-date-picker
                                v-model="form.frequencyDetail"
                                type="datetime"
                                clearable
                                placeholder="选择开始日期"
                                format="MM,DD,HH:mm"
                                value-format="MM,DD,HH:mm"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button @click="cancel">取消</el-button>
                    <el-button type="primary" @click="submitForm">保存</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>
<script setup>
@@ -113,115 +127,117 @@
const operationType = ref('add');
const deviceOptions = ref([]);
const data = reactive({
  form: {
    taskId: undefined,
    taskName: undefined,
    inspector: '',
    inspectorIds: '',
    remarks: '',
    frequencyType: '',
    frequencyDetail: '',
    week: '',
    time: ''
  },
  rules: {
    taskId: [{ required: true, message: "请选择设备", trigger: "change" },],
    inspector: [{ required: true, message: "请输入巡检人", trigger: "blur" },],
  }
    form: {
        taskId: undefined,
        taskName: undefined,
        inspector: '',
        inspectorIds: '',
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: '',
        dateStr: ''
    },
    rules: {
        taskId: [{ required: true, message: "请选择设备", trigger: "change" },],
        inspector: [{ required: true, message: "请输入巡检人", trigger: "blur" },],
        dateStr: [{ required: true, message: "请选择登记时间", trigger: "change" }]
    }
})
const { form, rules } = toRefs(data)
const userList = ref([])
const loadDeviceName = async () => {
  const { data } = await getDeviceLedger();
  deviceOptions.value = data;
    const { data } = await getDeviceLedger();
    deviceOptions.value = data;
};
const setDeviceModel = (id) => {
  const option = deviceOptions.value.find((item) => item.id === id);
  if (option) {
    form.value.taskName = option.deviceName;
  }
    const option = deviceOptions.value.find((item) => item.id === id);
    if (option) {
        form.value.taskName = option.deviceName;
    }
}
// æ‰“开弹框
const openDialog = async (type, row) => {
  dialogVisitable.value = true
  operationType.value = type
  // é‡ç½®è¡¨å•
  resetForm();
  // åŠ è½½ç”¨æˆ·åˆ—è¡¨
  userListNoPageByTenantId().then((res) => {
    userList.value = res.data;
  });
  // åŠ è½½è®¾å¤‡åˆ—è¡¨
  await loadDeviceName();
  if (type === 'edit' && row) {
    form.value = {...row}
    form.value.inspector = form.value.inspectorIds.split(',').map(Number)
    // å¦‚果有设备ID,自动设置设备信息
    if (form.value.taskId) {
      setDeviceModel(form.value.taskId);
    }
  }
    dialogVisitable.value = true
    operationType.value = type
    // é‡ç½®è¡¨å•
    resetForm();
    // åŠ è½½ç”¨æˆ·åˆ—è¡¨
    userListNoPageByTenantId().then((res) => {
        userList.value = res.data;
    });
    // åŠ è½½è®¾å¤‡åˆ—è¡¨
    await loadDeviceName();
    if (type === 'edit' && row) {
        form.value = {...row}
        form.value.inspector = form.value.inspectorIds.split(',').map(Number)
        // å¦‚果有设备ID,自动设置设备信息
        if (form.value.taskId) {
            setDeviceModel(form.value.taskId);
        }
    }
}
// å…³é—­å¯¹è¯æ¡†
const cancel = () => {
  resetForm()
  dialogVisitable.value = false
  emit('closeDia')
    resetForm()
    dialogVisitable.value = false
    emit('closeDia')
}
// é‡ç½®è¡¨å•函数
const resetForm = () => {
  if (proxy.$refs.formRef) {
    proxy.$refs.formRef.resetFields()
  }
  // é‡ç½®è¡¨å•数据确保设备信息正确重置
  form.value = {
    taskId: undefined,
    taskName: undefined,
    inspector: '',
    inspectorIds: '',
    remarks: '',
    frequencyType: '',
    frequencyDetail: '',
    week: '',
    time: ''
  }
    if (proxy.$refs.formRef) {
        proxy.$refs.formRef.resetFields()
    }
    // é‡ç½®è¡¨å•数据确保设备信息正确重置
    form.value = {
        taskId: undefined,
        taskName: undefined,
        inspector: '',
        inspectorIds: '',
        remarks: '',
        frequencyType: '',
        frequencyDetail: '',
        week: '',
        time: ''
    }
}
// æäº¤è¡¨å•
const submitForm = () => {
  proxy.$refs["formRef"].validate(async valid => {
    if (valid) {
      try {
        form.value.inspectorIds = form.value.inspector.join(',')
        delete form.value.inspector
        if (form.value.frequencyType === 'WEEKLY') {
          let frequencyDetail = ''
          frequencyDetail = form.value.week + ',' + form.value.time
          form.value.frequencyDetail = frequencyDetail
        }
        let res = await userStore.getInfo()
        form.value.registrantId = res.user.userId
        await addOrEditTimingTask(form.value)
        cancel()
        proxy.$modal.msgSuccess('提交成功')
      } catch (error) {
        proxy.$modal.msgError('提交失败,请重试')
      }
    }
  })
    proxy.$refs["formRef"].validate(async valid => {
        if (valid) {
            try {
                form.value.inspectorIds = form.value.inspector.join(',')
                delete form.value.inspector
                if (form.value.frequencyType === 'WEEKLY') {
                    let frequencyDetail = ''
                    frequencyDetail = form.value.week + ',' + form.value.time
                    form.value.frequencyDetail = frequencyDetail
                }
                let res = await userStore.getInfo()
                form.value.registrantId = res.user.userId
                await addOrEditTimingTask(form.value)
                cancel()
                proxy.$modal.msgSuccess('提交成功')
            } catch (error) {
                proxy.$modal.msgError('提交失败,请重试')
            }
        }
    })
}
defineExpose({ openDialog })
</script>
src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
@@ -32,7 +32,7 @@
        
        <!-- ç”Ÿäº§åŽ -->
        <div class="form-container">
          <div class="title">生产后</div>
          <div class="title">生产中</div>
          
          <!-- å›¾ç‰‡åˆ—表 -->
          <div style="display: flex; flex-wrap: wrap;">
@@ -59,7 +59,7 @@
        
        <!-- ç”Ÿäº§é—®é¢˜ -->
        <div class="form-container">
          <div class="title">生产问题</div>
          <div class="title">生产后</div>
          
          <!-- å›¾ç‰‡åˆ—表 -->
          <div style="display: flex; flex-wrap: wrap;">
src/views/equipmentManagement/inspectionManagement/index.vue
@@ -1,80 +1,78 @@
<template>
  <div class="app-container">
    <el-form :inline="true" :model="queryParams" class="search-form">
      <el-form-item label="搜索">
        <el-input
            v-model="queryParams.searchAll"
            placeholder="请输入关键字"
            clearable
            :style="{ width: '100%' }"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleQuery">查询</el-button>
        <el-button @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>
    <el-card>
      <div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;">
        <el-radio-group v-model="activeRadio" @change="radioChange">
          <el-radio-button v-for="tab in radios"
                           :key="tab.name"
                           :label="tab.label"
                           :value="tab.name"/>
        </el-radio-group>
        <!-- æ“ä½œæŒ‰é’®åŒº -->
        <el-space v-if="activeRadio !== 'task'">
          <el-button type="primary" :icon="Plus" @click="handleAdd(undefined)">新建</el-button>
          <el-button type="danger" :icon="Delete" @click="handleDelete">删除</el-button>
          <el-button @click="handleOut">导出</el-button>
        </el-space>
        <el-space v-else>
          <el-button @click="handleOut">导出</el-button>
        </el-space>
      </div>
      <div>
        <div>
          <PIMTable :table-loading="tableLoading"
                  :table-data="tableData"
                  :column="tableColumns"
                  @selection-change="handleSelectionChange"
                  :is-selection="true"
                  :border="true"
                  :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }"
          >
          <template #inspector="{ row }">
            <div class="person-tags">
              <!-- è°ƒè¯•信息,上线时删除 -->
              <!-- {{ console.log('inspector data:', row.inspector) }} -->
              <template v-if="row.inspector && row.inspector.length > 0">
                <el-tag
                  v-for="(person, index) in row.inspector"
                  :key="index"
                  size="small"
                  type="primary"
                  class="person-tag"
                >
                  {{ person }}
                </el-tag>
              </template>
              <span v-else class="no-data">--</span>
            </div>
          </template>
            </PIMTable>
        </div>
        <pagination
            v-if="total>0"
            :page="pageNum"
            :limit="pageSize"
            :total="total"
            @pagination="handlePagination"
            :layout="'total, prev, pager, next, jumper'"
        />
      </div>
    </el-card>
    <form-dia ref="formDia" @closeDia="handleQuery"></form-dia>
    <view-files ref="viewFiles"></view-files>
  </div>
    <div class="app-container">
        <el-form :inline="true" :model="queryParams" class="search-form">
            <el-form-item label="搜索">
                <el-input
                    v-model="queryParams.searchAll"
                    placeholder="请输入关键字"
                    clearable
                    :style="{ width: '100%' }"
                />
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="handleQuery">查询</el-button>
                <el-button @click="resetQuery">重置</el-button>
            </el-form-item>
        </el-form>
        <el-card>
            <div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom: 10px;">
                <el-radio-group v-model="activeRadio" @change="radioChange">
                    <el-radio-button v-for="tab in radios"
                                                     :key="tab.name"
                                                     :label="tab.label"
                                                     :value="tab.name"/>
                </el-radio-group>
                <!-- æ“ä½œæŒ‰é’®åŒº -->
                <el-space v-if="activeRadio !== 'task'">
                    <el-button type="primary" :icon="Plus" @click="handleAdd(undefined)">新建</el-button>
                    <el-button type="danger" :icon="Delete" @click="handleDelete">删除</el-button>
                    <el-button @click="handleOut">导出</el-button>
                </el-space>
                <el-space v-else>
                    <el-button @click="handleOut">导出</el-button>
                </el-space>
            </div>
            <div>
                <div>
                    <PIMTable :table-loading="tableLoading"
                                        :table-data="tableData"
                                        :column="tableColumns"
                                        @selection-change="handleSelectionChange"
                                        :is-selection="true"
                                        :border="true"
                                        :table-style="{ width: '100%', height: 'calc(100vh - 23em)' }"
                                        :page="{
          current: pageNum,
          size: pageSize,
          total: total,
        }"
                                        @pagination="pagination"
                    >
                        <template #inspector="{ row }">
                            <div class="person-tags">
                                <!-- è°ƒè¯•信息,上线时删除 -->
                                <!-- {{ console.log('inspector data:', row.inspector) }} -->
                                <template v-if="row.inspector && row.inspector.length > 0">
                                    <el-tag
                                        v-for="(person, index) in row.inspector"
                                        :key="index"
                                        size="small"
                                        type="primary"
                                        class="person-tag"
                                    >
                                        {{ person }}
                                    </el-tag>
                                </template>
                                <span v-else class="no-data">--</span>
                            </div>
                        </template>
                    </PIMTable>
                </div>
            </div>
        </el-card>
        <form-dia ref="formDia" @closeDia="handleQuery"></form-dia>
        <view-files ref="viewFiles"></view-files>
    </div>
</template>
<script setup>
@@ -90,9 +88,9 @@
// æŽ¥å£å¼•å…¥
import {
  delTimingTask,
  inspectionTaskList,
  timingTaskList
    delTimingTask,
    inspectionTaskList,
    timingTaskList
} from "@/api/inspectionManagement/index.js";
// å…¨å±€å˜é‡
@@ -102,14 +100,14 @@
// æŸ¥è¯¢å‚æ•°
const queryParams = reactive({
  searchAll: "",
    searchAll: "",
});
// å•选框配置
const activeRadio = ref("taskManage");
const radios = reactive([
  { name: "taskManage", label: "定时任务管理" },
  { name: "task", label: "定时任务记录" },
    { name: "taskManage", label: "定时任务管理" },
    { name: "task", label: "定时任务记录" },
]);
// è¡¨æ ¼æ•°æ®
@@ -124,234 +122,233 @@
// åˆ—配置
const columns = ref([
  { prop: "taskName", label: "巡检任务名称", minWidth: 160 },
  { prop: "remarks", label: "备注", minWidth: 150 },
  { prop: "inspector", label: "执行巡检人", minWidth: 150, slot: "inspector" },
  {
    prop: "frequencyType",
    label: "频次",
    minWidth: 150,
    formatter: (_, __, val) => ({
      DAILY: "每日",
      WEEKLY: "每周",
      MONTHLY: "每月",
      QUARTERLY: "季度"
    }[val] || "")
  },
  {
    prop: "frequencyDetail",
    label: "开始日期与时间",
    minWidth: 150,
    formatter: (row, column, cellValue) => {
      // å…ˆåˆ¤æ–­æ˜¯å¦æ˜¯å­—符串
      if (typeof cellValue !== 'string') return '';
      let val = cellValue;
      const replacements = {
        MON: '周一',
        TUE: '周二',
        WED: '周三',
        THU: '周四',
        FRI: '周五',
        SAT: '周六',
        SUN: '周日'
      };
      // ä½¿ç”¨æ­£åˆ™ä¸€æ¬¡æ€§æ›¿æ¢æ‰€æœ‰åŒ¹é…é¡¹
      return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]);
    }
  },
  { prop: "registrant", label: "登记人", minWidth: 100 },
  { prop: "createTime", label: "登记日期", minWidth: 100 },
    { prop: "taskName", label: "巡检任务名称", minWidth: 160 },
    { prop: "remarks", label: "备注", minWidth: 150 },
    { prop: "inspector", label: "执行巡检人", minWidth: 150, slot: "inspector" },
    {
        prop: "frequencyType",
        label: "频次",
        minWidth: 150,
        formatter: (_, __, val) => ({
            DAILY: "每日",
            WEEKLY: "每周",
            MONTHLY: "每月",
            QUARTERLY: "季度"
        }[val] || "")
    },
    {
        prop: "frequencyDetail",
        label: "开始日期与时间",
        minWidth: 150,
        formatter: (row, column, cellValue) => {
            // å…ˆåˆ¤æ–­æ˜¯å¦æ˜¯å­—符串
            if (typeof cellValue !== 'string') return '';
            let val = cellValue;
            const replacements = {
                MON: '周一',
                TUE: '周二',
                WED: '周三',
                THU: '周四',
                FRI: '周五',
                SAT: '周六',
                SUN: '周日'
            };
            // ä½¿ç”¨æ­£åˆ™ä¸€æ¬¡æ€§æ›¿æ¢æ‰€æœ‰åŒ¹é…é¡¹
            return val.replace(/MON|TUE|WED|THU|FRI|SAT|SUN/g, match => replacements[match]);
        }
    },
    { prop: "registrant", label: "登记人", minWidth: 100 },
    { prop: "dateStr", label: "登记日期", minWidth: 100 },
]);
// æ“ä½œåˆ—配置
const getOperationColumn = (operations) => {
  if (!operations || operations.length === 0) return null;
  const operationConfig = {
    label: "操作",
    width: 130,
    fixed: "right",
    dataType: "action",
    operation: operations.map(op => {
      switch (op) {
        case 'edit':
          return {
            name: "编辑",
            clickFun: handleAdd,
            color: "#409EFF"
          };
        case 'viewFile':
          return {
            name: "查看附件",
            clickFun: viewFile,
            color: "#67C23A"
          };
        default:
          return null;
      }
    }).filter(Boolean)
  };
  return operationConfig;
    if (!operations || operations.length === 0) return null;
    const operationConfig = {
        label: "操作",
        width: 130,
        fixed: "right",
        dataType: "action",
        operation: operations.map(op => {
            switch (op) {
                case 'edit':
                    return {
                        name: "编辑",
                        clickFun: handleAdd,
                        color: "#409EFF"
                    };
                case 'viewFile':
                    return {
                        name: "查看附件",
                        clickFun: viewFile,
                        color: "#67C23A"
                    };
                default:
                    return null;
            }
        }).filter(Boolean)
    };
    return operationConfig;
};
onMounted(() => {
  radioChange('taskManage');
    radioChange('taskManage');
});
// å•选变化
const radioChange = (value) => {
  if (value === "taskManage") {
    const operationColumn = getOperationColumn(['edit']);
    tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
    operationsArr.value = ['edit'];
  } else if (value === "task") {
    const operationColumn = getOperationColumn(['viewFile']);
    tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
    operationsArr.value = ['viewFile'];
  }
  pageNum.value = 1;
  pageSize.value = 10;
  getList();
    if (value === "taskManage") {
        const operationColumn = getOperationColumn(['edit']);
        tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
        operationsArr.value = ['edit'];
    } else if (value === "task") {
        const operationColumn = getOperationColumn(['viewFile']);
        tableColumns.value = [...columns.value, ...(operationColumn ? [operationColumn] : [])];
        operationsArr.value = ['viewFile'];
    }
    pageNum.value = 1;
    pageSize.value = 10;
    getList();
};
// æŸ¥è¯¢æ“ä½œ
const handleQuery = () => {
  pageNum.value = 1;
  pageSize.value = 10;
  getList();
    pageNum.value = 1;
    pageSize.value = 10;
    getList();
};
// åˆ†é¡µå¤„理
const handlePagination = (val) => {
    pageNum.value = val.page;
    pageSize.value = val.size;
const pagination = (obj) => {
    pageNum.value = obj.page;
    pageSize.value = obj.limit;
    getList();
};
// èŽ·å–åˆ—è¡¨æ•°æ®
const getList = () => {
  tableLoading.value = true;
  const params = { ...queryParams, size: pageSize.value, current: pageNum.value };
  let apiCall;
  if (activeRadio.value === "task") {
    apiCall = inspectionTaskList(params);
  } else {
    apiCall = timingTaskList(params);
  }
  apiCall.then(res => {
    const rawData = res.data.records || [];
    // å¤„理 inspector å­—段,将字符串转换为数组(适用于所有情况)
    tableData.value = rawData.map(item => {
      const processedItem = { ...item };
      // å¤„理 inspector å­—段
      if (processedItem.inspector) {
        if (typeof processedItem.inspector === 'string') {
          // å­—符串按逗号分割
          processedItem.inspector = processedItem.inspector.split(',').map(s => s.trim()).filter(s => s);
        } else if (!Array.isArray(processedItem.inspector)) {
          // éžæ•°ç»„转为数组
          processedItem.inspector = [processedItem.inspector];
        }
      } else {
        // ç©ºå€¼è®¾ä¸ºç©ºæ•°ç»„
        processedItem.inspector = [];
      }
      return processedItem;
    });
    total.value = res.data.total || 0;
  }).finally(() => {
    tableLoading.value = false;
  });
    tableLoading.value = true;
    const params = { ...queryParams, size: pageSize.value, current: pageNum.value };
    let apiCall;
    if (activeRadio.value === "task") {
        apiCall = inspectionTaskList(params);
    } else {
        apiCall = timingTaskList(params);
    }
    apiCall.then(res => {
        const rawData = res.data.records || [];
        // å¤„理 inspector å­—段,将字符串转换为数组(适用于所有情况)
        tableData.value = rawData.map(item => {
            const processedItem = { ...item };
            // å¤„理 inspector å­—段
            if (processedItem.inspector) {
                if (typeof processedItem.inspector === 'string') {
                    // å­—符串按逗号分割
                    processedItem.inspector = processedItem.inspector.split(',').map(s => s.trim()).filter(s => s);
                } else if (!Array.isArray(processedItem.inspector)) {
                    // éžæ•°ç»„转为数组
                    processedItem.inspector = [processedItem.inspector];
                }
            } else {
                // ç©ºå€¼è®¾ä¸ºç©ºæ•°ç»„
                processedItem.inspector = [];
            }
            return processedItem;
        });
        total.value = res.data.total || 0;
    }).finally(() => {
        tableLoading.value = false;
    });
};
// é‡ç½®æŸ¥è¯¢
const resetQuery = () => {
  for (const key in queryParams) {
    if (!["pageNum", "pageSize"].includes(key)) {
      queryParams[key] = "";
    }
  }
  handleQuery();
    for (const key in queryParams) {
        if (!["pageNum", "pageSize"].includes(key)) {
            queryParams[key] = "";
        }
    }
    handleQuery();
};
// æ–°å¢ž / ç¼–辑
const handleAdd = (row) => {
  const type = row ? 'edit' : 'add';
  nextTick(() => {
    formDia.value?.openDialog(type, row);
  });
    const type = row ? 'edit' : 'add';
    nextTick(() => {
        formDia.value?.openDialog(type, row);
    });
};
// æŸ¥çœ‹é™„ä»¶
const viewFile = (row) => {
  nextTick(() => {
    viewFiles.value?.openDialog(row);
  });
    nextTick(() => {
        viewFiles.value?.openDialog(row);
    });
};
// åˆ é™¤æ“ä½œ
const handleDelete = () => {
  if (!selectedRows.value.length) {
    proxy.$modal.msgWarning("请选择要删除的数据");
    return;
  }
  const deleteIds = selectedRows.value.map(item => item.id);
  proxy.$modal.confirm('是否确认删除所选数据项?').then(() => {
    return delTimingTask(deleteIds);
  }).then(() => {
    proxy.$modal.msgSuccess("删除成功");
    handleQuery();
  }).catch(() => {});
    if (!selectedRows.value.length) {
        proxy.$modal.msgWarning("请选择要删除的数据");
        return;
    }
    const deleteIds = selectedRows.value.map(item => item.id);
    proxy.$modal.confirm('是否确认删除所选数据项?').then(() => {
        return delTimingTask(deleteIds);
    }).then(() => {
        proxy.$modal.msgSuccess("删除成功");
        handleQuery();
    }).catch(() => {});
};
// å¤šé€‰å˜æ›´
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
    selectedRows.value = selection;
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      // æ ¹æ®å½“前选中的标签页调用不同的导出接口
      if (activeRadio.value === "taskManage") {
        // å®šæ—¶ä»»åŠ¡ç®¡ç†
        proxy.download("/timingTask/export", {}, "定时任务管理.xlsx");
      } else if (activeRadio.value === "task") {
        // å®šæ—¶ä»»åŠ¡è®°å½•
        proxy.download("/inspectionTask/export", {}, "定时任务记录.xlsx");
      }
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            // æ ¹æ®å½“前选中的标签页调用不同的导出接口
            if (activeRadio.value === "taskManage") {
                // å®šæ—¶ä»»åŠ¡ç®¡ç†
                proxy.download("/timingTask/export", {}, "定时任务管理.xlsx");
            } else if (activeRadio.value === "task") {
                // å®šæ—¶ä»»åŠ¡è®°å½•
                proxy.download("/inspectionTask/export", {}, "定时任务记录.xlsx");
            }
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
</script>
<style scoped>
.person-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}
.person-tag {
  margin-right: 4px;
  margin-bottom: 2px;
    margin-right: 4px;
    margin-bottom: 2px;
}
.no-data {
  color: #909399;
  font-size: 14px;
    color: #909399;
    font-size: 14px;
}
</style>
src/views/equipmentManagement/ledger/index.vue
@@ -82,15 +82,14 @@
      </PIMTable>
    </div>
    <Modal ref="modalRef" @success="getTableData"></Modal>
        <el-dialog v-model="qrDialogVisible" title="二维码" width="300px">
            <div style="text-align:center;">
                <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" />
                <div style="margin-top:6px;font-size:14px;color:#333;">{{ qrRowData?.deviceName }}</div>
                <div style="margin:10px 0;">
                    <el-button type="primary" @click="downloadQRCode">下载二维码图片</el-button>
                </div>
            </div>
        </el-dialog>
    <el-dialog v-model="qrDialogVisible" title="二维码" width="300px">
      <div style="text-align:center;">
        <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" />
        <div style="margin:10px 0;">
          <el-button type="primary" @click="downloadQRCode">下载二维码图片</el-button>
        </div>
      </div>
    </el-dialog>
  </div>
</template>
@@ -300,44 +299,10 @@
};
const downloadQRCode = () => {
    const name = qrRowData.value?.deviceName || "二维码";
    const img = new Image();
    img.src = qrCodeUrl.value;
    img.onload = () => {
        const padding = 10;
        const qrSize = 200;
        const textHeight = 24; // space for text
        const width = qrSize + padding * 2;
        const height = qrSize + padding * 2 + textHeight;
        const canvas = document.createElement("canvas");
        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext("2d");
        // background
        ctx.fillStyle = "#ffffff";
        ctx.fillRect(0, 0, width, height);
        // draw QR centered
        ctx.drawImage(img, padding, padding, qrSize, qrSize);
        // draw name centered below
        ctx.fillStyle = "#333";
        ctx.font = "14px Arial";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        const maxTextWidth = width - padding * 2;
        let displayName = name;
        // ellipsis if too long
        while (ctx.measureText(displayName).width > maxTextWidth && displayName.length > 0) {
            displayName = displayName.slice(0, -1);
        }
        if (displayName !== name) displayName = displayName + "…";
        ctx.fillText(displayName, width / 2, qrSize + padding + textHeight / 2);
        const dataUrl = canvas.toDataURL("image/png");
        const a = document.createElement("a");
        a.href = dataUrl;
        a.download = `${name}.png`;
        a.click();
    };
  const a = document.createElement("a");
  a.href = qrCodeUrl.value;
  a.download = `${qrRowData.value.deviceName || "二维码"}.png`;
  a.click();
};
onMounted(() => {
src/views/equipmentManagement/measurementEquipment/components/calibrationDia.vue
@@ -67,7 +67,9 @@
                            <el-select
                                v-model="form.userId"
                                placeholder="请选择"
                                disabled
                filterable
                default-first-option
                :reserve-keyword="false"
                                clearable
                            >
                                <el-option
src/views/equipmentManagement/measurementEquipment/components/formDia.vue
@@ -64,7 +64,9 @@
                                v-model="form.userId"
                                placeholder="请选择"
                                clearable
                                disabled
                filterable
                default-first-option
                :reserve-keyword="false"
                            >
                                <el-option
                                    v-for="item in userList"
src/views/equipmentManagement/measurementEquipment/index.vue
@@ -148,13 +148,13 @@
                    openCalibrationDia("verifying", row);
                },
            },
            {
                name: "附件",
                type: "text",
                clickFun: (row) => {
          openFilesFormDia(row);
                },
            },
            // {
            //     name: "附件",
            //     type: "text",
            //     clickFun: (row) => {
      //     openFilesFormDia(row);
            //     },
            // },
        ],
    },
]);
@@ -221,12 +221,6 @@
const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
        // æ£€æŸ¥æ˜¯å¦æœ‰ä»–人维护的数据
        const unauthorizedData = selectedRows.value.filter(item => item.userId !== userStore.id);
        if (unauthorizedData.length > 0) {
            proxy.$modal.msgWarning("不可删除他人维护的数据");
            return;
        }
        ids = selectedRows.value.map((item) => item.id);
    } else {
        proxy.$modal.msgWarning("请选择数据");
src/views/equipmentManagement/repair/Form/MaintainForm.vue
@@ -6,6 +6,13 @@
    <el-form-item label="维修结果">
      <el-input v-model="form.maintenanceResult" placeholder="请输入维修结果" />
    </el-form-item>
    <el-form-item label="报修状态">
      <el-select v-model="form.status">
        <el-option label="待报修" :value="0"></el-option>
        <el-option label="完结" :value="1"></el-option>
        <el-option label="失败" :value="2"></el-option>
      </el-select>
    </el-form-item>
    <el-form-item label="维修日期">
      <el-date-picker
        v-model="form.maintenanceTime"
@@ -34,6 +41,7 @@
  maintenanceName: undefined, // ç»´ä¿®åç§°
  maintenanceResult: undefined, // ç»´ä¿®ç»“æžœ
  maintenanceTime: undefined, // ç»´ä¿®æ—¥æœŸ
  status: 0,
});
const setForm = (data) => {
src/views/equipmentManagement/repair/Form/RepairForm.vue
@@ -40,6 +40,19 @@
          <el-input v-model="form.repairName" placeholder="请输入报修人" />
        </el-form-item>
      </el-col>
    </el-row>
    <el-row v-if="id">
      <el-col :span="12">
        <el-form-item label="报修状态">
          <el-select v-model="form.status">
            <el-option label="待维修" :value="0"></el-option>
            <el-option label="完结" :value="1"></el-option>
            <el-option label="失败" :value="2"></el-option>
          </el-select>
        </el-form-item>
      </el-col>
    </el-row>
    <el-row>
      <el-col :span="24">
        <el-form-item label="故障现象">
          <el-input
@@ -58,6 +71,8 @@
import useFormData from "@/hooks/useFormData";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import useUserStore from "@/store/modules/user";
const { id } = defineProps(["id"])
defineOptions({
  name: "设备报修表单",
@@ -78,6 +93,7 @@
  repairTime: undefined, // æŠ¥ä¿®æ—¥æœŸ
  repairName: userStore.nickName, // æŠ¥ä¿®äºº
  remark: undefined, // æ•…障现象
  status: 0, // æŠ¥ä¿®çŠ¶æ€
});
const setDeviceModel = (id) => {
@@ -96,6 +112,7 @@
  form.repairTime = data.repairTime;
  form.repairName = data.repairName;
  form.remark = data.remark;
  form.status = data.status;
};
// onMounted(() => {
src/views/equipmentManagement/repair/Modal/RepairModal.vue
@@ -1,6 +1,6 @@
<template>
  <el-dialog v-model="visible" :title="modalOptions.title" @close="close">
    <RepairForm ref="repairFormRef" />
    <RepairForm ref="repairFormRef" :id="id" />
    <template #footer>
            <el-button type="primary" @click="sendForm" :loading="loading">
                {{ modalOptions.confirmText }}
src/views/equipmentManagement/repair/index.vue
@@ -106,8 +106,9 @@
        @pagination="changePage"
      >
        <template #statusRef="{ row }">
          <el-tag v-if="row.status === 2" type="danger">失败</el-tag>
          <el-tag v-if="row.status === 1" type="success">完结</el-tag>
          <el-tag v-if="row.status === 0" type="danger">待维修</el-tag>
          <el-tag v-if="row.status === 0" type="warning">待维修</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button
src/views/equipmentManagement/upkeep/Form/MaintenanceForm.vue
@@ -17,11 +17,22 @@
        style="width: 100%"
      />
    </el-form-item>
    <el-form-item label="保养状态">
      <el-select v-model="form.status">
        <el-option label="待保养" :value="0"></el-option>
        <el-option label="完结" :value="1"></el-option>
        <el-option label="失败" :value="2"></el-option>
      </el-select>
    </el-form-item>
    <el-form-item label="保养结果">
      <el-select v-model="form.maintenanceResult" placeholder="请选择保养结果">
      <!-- <el-select v-model="form.maintenanceResult" placeholder="请选择保养结果">
        <el-option label="完好" :value="1"></el-option>
        <el-option label="ç»´ä¿®" :value="0"></el-option>
      </el-select>
      </el-select> -->
      <el-input
        v-model="form.maintenanceResult"
        placeholder="请输入保养结果"
        type="text" />
    </el-form-item>
  </el-form>
</template>
@@ -40,6 +51,7 @@
  maintenanceActuallyName: undefined, // å®žé™…保养人
  maintenanceActuallyTime: undefined, // å®žé™…保养日期
  maintenanceResult: undefined, // ä¿å…»ç»“æžœ
  status: 0, // ä¿å…»çŠ¶æ€
});
const setForm = (data) => {
src/views/equipmentManagement/upkeep/Form/PlanForm.vue
@@ -5,6 +5,9 @@
        v-model="form.deviceLedgerId"
        @change="setDeviceModel"
        placeholder="请选择设备"
        filterable
        default-first-option
        :reserve-keyword="false"
      >
        <el-option
          v-for="(item, index) in deviceOptions"
@@ -20,6 +23,30 @@
        placeholder="请输入规格型号"
        disabled
      />
    </el-form-item>
    <el-form-item label="录入人">
      <el-select
        v-model="form.createUser"
        placeholder="请选择"
        filterable
        default-first-option
        :reserve-keyword="false"
        clearable
      >
        <el-option
          v-for="item in userList"
          :key="item.userId"
          :label="item.userName"
          :value="item.userId"
        />
      </el-select>
    </el-form-item>
    <el-form-item v-if="id" label="保修状态">
      <el-select v-model="form.status">
        <el-option label="待保修" :value="0"></el-option>
        <el-option label="完结" :value="1"></el-option>
        <el-option label="失败" :value="2"></el-option>
      </el-select>
    </el-form-item>
    <el-form-item label="计划保养日期">
      <el-date-picker
@@ -40,6 +67,7 @@
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
import { onMounted } from "vue";
import dayjs from "dayjs";
import { userListNoPage } from "@/api/system/user.js";
defineOptions({
  name: "计划表单",
@@ -51,11 +79,15 @@
  deviceOptions.value = data;
};
const { id } = defineProps(['id']);
const { form, resetForm } = useFormData({
  deviceLedgerId: undefined, // è®¾å¤‡Id
  deviceName: undefined, // è®¾å¤‡åç§°
  deviceModel: undefined, // è§„格型号
  maintenancePlanTime: undefined, // è®¡åˆ’保养日期
  createUser: undefined, // å½•入人
  status: 0, //保修状态
});
const setDeviceModel = (id) => {
@@ -75,15 +107,23 @@
  form.deviceLedgerId = data.deviceLedgerId;
  form.deviceName = data.deviceName;
  form.deviceModel = data.deviceModel;
  form.createUser = Number(data.createUser);
  form.status = data.status;
  form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
    "YYYY-MM-DD HH:mm:ss"
  );
};
// ç”¨æˆ·åˆ—表
const userList = ref([]);
const loadForm = () => {};
onMounted(() => {
  loadDeviceName();
  userListNoPage().then((res) => {
    userList.value = res.data;
  });
});
defineExpose({
src/views/equipmentManagement/upkeep/Modal/PlanModal.vue
@@ -5,7 +5,7 @@
    width="30%"
    @close="close"
  >
    <PlanForm ref="planFormRef"></PlanForm>
    <PlanForm ref="planFormRef" :id="id"></PlanForm>
    <template #footer>
            <el-button type="primary" @click="sendForm" :loading="loading">
                {{ modalOptions.confirmText }}
src/views/equipmentManagement/upkeep/index.vue
@@ -86,16 +86,18 @@
        @pagination="changePage"
      >
        <template #maintenanceResultRef="{ row }">
          <el-tag v-if="row.maintenanceResult === 1" type="success">
          <div>{{ row.maintenanceResult || '-' }}</div>
          <!-- <el-tag v-if="row.maintenanceResult === 1" type="success">
            å®Œå¥½
          </el-tag>
          <el-tag v-if="row.maintenanceResult === 0" type="danger">
            ç»´ä¿®
          </el-tag>
          </el-tag> -->
        </template>
        <template #statusRef="{ row }">
          <el-tag v-if="row.status === 2" type="danger">失败</el-tag>
          <el-tag v-if="row.status === 1" type="success">完结</el-tag>
          <el-tag v-if="row.status === 0" type="danger">待保养</el-tag>
          <el-tag v-if="row.status === 0" type="warning">待保养</el-tag>
        </template>
        <template #operation="{ row }">
          <el-button
@@ -186,13 +188,13 @@
    align: "center",
    prop: "createUserName",
  },
  {
    label: "录入日期",
    align: "center",
    prop: "createTime",
    formatData: (cell) => dayjs(cell).format("YYYY-MM-DD HH:mm:ss"),
    width: 200,
  },
  // {
  //   label: "录入日期",
  //   align: "center",
  //   prop: "createTime",
  //   formatData: (cell) => dayjs(cell).format("YYYY-MM-DD HH:mm:ss"),
  //   width: 200,
  // },
  {
    label: "实际保养人",
    align: "center",
src/views/financialManagement/expenseManagement/index.vue
@@ -106,7 +106,6 @@
  listPage,
  {
    expenseMethod: undefined,
    entryDate: undefined,
  },
  [
    {
@@ -227,11 +226,9 @@
const changeDaterange = (value) => {
  if (value) {
    filters.entryDate = value;
    filters.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    filters.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  } else {
    filters.entryDate = null;
    filters.entryDateStart = undefined;
    filters.entryDateEnd = undefined;
  }
@@ -259,6 +256,12 @@
};
onMounted(() => {
  filters.entryDate = [
    dayjs().format("YYYY-MM-DD"),
    dayjs().add(1, "day").format("YYYY-MM-DD"),
  ]
  filters.entryDateStart = dayjs().format("YYYY-MM-DD")
  filters.entryDateEnd = dayjs().add(1, "day").format("YYYY-MM-DD")
  getTableData();
});
</script>
src/views/financialManagement/financialStatements/index.vue
@@ -1,4 +1,4 @@
 <template>
<template>
  <div style="padding: 20px;">
    <!-- é¡µé¢æ ‡é¢˜å’Œæ—¥æœŸç­›é€‰ -->
    <div class="w-full md:w-auto flex items-center gap-3" style="margin-bottom: 20px;">
@@ -10,7 +10,7 @@
        range-separator="至"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
        clearable
        :default-value="[new Date(firstDayOfMonth), new Date()]"
        @change="handleDateChange"
        class="w-full md:w-auto"
        style="margin-right: 30px;"
@@ -137,7 +137,8 @@
import dayjs from "dayjs";
// æ—¥æœŸèŒƒå›´
const dateRange = ref(null);
const dateRange = ref([]);
const firstDayOfMonth = ref(null);
const chartStyle = {
    width: '100%',
    height: '100%', // è®¾ç½®å›¾è¡¨å®¹å™¨çš„高度
@@ -319,9 +320,6 @@
})
const getData = async () => {
  if (!dateRange.value || !dateRange.value.length) {
    return;
  }
  try {
    const {code,data} = await reportForms({entryDateStart:dateRange.value[0], entryDateEnd:dateRange.value[1]});
    if(code === 200) {
@@ -372,22 +370,30 @@
};
// åˆå§‹åŒ–
// åˆå§‹åŒ–日期范围(默认当月)
onMounted(() => {
  // ä¸è®¾ç½®é»˜è®¤æ—¥æœŸï¼Œç”±ç”¨æˆ·æ‰‹åŠ¨é€‰æ‹©
  const today = new Date();
  const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
  firstDayOfMonth.value = firstDay;
  dateRange.value = [dayjs(firstDay).format("YYYY-MM-DD"), dayjs(today).format("YYYY-MM-DD")];
  getData()
});
// å¤„理日期范围变化
const handleDateChange = (newRange) => {
  dateRange.value = newRange;
  if (newRange && newRange.length === 2) {
    dateRange.value = newRange;
    getData()
  }
};
// é‡ç½®æ—¥æœŸèŒƒå›´
const resetDateRange = () => {
  dateRange.value = null;
  const today = new Date();
  const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
  dateRange.value = [dayjs(firstDay).format("YYYY-MM-DD"), dayjs(today).format("YYYY-MM-DD")];
  getData()
};
</script>
src/views/financialManagement/revenueManagement/index.vue
@@ -106,7 +106,6 @@
  listPage,
  {
    incomeMethod: undefined,
    entryDate: undefined,
  },
  [
    {
@@ -227,11 +226,9 @@
const changeDaterange = (value) => {
  if (value) {
    filters.entryDate = value;
    filters.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    filters.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  } else {
    filters.entryDate = null;
    filters.entryDateStart = undefined;
    filters.entryDateEnd = undefined;
  }
@@ -259,6 +256,12 @@
};
onMounted(() => {
  filters.entryDate = [
    dayjs().format("YYYY-MM-DD"),
    dayjs().add(1, "day").format("YYYY-MM-DD"),
  ]
  filters.entryDateStart = dayjs().format("YYYY-MM-DD")
  filters.entryDateEnd = dayjs().add(1, "day").format("YYYY-MM-DD")
  getTableData();
});
</script>
src/views/index.vue
@@ -13,9 +13,13 @@
                            <div class="company-meta">{{userStore.roleName}}</div>
                        </div>
                        <div style="display: flex;align-items: center;gap: 8px">
                            <el-icon color="#5053B5" size="22"><Clock /></el-icon>
                            <span>登陆日期:{{userStore.currentLoginTime}}</span>
                        </div>
                        <el-icon color="#5053B5" size="22"><Clock /></el-icon>
                        <span>登陆日期:{{userStore.currentLoginTime}}</span>
                    </div>
                    <div style="display: flex;align-items: center;gap: 8px">
                        <el-icon color="#5053B5" size="22"><Calendar /></el-icon>
                        <span>排班时间:{{scheduleTime}}</span>
                    </div>
                    </div>
                </div>
                <div class="data-cards">
@@ -24,11 +28,11 @@
                        <div class="data-num">
                            <div>
                                <div class="data-desc">本月销售额/元</div>
                                <div class="data-value" :title="businessInfo.monthSaleMoney">{{businessInfo.monthSaleMoney}}</div>
                                <div class="data-value">{{businessInfo.monthSaleMoney}}</div>
                            </div>
                            <div>
                                <div class="data-desc">未开票金额/元</div>
                                <div class="data-value" :title="businessInfo.monthSaleHaveMoney">{{businessInfo.monthSaleHaveMoney}}</div>
                                <div class="data-value">{{businessInfo.monthSaleHaveMoney}}</div>
                            </div>
                        </div>
                        
@@ -38,24 +42,24 @@
                        <div class="data-num">
                            <div>
                                <div class="data-desc">本月采购额/元</div>
                                <div class="data-value" :title="businessInfo.monthPurchaseMoney">{{businessInfo.monthPurchaseMoney}}</div>
                                <div class="data-value">{{businessInfo.monthPurchaseMoney}}</div>
                            </div>
                            <div>
                                <div class="data-desc">待付款金额/元</div>
                                <div class="data-value" :title="businessInfo.monthPurchaseHaveMoney">{{businessInfo.monthPurchaseHaveMoney}}</div>
                                <div class="data-value">{{businessInfo.monthPurchaseHaveMoney}}</div>
                            </div>
                        </div>
                    </div>
                    <div class="data-card inventory">
                        <div class="data-title">采购库存</div>
                        <div class="data-title">库存数据</div>
                        <div class="data-num">
                            <div>
                                <div class="data-desc">当前库存总量/ä»¶</div>
                                <div class="data-value" :title="businessInfo.inventoryNum">{{businessInfo.inventoryNum}}</div>
                                <div class="data-value">{{businessInfo.inventoryNum}}</div>
                            </div>
                            <div>
                                <div class="data-desc">今日入库/ä»¶</div>
                                <div class="data-value" :title="businessInfo.todayInventoryNum">{{businessInfo.todayInventoryNum}}</div>
                                <div class="data-value">{{businessInfo.todayInventoryNum}}</div>
                            </div>
                        </div>
                    </div>
@@ -118,11 +122,11 @@
            <div class="main-panel">
                <div style="display: flex;justify-content: space-between;">
                    <div class="section-title">应收应付统计</div>
                    <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable">
                        <el-radio-button label="按周" :value="1" />
                        <el-radio-button label="按月" :value="2" />
                        <el-radio-button label="按季度" :value="3" />
                    </el-radio-group>
<!--                    <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable">-->
<!--                        <el-radio-button label="按周" :value="1" />-->
<!--                        <el-radio-button label="按月" :value="2" />-->
<!--                        <el-radio-button label="按季度" :value="3" />-->
<!--                    </el-radio-group>-->
                </div>
                <Echarts ref="chart"
                                 :color="barColors2"
@@ -165,17 +169,23 @@
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ref, reactive, onMounted } from 'vue'
import { ElNotification } from 'element-plus'
import Echarts from "@/components/Echarts/echarts.vue";
import * as echarts from 'echarts';
import useUserStore from "@/store/modules/user.js";
import { Clock, Calendar } from '@element-plus/icons-vue'
import {
    analysisCustomerContractAmounts, getAmountHalfYear,
    getBusiness,
    homeTodos,
    qualityStatistics,
    statisticsReceivablePayable
    statisticsReceivablePayable,
    approveAndDeviceTodos,
    noticesCount
} from "@/api/viewIndex.js";
import { getCurrentUserLatestScheduling } from "@/api/personnelManagement/scheduling.js";
import dayjs from "dayjs";
const userStore = useUserStore()
@@ -287,7 +297,7 @@
    formatter: function (params) {
        // åŠ¨æ€ç”Ÿæˆæç¤ºä¿¡æ¯ï¼ŒåŸºäºŽæ•°æ®é¡¹çš„ name å±žæ€§
        const description = params.name === '本月回款金额' ? '本月回款金额' : '应收款金额';
        return `${description} ${formattedNumber(params.value)}元 ${params.percent}%`;
        return `${description} ${formatNumber(params.value)}元 ${params.percent}%`;
    },
    position: 'right'
})
@@ -341,6 +351,10 @@
const todoList = ref([])
const radio1 = ref(1)
// æŽ’班时间
const scheduleTime = ref('')
const scheduleInfo = ref({})
// å›¾è¡¨å¼•用
const barChart = ref(null)
const lineChart = ref(null)
@@ -358,6 +372,9 @@
    statisticsReceivable()
    qualityStatisticsInfo()
    getAmountHalfYearNum()
    getCurrentUserSchedule()
    getApproveAndDeviceTodos()
    getOngoingAnnouncementNoticeNumber()
})
// æ•°æ®ç»Ÿè®¡
const getBusinessData = () => {
@@ -365,13 +382,6 @@
        businessInfo.value = {...res.data}
    })
}
const formattedNumber = (row, column, cellValue) => {
    // å¦‚果只传了一个参数,直接格式化该参数
    if (column === undefined && cellValue === undefined) {
        return parseFloat(row).toFixed(2)
    }
    return parseFloat(cellValue).toFixed(2)
};
// åˆåŒé‡‘额
const analysisCustomer = () => {
    analysisCustomerContractAmounts().then((res) => {
@@ -416,6 +426,35 @@
        qualityStatisticsObject.value.factoryNum = res.data.factoryNum
    })
}
// èŽ·å–å½“å‰ç”¨æˆ·æŽ’ç­ä¿¡æ¯
const getCurrentUserSchedule = async () => {
  try {
    const res = await getCurrentUserLatestScheduling()
    if (res.data) {
      const currentSchedule = res.data
      scheduleInfo.value = currentSchedule
      // æ ¼å¼åŒ–排班时间显示
      if (currentSchedule.startTime && currentSchedule.endTime) {
        scheduleTime.value = `${currentSchedule.startTime} - ${currentSchedule.endTime}`
      } else if (currentSchedule.workStartTime && currentSchedule.workEndTime) {
        const startTime = dayjs(currentSchedule.workStartTime).format('HH:mm')
        const endTime = dayjs(currentSchedule.workEndTime).format('HH:mm')
        scheduleTime.value = `${startTime} - ${endTime}`
      } else {
        scheduleTime.value = '今日无排班'
      }
    } else {
      scheduleTime.value = '今日无排班'
      scheduleInfo.value = {}
    }
  } catch (error) {
    console.error('获取排班信息失败:', error)
    scheduleTime.value = '获取排班信息失败'
  }
}
const getAmountHalfYearNum = async () => {
    const res = await getAmountHalfYear()
    console.log(res)
@@ -432,7 +471,7 @@
    xAxis2.value[0].data = monthName.map(item => item.replace(/~/g, '\n~'));
    lineSeries.value = [
        {
            name: '回款',
            name: '开票',
            type: 'line',
            data: receiptAmount,
            stack: 'Total',
@@ -461,7 +500,7 @@
            showSymbol: true,
        },
        {
            name: '开票',
            name: '回款',
            type: 'line',
            data: invoiceAmount,
            stack: 'Total',
@@ -490,6 +529,44 @@
            },
        }
    ]
}
// ååŒå¾…审批和报修待办事项
const getApproveAndDeviceTodos = async () => {
    try {
        const res = await approveAndDeviceTodos()
        const { approveTodo, deviceRepairTodo } = res.data
        // æ˜¾ç¤ºé€šçŸ¥
        ElNotification({
                title: '待办事项提醒',
                message: `当前有${approveTodo}条待审批事项,${deviceRepairTodo}条待维修事项`,
                type: 'warning',
                duration: 5000
            })
    } catch (error) {
        console.error('获取协同待办事项失败:', error)
    }
}
// è¿›è¡Œä¸­å…¬å‘Šé€šçŸ¥
const getOngoingAnnouncementNoticeNumber = async () => {
    try {
        const res = await noticesCount()
        // const { approveTodo, deviceRepairTodo } = res.data
        const _noticesCount = res.data
        if(!_noticesCount){
            return
        }
        // æ˜¾ç¤ºé€šçŸ¥
        ElNotification({
                title: '通知公告通知',
                message: `当前有${_noticesCount}条公告通知,注意查看`,
                type: 'warning',
                duration: 5000
            })
    } catch (error) {
        console.error('获取协同待办事项失败:', error)
    }
}
</script>
@@ -609,9 +686,6 @@
    color: #FFFFFF;
}
.data-value {
    max-width: 100px;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 18px;
    font-weight: 500;
    margin: 10px 0;
src/views/inventoryManagement/dispatchLog/index.vue
@@ -1,7 +1,7 @@
<template>
    <div class="app-container">
        <el-tabs v-model="activeTab" @tab-change="handleTabChange">
            <!-- <el-tab-pane label="采购出库" name="production">
            <el-tab-pane label="成品出库" name="production">
                <div class="search_form">
                    <div>
                        <span class="search_title ml10">出库日期:</span>
@@ -44,7 +44,8 @@
                    >
                        <el-table-column align="center" type="selection" width="55" />
                        <el-table-column align="center" label="序号" type="index" width="60" />
                        <el-table-column label="出库日期" prop="createTime" min-width="120" show-overflow-tooltip />
                        <el-table-column label="出库日期" prop="createTime" width="120" show-overflow-tooltip />
                        <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
                        <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
                        <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
                        <el-table-column label="单位" prop="unit" width="80" show-overflow-tooltip />
@@ -62,9 +63,9 @@
                        @pagination="paginationChange"
                    />
                </div>
            </el-tab-pane> -->
            </el-tab-pane>
            <el-tab-pane label="采购出库" name="purchase">
            <el-tab-pane label="原料出库" name="purchase">
                <div class="search_form">
                    <div>
                        <span class="search_title ml10">出库日期:</span>
@@ -107,18 +108,15 @@
                    >
                        <el-table-column align="center" type="selection" width="55" />
                        <el-table-column align="center" label="序号" type="index" width="60" />
                        <el-table-column label="出库日期" prop="createTime" show-overflow-tooltip />
                        <el-table-column label="产品名称" prop="productCategory" show-overflow-tooltip />
                        <el-table-column label="产品高度" prop="specificationModel" width="100" show-overflow-tooltip>
                        <template #default="scope">
                            <div>{{ scope.row.specificationModel }}{{ scope.row.unit }}</div>
                        </template>
                        </el-table-column>
                        <el-table-column label="出库数量/ä»¶" prop="inboundNum" show-overflow-tooltip />
                        <el-table-column label="每件数量/支" prop="boxNum" width="100" show-overflow-tooltip />
                        <el-table-column label="单价(元)/支" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                        <el-table-column label="纸箱规格" prop="cartonSpecifications" width="150"></el-table-column>
                        <el-table-column label="出库人" prop="createBy" show-overflow-tooltip />
                        <el-table-column label="出库日期" prop="createTime" width="120" show-overflow-tooltip />
                        <el-table-column label="采购合同号" prop="purchaseContractNumber" width="180" show-overflow-tooltip />
                        <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
                        <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
                        <el-table-column label="单位" prop="unit" width="80" show-overflow-tooltip />
                        <el-table-column label="出库数量" prop="inboundNum" width="100" show-overflow-tooltip />
                        <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                        <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" width="150"></el-table-column>
                        <el-table-column label="出库人" prop="createBy" width="80" show-overflow-tooltip />
                    </el-table>
                    <pagination
                        v-show="total > 0"
@@ -131,7 +129,7 @@
                </div>
            </el-tab-pane>
            <el-tab-pane label="成品出库" name="manual">
            <el-tab-pane label="材料出库" name="manual">
                <div class="search_form">
                    <div>
                        <span class="search_title ml10">出库日期:</span>
@@ -174,23 +172,16 @@
                    >
                        <el-table-column align="center" type="selection" width="55" />
                        <el-table-column align="center" label="序号" type="index" width="60" />
                        <el-table-column label="产品图片" width="100" align="center">
                            <template #default="scope">
                                <img class="barcode-img" :src="javaApiUrl+scope.row.url"></img>
                            </template>
                        </el-table-column>
                        <el-table-column label="出库日期" prop="createTime" width="150" show-overflow-tooltip />
                        <el-table-column label="产品名称" prop="productCategory" show-overflow-tooltip />
                        <el-table-column label="产品高度" prop="specificationModel" width="100" show-overflow-tooltip>
                        <template #default="scope">
                            <div>{{ scope.row.specificationModel }}{{ scope.row.unit }}</div>
                        </template>
                        </el-table-column>
                        <el-table-column label="出库数量/ä»¶" prop="inboundNum" width="150" show-overflow-tooltip />
                        <el-table-column label="每件数量/支" prop="boxNum" width="100" show-overflow-tooltip />
                        <el-table-column label="单价(美元)/ä»¶" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                        <el-table-column label="纸箱规格" prop="cartonSpecifications" width="150"></el-table-column>
                        <el-table-column label="出库人" prop="createBy" width="150" show-overflow-tooltip />
                        <el-table-column label="出库日期" prop="createTime" show-overflow-tooltip width="130"/>
                        <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
                        <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
                        <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
                        <el-table-column label="单位" prop="unit" show-overflow-tooltip />
                        <el-table-column label="物品类型" prop="itemType" show-overflow-tooltip />
                        <el-table-column label="出库数量" prop="inboundNum" show-overflow-tooltip />
                        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                        <el-table-column label="总价(元)" prop="taxInclusiveTotalPrice" width="150"></el-table-column>
                        <el-table-column label="出库人" prop="createBy" show-overflow-tooltip />
                    </el-table>
                    <pagination
                        v-show="total > 0"
@@ -227,7 +218,7 @@
                    <div v-for="(item, index) in printData" :key="index" class="print-page">
                        <div class="delivery-note">
                            <div class="header">
                                <div class="company-name">天津双奇点有限责任公司</div>
                                <div class="company-name">海川开心食品有限公司</div>
                                <div class="document-title">零售发货单</div>
                            </div>
                            
@@ -240,7 +231,7 @@
                                    <div>
                                        
                                        <span class="label">客户名称:</span>
                                        <span class="value">{{ item.supplierName }}</span>
                                        <span class="value">{{ item.supplierName || '张爱有' }}</span>
                                    </div>
                                </div>
                                <div class="info-row">
@@ -254,32 +245,32 @@
                                    <thead>
                                    <tr>
                                        <th>产品名称</th>
                                        <th>产品高度</th>
                                        <th>单价(美元)/ä»¶</th>
                                        <th>零售数量/ä»¶</th>
                                        <th>每件数量/支</th>
                                        <th>纸箱规格</th>
                                        <th>规格型号</th>
                                        <th>单位</th>
                                        <th>单价</th>
                                        <th>零售数量</th>
                                        <th>零售金额</th>
                                    </tr>
                                    </thead>
                                    <tbody>
                                    <tr>
                                        <td>{{ item.productCategory || '' }}</td>
                                        <td>{{ item.specificationModel || '' }}{{ item.unit || '' }}</td>
                                        <td>{{ item.productCategory || '砂灰砖' }}</td>
                                        <td>{{ item.specificationModel || '标准' }}</td>
                                        <td>{{ item.unit || '块' }}</td>
                                        <td>{{ item.taxInclusiveUnitPrice || '0' }}</td>
                                        <td>{{ item.inboundNum || '0' }}</td>
                                        <td>{{ item.boxNum || '0' }}</td>
                                        <td>{{ item.cartonSpecifications || '0' }}</td>
                                        <td>{{ item.inboundNum || '2000' }}</td>
                                        <td>{{ item.taxInclusiveTotalPrice || '0' }}</td>
                                    </tr>
                                    </tbody>
                                    <tfoot>
                                    <!-- <tr>
                                    <tr>
                                        <td class="label">合计</td>
                                        <td class="total-value"></td>
                                        <td class="total-value"></td>
                                        <td class="total-value"></td>
                                        <td class="total-value">{{ item.inboundNum || '2000' }}</td>
                                        <td class="total-value">{{ item.taxInclusiveTotalPrice || '0' }}</td>
                                    </tr> -->
                                    </tr>
                                    </tfoot>
                                </table>
                            </div>
@@ -333,12 +324,11 @@
    getStockInPageByProduct,
    getStockInPageByCustom,
} from "@/api/inventoryManagement/stockIn.js";
const javaApiUrl = __BASE_API__;
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
const tableData = ref([]);
const activeTab = ref('purchase');
const activeTab = ref('production');
const selectedRows = ref([]);
const tableLoading = ref(false);
const page = reactive({
@@ -440,7 +430,7 @@
    page.current = 1
    searchForm.value.supplierName = ''
    searchForm.value.customerName = ''
    searchForm.value.timeStr = getCurrentDate() // é‡ç½®ä¸ºå½“前日期
    searchForm.value.timeStr = ''
    selectedRows.value = []
      searchForm.value.productCategory = ''
    getList()
@@ -671,7 +661,7 @@
      <div class="print-page">
        <div class="delivery-note">
          <div class="header">
            <div class="company-name">双奇点有限责任公司</div>
            <div class="company-name">海川开心食品有限公司</div>
            <div class="document-title">零售发货单</div>
          </div>
          
@@ -683,41 +673,49 @@
              </div>
              <div>
                <span class="label">客户名称:</span>
                <span class="value">${item.supplierName}</span>
                <span class="value">${item.supplierName || '张爱有'}</span>
              </div>
            </div>
            <div class="info-row">
              <span class="label">单号:</span>
              <span class="value">${item.code}</span>
              <span class="value">${item.code || ''}</span>
            </div>
          </div>
          <div class="table-section">
            <table class="product-table">
                <thead>
                <tr>
                    <th>产品名称</th>
                    <th>产品高度</th>
                    <th>单价(美元)/ä»¶</th>
                    <th>零售数量/ä»¶</th>
                    <th>每件数量/支</th>
                    <th>纸箱规格</th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <td>{{ item.productCategory || '' }}</td>
                    <td>{{ item.specificationModel || '' }}{{ item.unit || '' }}</td>
                    <td>{{ item.taxInclusiveUnitPrice || '0' }}</td>
                    <td>{{ item.inboundNum || '0' }}</td>
                    <td>{{ item.boxNum || '0' }}</td>
                    <td>{{ item.cartonSpecifications || '0' }}</td>
                </tr>
                </tbody>
                <tfoot>
                </tfoot>
            </table>
        </div>
            <table class="product-table">
              <thead>
                <tr>
                  <th>产品名称</th>
                  <th>规格型号</th>
                  <th>单位</th>
                  <th>单价</th>
                  <th>零售数量</th>
                  <th>零售金额</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>${item.productCategory || '砂灰砖'}</td>
                  <td>${item.specificationModel || '标准'}</td>
                  <td>${item.unit || '块'}</td>
                  <td>${item.taxInclusiveUnitPrice || '0'}</td>
                  <td>${item.inboundNum || '2000'}</td>
                  <td>${item.taxInclusiveTotalPrice || '0'}</td>
                </tr>
              </tbody>
              <tfoot>
                <tr>
                  <td class="label">合计</td>
                  <td class="total-value"></td>
                  <td class="total-value"></td>
                  <td class="total-value"></td>
                  <td class="total-value">${item.inboundNum || '2000'}</td>
                  <td class="total-value">${item.taxInclusiveTotalPrice || '0'}</td>
                </tr>
              </tfoot>
            </table>
          </div>
          <div class="footer-section">
            <div class="footer-row">
@@ -970,12 +968,6 @@
    .print-page:last-child {
        page-break-after: avoid;
    }
}
.barcode-img{
  width: 80px;
  height: 80px;
  border-radius: 5px;
  background-color: #F5F5F5;
}
</style>
src/views/inventoryManagement/issueManagement/index.vue
@@ -1,19 +1,9 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <!-- <el-tab-pane label="采购出库" name="production">
      <el-tab-pane label="成品出库" name="production">
        <div class="search_form">
          <div>
            <span class="search_title ml10">入库日期:</span>
            <el-date-picker
              v-model="searchForm.timeStr"
              type="date"
              placeholder="请选择日期"
              value-format="YYYY-MM-DD"
              format="YYYY-MM-DD"
              clearable
              @change="handleQuery"
            />
            <span class="search_title ml10">产品大类:</span>
            <el-input
              v-model="searchForm.productCategory"
@@ -32,36 +22,25 @@
            :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
            :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
            <el-table-column align="center" label="序号" type="index" width="60" />
            <el-table-column label="入库时间" prop="createTime" width="100" show-overflow-tooltip />
            <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
            <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
            <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
            <el-table-column label="入库数量" prop="inboundNum" width="90" show-overflow-tooltip />
            <el-table-column label="剩余库存" prop="inboundNum0" width="90" show-overflow-tooltip />
            <el-table-column fixed="right" label="操作" min-width="60" align="center">
              <template #default="scope">
                <el-button link type="primary" size="small" @click="openForm(scope.row);">领用</el-button>
                <el-button link type="primary" size="small" @click="openForm(scope.row);">发货</el-button>
              </template>
            </el-table-column>
          </el-table>
          <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
            :page="page.current" :limit="page.size" @pagination="paginationChange" />
        </div>
      </el-tab-pane> -->
      </el-tab-pane>
     <el-tab-pane label="采购出库" name="purchase">
     <el-tab-pane label="原料出库" name="purchase">
       <div class="search_form">
         <div>
           <span class="search_title ml10">入库日期:</span>
           <el-date-picker
             v-model="searchForm.timeStr"
             type="date"
             placeholder="请选择日期"
             value-format="YYYY-MM-DD"
             format="YYYY-MM-DD"
             clearable
             @change="handleQuery"
           />
            <span class="search_title ml10">产品大类:</span>
            <el-input
              v-model="searchForm.productCategory"
@@ -81,22 +60,16 @@
           :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
           <el-table-column align="center" type="selection" width="55" />
           <el-table-column align="center" label="序号" type="index" width="60" />
           <el-table-column label="入库时间" prop="createTime" width="100" show-overflow-tooltip />
            <el-table-column label="产品名称" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="产品高度" prop="specificationModel" width="100" show-overflow-tooltip>
              <template #default="scope">
                <div>{{ scope.row.specificationModel }}{{ scope.row.unit }}</div>
              </template>
            </el-table-column>
                        <el-table-column label="入库数量/ä»¶" prop="inboundNum" width="100" show-overflow-tooltip />
                        <el-table-column label="每件数量/支" prop="boxNum" width="100" show-overflow-tooltip />
            <el-table-column label="剩余库存" prop="inboundNum0" width="90" show-overflow-tooltip />
                        <el-table-column label="单价(元)/ä»¶" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
            <el-table-column label="纸箱规格" prop="cartonSpecifications" width="150"></el-table-column>
            <el-table-column label="入库人" prop="createBy" width="80" show-overflow-tooltip />
                     <el-table-column label="采购合同号" prop="purchaseContractNumber" width="180" show-overflow-tooltip />
                     <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
           <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
           <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
           <el-table-column label="剩余库存" prop="inboundNum0" width="90" show-overflow-tooltip />
                     <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                     <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" width="150"></el-table-column>
           <el-table-column fixed="right" label="操作" min-width="60" align="center">
             <template #default="scope">
               <el-button link type="primary" size="small" @click="openForm(scope.row);" :disabled="!scope.row.inboundNum0">领用</el-button>
               <el-button link type="primary" size="small" @click="openForm(scope.row);">领用</el-button>
             </template>
           </el-table-column>
         </el-table>
@@ -105,22 +78,9 @@
       </div>
     </el-tab-pane>
      <el-tab-pane label="成品出库" name="manual">
      <el-tab-pane label="材料出库" name="manual">
        <div class="search_form">
          <div>
<!--            <span class="search_title">供应商名称:</span>-->
<!--            <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="请输入" @change="handleQuery"-->
<!--              clearable prefix-icon="Search" />-->
            <span class="search_title ml10">入库日期:</span>
            <el-date-picker
              v-model="searchForm.timeStr"
              type="date"
              placeholder="请选择日期"
              value-format="YYYY-MM-DD"
              format="YYYY-MM-DD"
              clearable
              @change="handleQuery"
            />
            <span class="search_title ml10">产品大类:</span>
            <el-input
              v-model="searchForm.productCategory"
@@ -138,29 +98,17 @@
          <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
            :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
            :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
            <!-- <el-table-column align="center" type="selection" width="55" /> -->
            <el-table-column align="center" type="selection" width="55" />
            <el-table-column align="center" label="序号" type="index" width="60" />
            <el-table-column label="产品图片" width="100" align="center">
              <template #default="scope">
                <img class="barcode-img" :src="javaApiUrl+scope.row.url"></img>
              </template>
            </el-table-column>
            <el-table-column label="入库时间" prop="inboundDate" width="100" show-overflow-tooltip />
            <el-table-column label="产品名称" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="产品高度" prop="specificationModel" width="100" show-overflow-tooltip>
              <template #default="scope">
                <div>{{ scope.row.specificationModel }}{{ scope.row.unit }}</div>
              </template>
            </el-table-column>
                        <el-table-column label="入库数量/ä»¶" prop="inboundNum" width="100" show-overflow-tooltip />
                        <el-table-column label="每件数量/支" prop="boxNum" width="100" show-overflow-tooltip />
            <el-table-column label="剩余库存" prop="inboundNum0" width="90" show-overflow-tooltip />
                        <el-table-column label="单价(美元)/ä»¶" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
            <el-table-column label="纸箱规格" prop="cartonSpecifications" width="150"></el-table-column>
            <el-table-column label="入库人" prop="createBy" width="150" show-overflow-tooltip />
            <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
            <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
            <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
            <el-table-column label="物品类型" prop="itemType" show-overflow-tooltip />
                        <el-table-column label="剩余库存" prop="inboundNum0" width="90" show-overflow-tooltip />
            <el-table-column fixed="right" label="操作" width="100" align="center">
              <template #default="scope">
                <el-button link type="primary" size="small" @click="openForm(scope.row);" :disabled="!scope.row.inboundNum0">领用</el-button>
                <el-button link type="primary" size="small" @click="openForm(scope.row);">领用</el-button>
              </template>
            </el-table-column>
          </el-table>
@@ -169,17 +117,17 @@
        </div>
      </el-tab-pane>
    </el-tabs>
    <el-dialog v-model="dialogFormVisible" :title="'新增出库'" width="40%" @close="closeDia">
    <el-dialog v-model="dialogFormVisible" :title="getDialogTitle()" width="40%" @close="closeDia">
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
                <div>可出库数量:{{currentRowNum}}</div>
        <el-form-item label="出库数量:" prop="salesContractNo">
          <el-input-number :step="1" :min="0" :max="currentRowNum" style="width: 100%" v-model="form.inboundQuantity" placeholder="请输入" clearable />
            <div>{{getAvailableQuantityText()}}:{{currentRowNum}}</div>
        <el-form-item :label="getQuantityLabel()" prop="salesContractNo">
          <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.inboundQuantity" placeholder="请输入" clearable />
        </el-form-item>
        <el-form-item label="出库日期:" prop="projectName">
        <el-form-item :label="getDateLabel()" prop="projectName">
          <el-date-picker style="width: 100%" v-model="form.inboundTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
            type="date" placeholder="请选择" clearable />
        </el-form-item>
        <el-form-item label="出库人:" prop="entryPerson">
        <el-form-item :label="getPersonLabel()" prop="entryPerson">
          <el-select v-model="form.nickName"                 filterable
                     default-first-option
                     :reserve-keyword="false" placeholder="请选择" clearable>
@@ -213,7 +161,6 @@
    delStockManage,
    stockOut,
} from "@/api/inventoryManagement/stockManage.js";
const javaApiUrl = __BASE_API__;
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
@@ -230,12 +177,12 @@
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const dialogFormVisible = ref(false)
const activeTab = ref('purchase')
const activeTab = ref('production')
const data = reactive({
  searchForm: {
    supplierName: '',
    customerName: '',
    inboundQuantity:0,
    inboundQuantity:'',
    inboundTime:'',
    nickName: '',
    userId: '',
@@ -296,7 +243,7 @@
  page.current = 1
  searchForm.value.supplierName = ''
  searchForm.value.customerName = ''
  searchForm.value.timeStr = getCurrentDate() // é‡ç½®ä¸ºå½“前日期
  searchForm.value.timeStr = ''
  selectedRows.value = []
  searchForm.value.productCategory = ''
  getList()
@@ -343,7 +290,7 @@
  // åˆå§‹åŒ–表单数据
  form.value = {
    productrecordId: '',
    inboundQuantity: 0, // å‡ºåº“数量清空
    inboundQuantity: '', // å‡ºåº“数量清空
    inboundTime: getCurrentDate(), // é»˜è®¤å½“前日期
    nickName: '', // é»˜è®¤å½“前用户
  }
@@ -361,13 +308,7 @@
const submitForm = () => {
  let num = Number(form.value.inboundQuantity)
  if(num <= 0 || num > currentRowNum.value){
    return proxy.$modal.msgWarning("请填写出库数量")
  }
  if(!form.value.nickName){
    return proxy.$modal.msgWarning("请选择出库人")
  }
  if(!form.value.inboundTime){
    return proxy.$modal.msgWarning("请选择出库时间")
    return proxy.$modal.msgWarning("请填入有效数字")
  }
  proxy.$refs["formRef"].validate(valid => {
    if (valid && currentRowId.value) {
@@ -409,13 +350,13 @@
  }
  ).then(() => {
    // æ ¹æ®ä¸åŒçš„ tab ç±»åž‹è°ƒç”¨ä¸åŒçš„导出接口
    let exportUrl = "/stockmanagement/export"
    let exportUrl = "/stockin/export"
    if (activeTab.value === 'production') {
      exportUrl = "/stockmanagement/exportOne"
      exportUrl = "/stockin/exportOne"
    } else if (activeTab.value === 'manual') {
      exportUrl = "/stockmanagement/exportTwo"
      exportUrl = "/stockin/exportTwo"
    }
    proxy.download(exportUrl, {}, '出库台账.xlsx')
    proxy.download(exportUrl, {}, '入库台账.xlsx')
  }).catch(() => {
    proxy.$modal.msg("已取消")
  })
@@ -453,16 +394,62 @@
  const day = String(today.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}
// æ ¹æ®tab类型获取弹框标题
const getDialogTitle = () => {
  const titleMap = {
    production: '新增发货',
    purchase: '新增领用',
    manual: '新增领用'
  };
  return titleMap[activeTab.value] || '新增出库';
};
// æ ¹æ®tab类型获取可出库数量文本
const getAvailableQuantityText = () => {
  const textMap = {
    production: '可发货数量',
    purchase: '可领用数量',
    manual: '可领用数量'
  };
  return textMap[activeTab.value] || '可出库数量';
};
// æ ¹æ®tab类型获取数量字段标签
const getQuantityLabel = () => {
  const labelMap = {
    production: '发货数量:',
    purchase: '领用数量:',
    manual: '领用数量:'
  };
  return labelMap[activeTab.value] || '出库数量:';
};
// æ ¹æ®tab类型获取日期字段标签
const getDateLabel = () => {
  const labelMap = {
    production: '发货日期:',
    purchase: '领用日期:',
    manual: '领用日期:'
  };
  return labelMap[activeTab.value] || '出库日期:';
};
// æ ¹æ®tab类型获取人员字段标签
const getPersonLabel = () => {
  const labelMap = {
    production: '发货人:',
    purchase: '领用人:',
    manual: '领用人:'
  };
  return labelMap[activeTab.value] || '出库人:';
};
onMounted(() => {
  getList()
})
</script>
<style scoped lang="scss">
  .barcode-img{
    width: 80px;
    height: 80px;
    border-radius: 5px;
    background-color: #F5F5F5;
  }
</style>
<style scoped lang="scss"></style>
src/views/inventoryManagement/receiptManagement/components/formDia.vue
@@ -1,81 +1,81 @@
<template>
    <el-dialog v-model="dialogFormVisible" :title="getDialogTitle()" width="70%"
                         @close="closeDia">
        <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
            <el-form-item label="采购订单号" prop="purchaseContractNumber">
                <el-select
                    v-model="form.purchaseContractNumber"
                    placeholder="请选择采购订单号"
                    clearable
                    filterable
                    :loading="loadingPurchaseOptions"
                    @change="handlePurchaseChange"
                    :disabled="operationType === 'edit'"
                    style="width: 100%"
                >
                    <el-option
                        v-for="item in purchaseOptions"
                        :key="item.purchaseContractNumber"
                        :label="formatPurchaseOption(item)"
                        :value="item.purchaseContractNumber"
                    />
                </el-select>
            </el-form-item>
            <el-table
                :data="productList"
                border
                v-loading="loadingProducts"
                @selection-change="handleSelectionChange"
            >
                <el-table-column align="center" type="selection" width="55" />
                <el-table-column
                    align="center"
                    label="序号"
                    type="index"
                    width="60"
                />
                <el-table-column label="产品大类" prop="productCategory" />
                <el-table-column label="规格型号" prop="specificationModel" />
                <el-table-column label="单位" prop="unit" width="70" />
                <!-- <el-table-column label="供应商" prop="supplierName" width="100" /> -->
                <el-table-column label="采购数量" prop="quantity" width="100" />
                <el-table-column label="待入库数量" prop="quantity0" width="100" />
                <el-table-column label="本次入库数量" prop="quantityStock" width="150">
                    <template #default="scope">
                        <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.quantityStock" @change="() => calculateTotalPrice(scope.row)" />
                    </template>
                </el-table-column>
                <el-table-column label="税率(%)" prop="taxRate" width="120" />
                <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150">
  <el-dialog v-model="dialogFormVisible" :title="getDialogTitle()" width="70%"
    @close="closeDia">
    <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
      <el-form-item label="采购订单号" prop="purchaseContractNumber">
        <el-select
          v-model="form.purchaseContractNumber"
          placeholder="请选择采购订单号"
          clearable
          filterable
          :loading="loadingPurchaseOptions"
          @change="handlePurchaseChange"
          :disabled="operationType === 'edit'"
          style="width: 100%"
        >
          <el-option
            v-for="item in purchaseOptions"
            :key="item.purchaseContractNumber"
            :label="formatPurchaseOption(item)"
            :value="item.purchaseContractNumber"
          />
        </el-select>
      </el-form-item>
      <el-table
        :data="productList"
        border
        v-loading="loadingProducts"
        @selection-change="handleSelectionChange"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column
          align="center"
          label="序号"
          type="index"
          width="60"
        />
        <el-table-column label="产品大类" prop="productCategory" />
        <el-table-column label="规格型号" prop="specificationModel" />
        <el-table-column label="单位" prop="unit" width="70" />
        <!-- <el-table-column label="供应商" prop="supplierName" width="100" /> -->
        <el-table-column label="采购数量" prop="quantity" width="100" />
        <el-table-column label="待入库数量" prop="quantity0" width="100" />
        <el-table-column label="本次入库数量" prop="quantityStock" width="150">
          <template #default="scope">
            <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.quantityStock" @change="() => calculateTotalPrice(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column label="税率(%)" prop="taxRate" width="120" />
        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150">
                    <template #default="scope">
                        <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.taxInclusiveUnitPrice" @change="() => calculateTotalPrice(scope.row)" :disabled="operationType === 'edit'"/>
                    </template>
                </el-table-column>
                <el-table-column
                    label="总价(元)"
        <el-table-column
          label="总价(元)"
                    :formatter="formattedNumber"
                    prop="taxInclusiveTotalPrice"
                    width="150"
                >
                </el-table-column>
            </el-table>
        </el-form>
        <template #footer>
            <div class="dialog-footer">
                <el-button type="primary" @click="submitForm">确认</el-button>
                <el-button @click="closeDia">取消</el-button>
            </div>
        </template>
    </el-dialog>
          prop="taxInclusiveTotalPrice"
          width="150"
        >
        </el-table-column>
      </el-table>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="submitForm">确认</el-button>
        <el-button @click="closeDia">取消</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { ref, reactive, toRefs, getCurrentInstance } from 'vue'
import useUserStore from '@/store/modules/user'
import {
    updateStockIn,
    addSutockIn,
    selectProductRecordListByPuechaserId
  updateStockIn,
  addSutockIn,
  selectProductRecordListByPuechaserId
} from "@/api/inventoryManagement/stockIn.js";
import { purchaseListPage } from "@/api/procurementManagement/procurementLedger.js";
@@ -93,302 +93,303 @@
const loading = ref(false);
const data = reactive({
    form: {
        id: null,
        purchaseContractNumber: '', // é‡‡è´­è®¢å•号
        supplierId: null,       // ä¾›åº”商ID
        supplierName: '',       // ä¾›åº”商名称
        inboundTime: '',        // å…¥åº“æ—¶é—´
        inboundBatch: '',       // å…¥åº“批次
        recorderId: userStore.userId, // å½•入人ID
        recorderName: userStore.name, // å½•入人姓名
        entryDate: getCurrentDate(),  // å½•入日期
        remark: '',             // å¤‡æ³¨
    },
    rules: {
        purchaseContractNumber: [{ required: true, message: "请输入采购合同号", trigger: "blur" }],
        supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
        inboundTime: [{ required: true, message: "请选择入库时间", trigger: "change" }],
        inboundBatch: [{ required: true, message: "请输入入库批次", trigger: "blur" }]
    }
  form: {
    id: null,
    purchaseContractNumber: '', // é‡‡è´­è®¢å•号
    supplierId: null,       // ä¾›åº”商ID
    supplierName: '',       // ä¾›åº”商名称
    inboundTime: '',        // å…¥åº“æ—¶é—´
    inboundBatch: '',       // å…¥åº“批次
    recorderId: userStore.userId, // å½•入人ID
    recorderName: userStore.name, // å½•入人姓名
    entryDate: getCurrentDate(),  // å½•入日期
    remark: '',             // å¤‡æ³¨
  },
  rules: {
    purchaseContractNumber: [{ required: true, message: "请输入采购合同号", trigger: "blur" }],
    supplierId: [{ required: true, message: "请选择供应商", trigger: "change" }],
    inboundTime: [{ required: true, message: "请选择入库时间", trigger: "change" }],
    inboundBatch: [{ required: true, message: "请输入入库批次", trigger: "blur" }]
  }
})
const { form, rules } = toRefs(data)
// åŠ¨æ€è®¡ç®—å¯¹è¯æ¡†æ ‡é¢˜
const getDialogTitle = () => {
    return operationType.value === 'add' ? '新增入库' : '编辑入库'
  return operationType.value === 'add' ? '新增入库' : '编辑入库'
}
const formatPurchaseOption = (item = {}) => {
    const contract = item.purchaseContractNumber || '--';
    const supplier = item.supplierName ? ` Â· ${item.supplierName}` : '';
    return `${contract}${supplier}`;
  const contract = item.purchaseContractNumber || '--';
  const supplier = item.supplierName ? ` Â· ${item.supplierName}` : '';
  return `${contract}${supplier}`;
};
const loadPurchaseOptions = async (keyword = '') => {
    try {
        loadingPurchaseOptions.value = true;
        const res = await purchaseListPage({
            current: -1,
            size: -1,
            purchaseContractNumber: keyword,
        });
        const records = res.data?.records || [];
        purchaseOptions.value = records;
        if (
            form.value.purchaseContractNumber &&
            !purchaseOptions.value.find(
                (item) => item.purchaseContractNumber === form.value.purchaseContractNumber
            )
        ) {
            purchaseOptions.value.push({
                purchaseContractNumber: form.value.purchaseContractNumber,
                supplierName: form.value.supplierName,
                supplierId: form.value.supplierId,
            });
        }
    } finally {
        loadingPurchaseOptions.value = false;
    }
  try {
    loadingPurchaseOptions.value = true;
    const res = await purchaseListPage({
      current: -1,
      size: -1,
      purchaseContractNumber: keyword,
    });
    const records = res.data?.records || [];
    purchaseOptions.value = records;
    if (
      form.value.purchaseContractNumber &&
      !purchaseOptions.value.find(
        (item) => item.purchaseContractNumber === form.value.purchaseContractNumber
      )
    ) {
      purchaseOptions.value.push({
        purchaseContractNumber: form.value.purchaseContractNumber,
        supplierName: form.value.supplierName,
        supplierId: form.value.supplierId,
      });
    }
  } finally {
    loadingPurchaseOptions.value = false;
  }
};
const handlePurchaseChange = (value) => {
    form.value.purchaseContractNumber = value || '';
    const matched = purchaseOptions.value.find(
        (item) => item.purchaseContractNumber === value
    );
    if (matched) {
        form.value.supplierName = matched.supplierName || form.value.supplierName;
        form.value.supplierId = matched.supplierId || form.value.supplierId;
    }
    if (!value) {
        productList.value = [];
        return;
    }
    fetchProductsByContract();
  form.value.purchaseContractNumber = value || '';
  const matched = purchaseOptions.value.find(
    (item) => item.purchaseContractNumber === value
  );
  if (matched) {
    form.value.supplierName = matched.supplierName || form.value.supplierName;
    form.value.supplierId = matched.supplierId || form.value.supplierId;
  }
  if (!value) {
    productList.value = [];
    return;
  }
  fetchProductsByContract();
};
const exceedsAddLimit = (product) => {
    const stock = Number(product?.quantityStock ?? 0);
    const waiting = Number(product?.quantity0 ?? 0);
    if (!Number.isFinite(stock) || !Number.isFinite(waiting)) {
        return false;
    }
    return stock > waiting;
  const stock = Number(product?.quantityStock ?? 0);
  const waiting = Number(product?.quantity0 ?? 0);
  if (!Number.isFinite(stock) || !Number.isFinite(waiting)) {
    return false;
  }
  return stock > waiting;
};
const exceedsEditLimit = (product) => {
    const stock = Number(product?.quantityStock ?? 0);
    const waiting = Number(product?.quantity0 ?? 0);
    const original = Number(product?.originalQuantityStock ?? 0);
    if (!Number.isFinite(stock) || !Number.isFinite(waiting) || !Number.isFinite(original)) {
        return false;
    }
    return stock > waiting + original;
  const stock = Number(product?.quantityStock ?? 0);
  const waiting = Number(product?.quantity0 ?? 0);
  const original = Number(product?.originalQuantityStock ?? 0);
  if (!Number.isFinite(stock) || !Number.isFinite(waiting) || !Number.isFinite(original)) {
    return false;
  }
  return stock > waiting + original;
};
const formattedNumber = (row, column, cellValue) => {
    return parseFloat(cellValue).toFixed(2);
  return parseFloat(cellValue).toFixed(2);
};
// è®¡ç®—总价
const calculateTotalPrice = (row) => {
    const quantityStock = Number(row?.quantityStock ?? 0);
    const taxInclusiveUnitPrice = Number(row?.taxInclusiveUnitPrice ?? 0);
    if (Number.isFinite(quantityStock) && Number.isFinite(taxInclusiveUnitPrice)) {
        row.taxInclusiveTotalPrice = quantityStock * taxInclusiveUnitPrice;
    } else {
        row.taxInclusiveTotalPrice = 0;
    }
  const quantityStock = Number(row?.quantityStock ?? 0);
  const taxInclusiveUnitPrice = Number(row?.taxInclusiveUnitPrice ?? 0);
  if (Number.isFinite(quantityStock) && Number.isFinite(taxInclusiveUnitPrice)) {
    row.taxInclusiveTotalPrice = quantityStock * taxInclusiveUnitPrice;
  } else {
    row.taxInclusiveTotalPrice = 0;
  }
};
const fetchProductsByContract = async () => {
    if (!form.value.purchaseContractNumber) {
        proxy.$modal.msgWarning('请选择合同号')
        return
    }
    try {
        loadingProducts.value = true
        const productRes = await selectProductRecordListByPuechaserId({
            purchaseContractNumber: form.value.purchaseContractNumber
        });
        if (!productRes.data || productRes.data.length === 0) {
            proxy.$modal.msgWarning('该合同下没有产品记录')
            productList.value = [];
            return
        }
        productList.value = productRes.data.map(item => ({
            ...item,
            quantityStock: 0,
            taxInclusiveUnitPrice: Number(item?.taxInclusiveUnitPrice ?? 0),
            taxInclusiveTotalPrice: 0,
            originalQuantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? 0),
        }))
    } catch (error) {
        console.error('查询产品记录失败:', error)
        proxy.$modal.msgError('查询产品记录失败')
        productList.value = [];
    } finally {
        loadingProducts.value = false
    }
  if (!form.value.purchaseContractNumber) {
    proxy.$modal.msgWarning('请选择合同号')
    return
  }
  try {
    loadingProducts.value = true
    const productRes = await selectProductRecordListByPuechaserId({
      purchaseContractNumber: form.value.purchaseContractNumber
    });
    if (!productRes.data || productRes.data.length === 0) {
      proxy.$modal.msgWarning('该合同下没有产品记录')
      productList.value = [];
      return
    }
    productList.value = productRes.data.map(item => ({
      ...item,
      quantityStock: 0,
      taxInclusiveUnitPrice: Number(item?.taxInclusiveUnitPrice ?? 0),
      taxInclusiveTotalPrice: 0,
      originalQuantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? 0),
    }))
  } catch (error) {
    console.error('查询产品记录失败:', error)
    proxy.$modal.msgError('查询产品记录失败')
    productList.value = [];
  } finally {
    loadingProducts.value = false
  }
}
const updatePro = async () => {
    if (selectedRows.value.length === 0) {
        proxy.$modal.msgWarning('请先选择产品');
        return;
    }
    const target = selectedRows.value[0];
    const stock = Number(target?.quantityStock ?? 0);
    if (!Number.isFinite(stock) || stock <= 0) {
        proxy.$modal.msgWarning('请填写有效的入库数量');
        return;
    }
    if (exceedsEditLimit(target)) {
        proxy.$modal.msgError('本次入库数量不能超过原入库数量与待入库数量之和');
        return;
    }
    const stockInData = {
        id: selectedRows.value[0].recordId,
        quantityStock: Number(selectedRows.value[0].quantityStock),
    };
    await updateStockIn(stockInData)
    proxy.$modal.msgSuccess('修改入库成功')
    closeDia()
    emit('success')
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning('请先选择产品');
    return;
  }
  const target = selectedRows.value[0];
  const stock = Number(target?.quantityStock ?? 0);
  if (!Number.isFinite(stock) || stock <= 0) {
    proxy.$modal.msgWarning('请填写有效的入库数量');
    return;
  }
  if (exceedsEditLimit(target)) {
    proxy.$modal.msgError('本次入库数量不能超过原入库数量与待入库数量之和');
    return;
  }
  const stockInData = {
    id: selectedRows.value[0].recordId,
    quantityStock: Number(selectedRows.value[0].quantityStock),
  };
  await updateStockIn(stockInData)
  proxy.$modal.msgSuccess('修改入库成功')
  closeDia()
  emit('success')
}
const submitForm = async () => {
    if (selectedRows.value.length === 0) {
        proxy.$modal.msgWarning('请先选择采购合同并选择产品')
        return
    }
    if(operationType.value !== 'add'){
        await updatePro()
        return
    }
    try {
        await proxy.$refs.formRef.validate()
        const invalidProducts = selectedRows.value.filter((product) => {
            const stock = Number(product?.quantityStock ?? 0);
            if (!Number.isFinite(stock) || stock <= 0) {
                return true;
            }
            return exceedsAddLimit(product);
        })
        if (invalidProducts.length > 0) {
            proxy.$modal.msgWarning('本次入库数量不能大于待入库数量,且需大于0');
            return;
        }
        const stockInData = {
            ...form.value,
            inboundTime: formatDateTime(),
            nickName: userStore.nickName,
            details: selectedRows.value.map(product => ({
                id: product.id,
                inboundQuantity: Number(product.quantityStock),
                unitPrice: Number(product.taxInclusiveUnitPrice),
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning('请先选择采购合同并选择产品')
    return
  }
  if(operationType.value !== 'add'){
    await updatePro()
    return
  }
  try {
    await proxy.$refs.formRef.validate()
    const invalidProducts = selectedRows.value.filter((product) => {
        const stock = Number(product?.quantityStock ?? 0);
        if (!Number.isFinite(stock) || stock <= 0) {
          return true;
        }
        return exceedsAddLimit(product);
    })
    if (invalidProducts.length > 0) {
      proxy.$modal.msgError('本次入库数量需大于0,且不能超过待入库数量')
      return
    }
    const stockInData = {
      ...form.value,
      inboundTime: formatDateTime(form.value.inboundTime),
      nickName: userStore.nickName,
      details: selectedRows.value.map(product => ({
        id: product.id,
        inboundQuantity: Number(product.quantityStock),
                taxInclusiveUnitPrice: Number(product.taxInclusiveUnitPrice),
                taxInclusiveTotalPrice: Number(product.taxInclusiveTotalPrice)
            })),
        };
        loading.value = true
        await addSutockIn(stockInData)
        proxy.$modal.msgSuccess('新增入库成功')
        closeDia()
        emit('success')
    } catch (error) {
        console.error('提交失败:', error)
        if (!error.errors) {
            proxy.$modal.msgError('操作失败,请重试')
        }
    } finally {
        loading.value = false
    }
      })),
    };
    loading.value = true
    await addSutockIn(stockInData)
    proxy.$modal.msgSuccess('新增入库成功')
    closeDia()
    emit('success')
  } catch (error) {
    console.error('提交失败:', error)
    if (!error.errors) {
      proxy.$modal.msgError('操作失败,请重试')
    }
  } finally {
    loading.value = false
  }
}
const closeDia = () => {
    proxy.$refs.formRef.resetFields()
    dialogFormVisible.value = false
    emit('close')
  proxy.$refs.formRef.resetFields()
  dialogFormVisible.value = false
  emit('close')
}
const handleSelectionChange = (selection) => {
    selectedRows.value = selection.filter(item => item.id);
  selectedRows.value = selection.filter(item => item.id);
}
function formatDateTime(date = new Date(), includeTime = true) {
    const d = new Date(date);
    const year = d.getFullYear();
    const month = String(d.getMonth() + 1).padStart(2, '0');
    const day = String(d.getDate()).padStart(2, '0');
    if (!includeTime) {
        return `${year}-${month}-${day}`;
    }
    const hours = String(d.getHours()).padStart(2, '0');
    const minutes = String(d.getMinutes()).padStart(2, '0');
    const seconds = String(d.getSeconds()).padStart(2, '0');
    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
  const d = new Date(date);
  const year = d.getFullYear();
  const month = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  if (!includeTime) {
    return `${year}-${month}-${day}`;
  }
  const hours = String(d.getHours()).padStart(2, '0');
  const minutes = String(d.getMinutes()).padStart(2, '0');
  const seconds = String(d.getSeconds()).padStart(2, '0');
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
function getCurrentDate() {
    return formatDateTime(new Date(), false);
  return formatDateTime(new Date(), false);
}
const openDialog = async (type, row) => {
    operationType.value = type
    dialogFormVisible.value = true
    selectedRows.value = []
    await loadPurchaseOptions();
    if (type === 'add') {
        form.value = {
            id: null,
            purchaseContractNumber: '',
            supplierId: null,
            supplierName: '',
            inboundTime: '',
            inboundBatch: '',
            recorderId: userStore.userId,
            recorderName: userStore.name,
            entryDate: getCurrentDate(),
            remark: ''
        }
        productList.value = []
    } else {
        form.value = JSON.parse(JSON.stringify(row))
        try {
            loadingProducts.value = true
            const res = await selectProductRecordListByPuechaserId({
                purchaseContractNumber: form.value.purchaseContractNumber,
                id: row.id
            });
            productList.value = res.data.map(item => ({
                ...item,
                quantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0),
                taxInclusiveUnitPrice: Number(item?.taxInclusiveUnitPrice ?? 0),
                taxInclusiveTotalPrice: Number(item?.quantityStock ?? 0) * Number(item?.taxInclusiveUnitPrice ?? 0),
                originalQuantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0),
            }))
            selectedRows.value = productList.value
        } catch (error) {
            console.error('加载产品失败:', error)
            proxy.$modal.msgError('加载产品失败')
            productList.value = []
        } finally {
            loadingProducts.value = false
        }
    }
  operationType.value = type
  dialogFormVisible.value = true
  selectedRows.value = []
  await loadPurchaseOptions();
  if (type === 'add') {
    form.value = {
      id: null,
      purchaseContractNumber: '',
      supplierId: null,
      supplierName: '',
      inboundTime: '',
      inboundBatch: '',
      recorderId: userStore.userId,
      recorderName: userStore.name,
      entryDate: getCurrentDate(),
      remark: ''
    }
    productList.value = []
  } else {
    form.value = JSON.parse(JSON.stringify(row))
    try {
      loadingProducts.value = true
      const res = await selectProductRecordListByPuechaserId({
        purchaseContractNumber: form.value.purchaseContractNumber,
        id: row.id
      });
      productList.value = res.data.map(item => ({
        ...item,
        quantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0),
        taxInclusiveUnitPrice: Number(item?.taxInclusiveUnitPrice ?? 0),
        taxInclusiveTotalPrice: Number(item?.quantityStock ?? 0) * Number(item?.taxInclusiveUnitPrice ?? 0),
        originalQuantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? row.inboundNum ?? 0),
      }))
      selectedRows.value = productList.value
    } catch (error) {
      console.error('加载产品失败:', error)
      proxy.$modal.msgError('加载产品失败')
      productList.value = []
    } finally {
      loadingProducts.value = false
    }
  }
}
defineExpose({
    openDialog,
  openDialog,
})
</script>
src/views/inventoryManagement/receiptManagement/components/formDiaManual.vue
@@ -16,54 +16,43 @@
          type="index"
          width="60"
        />
        <el-table-column label="产品图片" align="center" prop="productCategory" width="100">
        <el-table-column label="产品大类" prop="productCategory" width="200">
          <template #default="scope">
            <el-upload
              :action="uploadUrl"
              :before-upload="handleBeforeUpload"
              :on-success="(res,file)=>{handleUploadSuccess(res,file,scope.row)}"
              :on-error="handleUploadError"
              name="file"
              :show-file-list="false"
              :headers="headers"
              accept="image/*"
              :data="{ type: 9 }"
            >
              <img class="upload-img" v-if="scope.row.url" :src="javaApiUrl+scope.row.url"></img>
              <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
            </el-upload>
            <el-input v-model="scope.row.productCategory" placeholder="请输入产品大类" />
          </template>
        </el-table-column>
        <el-table-column label="产品名称" prop="productCategory" width="200">
        <el-table-column label="规格型号" prop="specificationModel" width="200">
          <template #default="scope">
            <el-input v-model="scope.row.productCategory" placeholder="请输入产品名称" />
            <el-input v-model="scope.row.specificationModel" placeholder="请输入规格型号" />
          </template>
        </el-table-column>
        <el-table-column label="产品高度" prop="specificationModel" width="200">
        <el-table-column label="单位" prop="unit" width="100">
          <template #default="scope">
            <el-input v-model="scope.row.specificationModel" placeholder="请输入产品高度" />
            <el-input v-model="scope.row.unit" placeholder="请输入单位" />
          </template>
        </el-table-column>
        <el-table-column label="纸箱规格" prop="cartonSpecifications" width="200">
        <el-table-column label="供应商" prop="supplierName" width="200">
          <template #default="scope">
            <el-input v-model="scope.row.cartonSpecifications" placeholder="请输入纸箱规格" />
            <el-input v-model="scope.row.supplierName" placeholder="请输入供应商" />
          </template>
        </el-table-column>
        <el-table-column label="入库数量-ä»¶" prop="inboundNum" width="150">
        <el-table-column label="物品类型" prop="itemType" width="150">
          <template #default="scope">
            <el-input-number :step="1" :min="0" style="width: 100%" v-model="scope.row.inboundNum" @change="() => calculateTotalPrice(scope.row)" />
            <el-select v-model="scope.row.itemType" filterable allow-create placeholder="请选择物品类型" style="width: 100%">
              <el-option
                v-for="item in itemTypeOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </template>
        </el-table-column>
        <el-table-column label="每件数量/支" prop="boxNum" width="150">
        <el-table-column label="入库数量" prop="inboundNum" width="150">
          <template #default="scope">
            <el-input-number :step="1" :min="0" style="width: 100%" v-model="scope.row.boxNum" @change="() => calculateTotalPrice(scope.row)" />
            <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.inboundNum" @change="() => calculateTotalPrice(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column label="单价(美元)/ä»¶" prop="taxInclusiveUnitPrice" width="150">
         <template #default="scope">
           <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.taxInclusiveUnitPrice" @change="() => calculateTotalPrice(scope.row)" />
         </template>
       </el-table-column>
        <el-table-column label="入库日期" prop="inboundDate" width="180">
          <template #default="scope">
            <el-date-picker
@@ -76,18 +65,22 @@
            />
          </template>
        </el-table-column>
<!--        <el-table-column label="数量" prop="quantityStock" width="150">-->
<!--          <template #default="scope">-->
<!--            <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.quantityStock" @change="() => calculateTotalPrice(scope.row)" />-->
<!--          </template>-->
<!--        </el-table-column>-->
<!--        <el-table-column -->
<!--           label="总价(元)" -->
<!--           prop="taxInclusiveTotalPrice" -->
<!--           width="150" -->
<!--         >-->
<!--        </el-table-column>-->
        <el-table-column label="数量" prop="quantityStock" width="150">
          <template #default="scope">
            <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.quantityStock" @change="() => calculateTotalPrice(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150">
          <template #default="scope">
            <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.taxInclusiveUnitPrice" @change="() => calculateTotalPrice(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column
           label="总价(元)"
           prop="taxInclusiveTotalPrice"
           width="150"
         >
        </el-table-column>
        <el-table-column label="操作" width="80" v-if="operationType === 'add'">
          <template #default="scope">
            <el-button type="danger" size="small" @click="removeProductRow(scope.$index)">删除</el-button>
@@ -111,18 +104,10 @@
  addStockInCustom,
  updateStockInCustom,
} from "@/api/inventoryManagement/stockIn.js";
import { getToken } from "@/utils/auth";
const headers = ref({
  Authorization: "Bearer " + getToken(),
});
const javaApiUrl = __BASE_API__;
const userStore = useUserStore()
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close', 'success'])
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload"); // ä¸Šä¼ çš„图片服务器地址
const operationType = ref('')// æ“ä½œç±»åž‹: 'add' æˆ– 'edit'
const dialogFormVisible = ref(false)// å¼¹æ¡†æ˜¾ç¤ºçŠ¶æ€
@@ -186,7 +171,7 @@
    id: null,
    productCategory: '',
    specificationModel: '',
    cartonSpecifications:'',
    unit: '',
    supplierName: form.value.supplierName || '',
    itemType: '',
    inboundNum: 0,
@@ -196,7 +181,6 @@
    taxInclusiveTotalPrice: 0,
    taxRate: null,
    taxExclusiveTotalPrice: 0,
    boxNum: 0,
  });
};
@@ -233,42 +217,23 @@
    // éªŒè¯è‡ªå®šä¹‰æ·»åŠ çš„æ•°æ®å¿…å¡«å­—æ®µ
    for (let i = 0; i < productList.value.length; i++) {
      const product = productList.value[i];
      if (!product.productCategory || !product.specificationModel) {
        proxy.$modal.msgError(`第${i + 1}行产品数据未填写完整(产品、产品高度、高度单位为必填)`)
      if (!product.productCategory || !product.specificationModel || !product.unit) {
        proxy.$modal.msgError(`第${i + 1}行产品数据未填写完整(产品大类、规格型号、单位为必填)`)
        return
      }
      if (!product.url) {
        proxy.$modal.msgError(`第${i + 1}行产品未上传产品图片`)
        return
      }
      if (!product.cartonSpecifications) {
        proxy.$modal.msgError(`第${i + 1}行产品未填写纸箱规格`)
        return
      }
      // if (!product.itemType) {
      //   proxy.$modal.msgError(`第${i + 1}行请选择物品类型`)
      //   return
      // }
      const stock = Number(product?.inboundNum ?? 0);
      if (!Number.isFinite(stock) || stock <= 0) {
        proxy.$modal.msgError(`第${i + 1}行本次入库数量需大于0`)
        return
      }
      const boxNum = Number(product?.boxNum ?? 0);
      if (!Number.isFinite(boxNum) || boxNum <= 0) {
        proxy.$modal.msgError(`第${i + 1}行每件数量/支需大于0`)
        return
      }
      const taxInclusiveUnitPrice = Number(product?.taxInclusiveUnitPrice ?? 0);
      if (!Number.isFinite(taxInclusiveUnitPrice) || taxInclusiveUnitPrice <= 0) {
        proxy.$modal.msgError(`第${i + 1}行单价(元)需大于0`)
      if (!product.itemType) {
        proxy.$modal.msgError(`第${i + 1}行请选择物品类型`)
        return
      }
      if (!product.inboundDate) {
        proxy.$modal.msgError(`第${i + 1}行请选择入库日期`)
        return
      }
      const stock = Number(product?.inboundNum ?? 0);
      if (!Number.isFinite(stock) || stock <= 0) {
        proxy.$modal.msgError(`第${i + 1}行本次入库数量需大于0`)
        return
      }
    }
    const payloadList = productList.value.map(product => ({
@@ -276,6 +241,7 @@
            inboundNum: Number(product.inboundNum),
      productCategory: product.productCategory,
      specificationModel: product.specificationModel,
      unit: product.unit,
      supplierName: product.supplierName || form.value.supplierName,
      itemType: product.itemType,
      inboundDate: formatDateTime(product.inboundDate, false),
@@ -283,9 +249,6 @@
      taxExclusiveTotalPrice: Number(product.taxExclusiveTotalPrice || 0),
            taxInclusiveUnitPrice: Number(product.taxInclusiveUnitPrice || 0),
            taxInclusiveTotalPrice: Number(product.taxInclusiveTotalPrice || 0),
      boxNum:Number(product.boxNum),
      cartonSpecifications: product.cartonSpecifications,
      url: product.url||'',
    }));
    loading.value = true
    if (operationType.value === 'edit') {
@@ -314,30 +277,6 @@
  dialogFormVisible.value = false
  productList.value = []
  emit('close')
}
// ä¸Šä¼ å‰æ ¡æ£€æ ¼å¼å’Œå¤§å°
function handleBeforeUpload(file) {
  const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
  const isJPG = type.includes(file.type);
  //检验文件格式
  if (!isJPG) {
    proxy.$modal.msgError(`图片格式错误!`);
    return false;
  }
  return true;
}
const handleUploadSuccess = (res, file,item) => {
  // å¦‚果上传成功
  if (res.code == 200) {
    item.url = res.data?.tempPath||''
  } else {
    proxy.$modal.msgError("图片插入失败");
  }
}
// ä¸Šä¼ å¤±è´¥å¤„理
function handleUploadError() {
  proxy.$modal.msgError("图片插入失败");
}
const openDialog = async (type, row) => {
@@ -370,6 +309,7 @@
      id: row?.id ?? null,
      productCategory: row?.productCategory ?? '',
      specificationModel: row?.specificationModel ?? '',
      unit: row?.unit ?? '',
      supplierName: row?.supplierName ?? '',
      itemType: row?.itemType ?? '',
      inboundNum: Number(row?.inboundNum ?? row?.inboundQuantity ?? 0),
@@ -379,9 +319,6 @@
      taxInclusiveUnitPrice: Number(row?.taxInclusiveUnitPrice ?? 0),
      taxInclusiveTotalPrice: Number(row?.taxInclusiveTotalPrice ?? 0),
      taxExclusiveTotalPrice: Number(row?.taxExclusiveTotalPrice ?? 0),
      boxNum: Number(row?.boxNum ?? 0),
      cartonSpecifications: row?.cartonSpecifications ?? '',
      url: row?.url ?? '',
    }]
  }
}
@@ -391,11 +328,5 @@
})
</script>
<style scoped lang="scss">
  .upload-img{
    width: 80px;
    height: 80px;
    object-fit: contain;
  }
</style>
<style scoped lang="scss"></style>
src/views/inventoryManagement/receiptManagement/components/formDiaProduct.vue
@@ -289,7 +289,6 @@
      taxRate: Number(row?.taxRate ?? 0),
      unitPrice: Number(row?.unitPrice ?? 0),
      taxExclusiveTotalPrice: Number(row?.taxExclusiveTotalPrice ?? 0),
      dollarPrice: Number(row?.dollarPrice ?? 0),
    }]
  }
}
src/views/inventoryManagement/receiptManagement/index.vue
@@ -1,7 +1,59 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
     <el-tab-pane label="采购入库" name="purchase">
      <el-tab-pane label="成品入库" name="production">
        <div class="search_form">
          <div>
            <span class="search_title ml10">入库日期:</span>
            <el-date-picker
              v-model="searchForm.timeStr"
              type="date"
              placeholder="请选择日期"
              value-format="YYYY-MM-DD"
              format="YYYY-MM-DD"
              clearable
              @change="handleQuery"
            />
            <span class="search_title ml10">产品大类:</span>
            <el-input
              v-model="searchForm.productCategory"
              style="width: 240px"
              placeholder="请输入"
              clearable
            />
            <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
          </div>
          <div>
            <el-button @click="handleOut">导出</el-button>
            <el-button type="danger" plain @click="handleDelete">删除</el-button>
          </div>
        </div>
        <div class="table_list">
          <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
            :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
            :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
            <el-table-column align="center" type="selection" width="55" />
            <el-table-column align="center" label="序号" type="index" width="60" />
            <el-table-column label="入库时间" prop="createTime" show-overflow-tooltip />
                        <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
            <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
            <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
                        <el-table-column label="入库数量" prop="inboundNum" width="100" show-overflow-tooltip />
                        <el-table-column label="单价(元)" prop="unitPrice" width="150"></el-table-column>
                        <el-table-column label="总价(元)" prop="totalPrice" width="150"></el-table-column>
            <el-table-column fixed="right" label="操作" min-width="60" align="center">
              <template #default="scope">
                <el-button link type="primary" size="small" @click="openForm('edit', scope.row, 'production');">编辑</el-button>
              </template>
            </el-table-column>
          </el-table>
          <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
            :page="page.current" :limit="page.size" @pagination="paginationChange" />
        </div>
      </el-tab-pane>
     <el-tab-pane label="原料入库" name="purchase">
       <div class="search_form">
         <div>
           <span class="search_title ml10">入库日期:</span>
@@ -36,20 +88,18 @@
           <el-table-column align="center" type="selection" width="55" />
           <el-table-column align="center" label="序号" type="index" width="60" />
           <el-table-column label="入库时间" prop="createTime" width="100" show-overflow-tooltip />
          <el-table-column label="产品名称" prop="productCategory" show-overflow-tooltip />
          <el-table-column label="产品高度" prop="specificationModel" width="100" show-overflow-tooltip>
            <template #default="scope">
              <div>{{ scope.row.specificationModel }}{{ scope.row.unit }}</div>
            </template>
          </el-table-column>
          <el-table-column label="入库数量" prop="inboundNum" width="100" show-overflow-tooltip />
          <el-table-column label="单价(元)/ä»¶" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
          <el-table-column label="入库人" prop="createBy" width="120" show-overflow-tooltip />
                     <el-table-column label="采购合同号" prop="purchaseContractNumber" width="180" show-overflow-tooltip />
<el-table-column label="供应商名称" prop="supplierName" width="240" show-overflow-tooltip />
           <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
           <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
           <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
                     <el-table-column label="入库数量" prop="inboundNum" width="100" show-overflow-tooltip />
                     <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                     <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" width="150"></el-table-column>
           <el-table-column label="入库人" prop="createBy" width="80" show-overflow-tooltip />
           <el-table-column fixed="right" label="操作" min-width="60" align="center">
             <template #default="scope">
               <el-button link type="primary" size="small" @click="openForm('edit', scope.row, 'purchase');">编辑</el-button>
                <!-- <el-button link type="success" size="small" @click="showQRCode(scope.row,1)">生成条形码</el-button>
                <el-button link type="success" size="small" @click="showERCode(scope.row,1)">生成二维码</el-button> -->
             </template>
           </el-table-column>
         </el-table>
@@ -58,7 +108,7 @@
       </div>
     </el-tab-pane>
      
      <el-tab-pane label="成品入库" name="manual">
      <el-tab-pane label="材料入库" name="manual">
        <div class="search_form">
          <div>
            <span class="search_title ml10">入库日期:</span>
@@ -92,28 +142,20 @@
            :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
            <el-table-column align="center" type="selection" width="55" />
            <el-table-column align="center" label="序号" type="index" width="60" />
            <el-table-column label="产品图片" width="100" align="center">
              <template #default="scope">
                <img class="barcode-img" :src="javaApiUrl+scope.row.url"></img>
              </template>
            </el-table-column>
            <el-table-column label="入库时间" prop="inboundDate" width="100" show-overflow-tooltip />
            <el-table-column label="产品名称" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="产品高度" prop="specificationModel" width="100" show-overflow-tooltip>
              <template #default="scope">
                <div>{{ scope.row.specificationModel }}{{ scope.row.unit }}</div>
              </template>
            </el-table-column>
                        <el-table-column label="入库数量/ä»¶" prop="inboundNum" width="100" show-overflow-tooltip />
                        <el-table-column label="每件数量/支" prop="boxNum" width="100" show-overflow-tooltip />
                        <el-table-column label="单价(元)/ä»¶" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                        <el-table-column label="纸箱规格" prop="cartonSpecifications" width="150"></el-table-column>
            <el-table-column label="入库人" prop="createBy" width="150" show-overflow-tooltip />
                        <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
            <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
            <el-table-column label="单位" prop="unit" width="70" show-overflow-tooltip />
            <el-table-column label="物品类型" prop="itemType" show-overflow-tooltip />
                        <el-table-column label="入库数量" prop="inboundNum" width="100" show-overflow-tooltip />
                        <el-table-column label="剩余库存" prop="inboundNum0" show-overflow-tooltip />
                        <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                        <el-table-column label="总价(元)" prop="taxInclusiveTotalPrice" width="150"></el-table-column>
            <el-table-column label="入库人" prop="createBy" width="80" show-overflow-tooltip />
            <el-table-column fixed="right" label="操作" width="100" align="center">
              <template #default="scope">
                <el-button link type="primary" size="small" @click="openForm('edit', scope.row, 'manual');">编辑</el-button>
                <el-button link type="success" size="small" @click="showQRCode(scope.row,2)">生成条形码</el-button>
                <el-button link type="success" size="small" @click="showERCode(scope.row,2)">生成二维码</el-button>
              </template>
            </el-table-column>
          </el-table>
@@ -126,102 +168,6 @@
    <form-dia ref="formDia" @close="handleQuery" @success="handleQuery"></form-dia>
    <form-dia-manual ref="formDiaManual" @close="handleQuery" @success="handleQuery"></form-dia-manual>
    <form-dia-product ref="formDiaProduct" @close="handleQuery" @success="handleQuery"></form-dia-product>
    <el-dialog
      v-model="qrCodeDialogVisible"
      title="商品条形码"
      width="400px"
      center
    >
      <div style="text-align: center;">
        <img id="barcode" style="width:200px;height: 50px;"/>
        <!-- <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" /> -->
        <div style="margin: 20px;">
          <el-button type="primary" @click="downloadQRCode">下载条形码</el-button>
        </div>
      </div>
    </el-dialog>
    <!-- äºŒç»´ç æ˜¾ç¤ºå¯¹è¯æ¡† -->
    <el-dialog
      v-model="erCodeDialogVisible"
      title="商品二维码"
      width="400px"
      center
    >
      <div style="text-align: center;">
        <img :src="erCodeUrl" alt="二维码" style="width:200px;height:200px;" />
        <div style="margin: 20px;">
          <el-button type="primary" @click="downloadERCode">下载二维码图片</el-button>
        </div>
      </div>
    </el-dialog>
    <el-dialog v-model="barcodeDia" title="产品信息" width="40%" @close="closeBarcodeDia">
      <div>
        <el-row v-if="barcodeDetail.url" :gutter="30">
          <el-col :span="12">
            <div class="barcode-item">
              <div class="barcode-label">图片</div>
              <img class="barcode-img" :src="javaApiUrl+barcodeDetail.url"></img>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <div class="barcode-item">
              <div class="barcode-label">名称</div>
              <div class="barcode-value">{{barcodeDetail.productCategory}}</div>
            </div>
          </el-col>
          <el-col :span="12">
            <div class="barcode-item">
              <div class="barcode-label">产品高度</div>
              <div class="barcode-value">{{barcodeDetail.specificationModel}}{{barcodeDetail.unit}}</div>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <div class="barcode-item">
              <div class="barcode-label">单价</div>
              <div class="barcode-value">{{barcodeDetail.taxInclusiveUnitPrice}}美元/ä»¶</div>
            </div>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <div class="barcode-item">
              <div class="barcode-label">数量/ä»¶</div>
              <div class="barcode-value">{{barcodeDetail.inboundNum}}</div>
            </div>
          </el-col>
          <el-col :span="12">
            <div class="barcode-item">
              <div class="barcode-label">每件数量/支</div>
              <div class="barcode-value">{{barcodeDetail.boxNum}}</div>
            </div>
          </el-col>
        </el-row>
         <el-row :gutter="30">
          <el-col :span="12">
            <div class="barcode-item">
              <div class="barcode-label">纸箱规格</div>
              <div class="barcode-value">{{barcodeDetail.cartonSpecifications}}</div>
            </div>
          </el-col>
          <el-col :span="12">
            <div class="barcode-item">
              <div class="barcode-label">入库人</div>
              <div class="barcode-value">{{barcodeDetail.createBy}}</div>
            </div>
          </el-col>
        </el-row>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeBarcodeDia">关闭</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
@@ -231,19 +177,15 @@
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import dayjs from 'dayjs'
const javaApiUrl = __BASE_API__;
import {
    getStockInPage,
    getStockInPageByProduction,
    delStockIn,
    delStockInCustom, getInPageByCustom,
  stockinDetail,detailManagementByCustom
} from "@/api/inventoryManagement/stockIn.js";
import FormDia from './components/formDia.vue'
import FormDiaManual from './components/formDiaManual.vue'
import FormDiaProduct from './components/formDiaProduct.vue'
import QRCode from "qrcode";
import JsBarcode from "jsbarcode";
// èŽ·å–å½“å‰æ—¥æœŸ
function getCurrentDate() {
@@ -258,7 +200,7 @@
const formDia = ref()
const formDiaManual = ref()
const formDiaProduct = ref()
const activeTab = ref('purchase') // å½“前激活的 tab
const activeTab = ref('production') // å½“前激活的 tab
const page = reactive({
  current: 1,
@@ -342,14 +284,13 @@
  // åˆ‡æ¢ tab æ—¶æ¸…空搜索条件
  searchForm.value.supplierName = ''
  searchForm.value.customerName = ''
  searchForm.value.timeStr = getCurrentDate() // é‡ç½®ä¸ºå½“前日期
  searchForm.value.timeStr = ''
  searchForm.value.productCategory = ''
  getList()
}
// æ‰“开弹框
const openForm = async (type, row, tabType) => {
  if(barcodeDia.value)return
  const currentTab = tabType || activeTab.value
  await nextTick(() => {
    if (currentTab === 'manual') {
@@ -421,7 +362,7 @@
    } else {
      // ææ–™å…¥åº“
      deleteApi = delStockInCustom
      deleteParams = [...ids]
      deleteParams = { ids }
    }
    
    deleteApi(deleteParams).then(() => {
@@ -435,173 +376,12 @@
  })
}
// äºŒç»´ç ç›¸å…³å˜é‡
const qrCodeDialogVisible = ref(false);
const qrCodeUrl = ref("");
const showQRCode = async (row,type) => {
  if(barcodeDia.value)return
  try {
    // æž„建二维码内容,只包含采购合同号(纯文本)
    let qrContent = row.id || '';
    // æ£€æŸ¥å†…容是否为空
    if (!qrContent) {
      proxy.$modal.msgWarning("该行商品id,无法生成条形码");
      return;
    }
    qrContent+=`,${type}`
    qrCodeDialogVisible.value = true;
    await nextTick();
    JsBarcode("#barcode", qrContent+'', {
      width:10,
      height:100,
      displayValue: false
    });
  } catch (error) {
    console.error('生成条形码失败:', error);
    proxy.$modal.msgError("生成条形码失败:" + error.message);
  }
};
const erCodeDialogVisible = ref(false);
const erCodeUrl = ref("");
const showERCode = async (row,type) => {
  if(barcodeDia.value)return
  let qrContent = row.id || '';
    // æ£€æŸ¥å†…容是否为空
  if (!qrContent) {
    proxy.$modal.msgWarning("该行商品id,无法生成二维码");
    return;
  }
  qrContent+=`,${type}`
  try {
    erCodeUrl.value = await QRCode.toDataURL(qrContent+'', {
      width: 200,
      margin: 2,
      color: {
        dark: '#000000',
        light: '#FFFFFF'
      }
    });
    erCodeDialogVisible.value = true;
  } catch (error) {
    console.error('生成二维码失败:', error);
    proxy.$modal.msgError("生成二维码失败:" + error.message);
  }
};
const downloadQRCode = () => {
  const imgSrc = document.getElementById('barcode').src
  const a = document.createElement('a');
  if(!imgSrc){
    proxy.$modal.msgWarning('暂无条形码')
    return
  }
  a.href = imgSrc;
  a.download = `商品条形码_${new Date().getTime()}.png`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  proxy.$modal.msgSuccess("下载成功");
};
// ä¸‹è½½äºŒç»´ç 
const downloadERCode = () => {
  if (!erCodeUrl.value) {
    proxy.$modal.msgWarning("二维码未生成");
    return;
  }
  const a = document.createElement('a');
  a.href = erCodeUrl.value;
  a.download = `商品二维码_${new Date().getTime()}.png`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  proxy.$modal.msgSuccess("下载成功");
};
//扫码相关参数
const barcodeDia = ref(false);
const scanBarcodeInput = ref('');
const barcodeDetail = ref({})
// æ‰«ç å‡½æ•°
function scanBarcode (e){
  if(!e||!e.target||!e.target.tagName){
    return;
  }
  if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
    return
  }
  if (e.key === 'Enter') {
    let _value = scanBarcodeInput.value
    getDetail(_value)
    scanBarcodeInput.value = ""
  } else {
    scanBarcodeInput.value += e.key
  }
}
const getDetail = (barcode)=>{
  if(barcode.indexOf(",")==-1){
    proxy.$modal.msgWarning("请扫描正确的条形码")
    return
  }
  let barcodeList = barcode.split(",")
  let barcodeId = barcodeList[0]
  let type = barcodeList[1]
  let detailApi = null
  if(type==1){
    detailApi = stockinDetail
  }else if(type==2){
    detailApi = detailManagementByCustom
  }
  if(!detailApi){
    proxy.$modal.msgWarning("请扫描正确的条形码")
    return
  }
  detailApi({id:barcodeId}).then((resp) => {
    if(!resp.data){
      proxy.$modal.msgError("商品不存在")
      return
    }
    if(resp.code!=200){
      proxy.$modal.msgError(res.msg)
      return
    }
    barcodeDetail.value = resp.data
    barcodeDia.value = true
  }).catch(() => {
    proxy.$modal.msgError("查看详情失败")
  })
}
const closeBarcodeDia = () => {
  barcodeDia.value = false
}
onMounted(() => {
  // æ·»åŠ æ‰«ç æžªç›‘å¬äº‹ä»¶
  document.removeEventListener('keypress',scanBarcode)
  document.addEventListener('keypress', scanBarcode)
  getList()
})
</script>
<style scoped lang="scss">
.barcode-item{
  display: flex;
  justify-content: space-between;
  padding: 5px 0;
}
.barcode-img{
  width: 80px;
  height: 80px;
  border-radius: 5px;
  background-color: #F5F5F5;
}
</style>
<style scoped lang="scss"></style>
src/views/inventoryManagement/stockManagement/components/FormDiaManual.vue
@@ -4,28 +4,27 @@
    <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="产品名称:" prop="productCategory">
          <el-form-item label="产品大类:" prop="productCategory">
            <el-input disabled v-model="form.productCategory" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="产品高度:" prop="specificationModel">
          <el-form-item label="规格型号:" prop="specificationModel">
            <el-input disabled v-model="form.specificationModel" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="高度单位:" prop="unit">
          <el-form-item label="单位:" prop="unit">
            <el-input disabled v-model="form.unit" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <!-- <el-col :span="12">
        <el-col :span="12">
          <el-form-item label="物品类型:" prop="itemType">
            <el-input disabled v-model="form.itemType" placeholder="请输入" clearable />
          </el-form-item>
        </el-col> -->
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
@@ -35,36 +34,35 @@
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="入库数量/件:" prop="inboundNum">
            <el-input-number :step="1" v-model="form.inboundNum" :min="form.totalInboundNum" style="width: 100%"
                                                             placeholder="请输入" clearable/>
          <el-form-item label="库存数量:" prop="inboundNum">
            <el-input v-model="form.inboundNum" placeholder="请输入" clearable @input="calculateTotalPrice" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="已出库数量/件:" prop="totalInboundNum">
          <el-form-item label="已出库数量:" prop="totalInboundNum">
            <el-input disabled v-model="form.totalInboundNum" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="待出库数量/件:" prop="inboundNum0">
          <el-form-item label="待出库数量:" prop="inboundNum0">
            <el-input disabled v-model="form.inboundNum0" placeholder="请输入" clearable />
          </el-form-item>
        </el-col>
      </el-row>
<!--      <el-row :gutter="30">-->
<!--        <el-col :span="12">-->
<!--          <el-form-item label="单价(元):" prop="taxInclusiveUnitPrice">-->
<!--            <el-input v-model="form.taxInclusiveUnitPrice" placeholder="请输入" clearable @input="calculateTotalPrice" />-->
<!--          </el-form-item>-->
<!--        </el-col>-->
<!--        <el-col :span="12">-->
<!--          <el-form-item label="总价(元):" prop="taxInclusiveTotalPrice">-->
<!--            <el-input disabled v-model="form.taxInclusiveTotalPrice" placeholder="自动计算" clearable />-->
<!--          </el-form-item>-->
<!--        </el-col>-->
<!--      </el-row>-->
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="单价(元):" prop="taxInclusiveUnitPrice">
            <el-input v-model="form.taxInclusiveUnitPrice" placeholder="请输入" clearable @input="calculateTotalPrice" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="总价(元):" prop="taxInclusiveTotalPrice">
            <el-input disabled v-model="form.taxInclusiveTotalPrice" placeholder="自动计算" clearable />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
src/views/inventoryManagement/stockManagement/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="app-container">
    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
      <!-- <el-tab-pane label="采购库存" name="production">
      <el-tab-pane label="成品库存" name="production">
        <div class="search_form">
          <div>
            <span class="search_title ml10">入库日期:</span>
@@ -14,7 +14,7 @@
              clearable
              @change="handleQuery"
            />
                            <span class="search_title ml10">产品名称:</span>
                            <span class="search_title ml10">产品大类:</span>
                            <el-input
                                v-model="searchForm.productCategory"
                                style="width: 240px"
@@ -25,6 +25,7 @@
          </div>
          <div>
            <el-button @click="handleOut">导出</el-button>
<!--            <el-button type="danger" plain @click="handleDelete">删除</el-button>-->
          </div>
        </div>
        <div class="table_list">
@@ -35,10 +36,10 @@
            <el-table-column align="center" type="selection" width="55" />
            <el-table-column align="center" label="序号" type="index" width="60" />
            <el-table-column label="入库日期" prop="createTime" width="100" show-overflow-tooltip />
            <el-table-column label="产品名称" prop="productCategory" show-overflow-tooltip />
                        <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
            <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
            <el-table-column label="单位" prop="unit" width="80" show-overflow-tooltip />
            <el-table-column label="入库数量" prop="inboundNum" width="100" show-overflow-tooltip />
            <el-table-column label="已出库数量" prop="totalInboundNum" width="100" show-overflow-tooltip />
            <el-table-column label="剩余库存" prop="inboundNum0" width="100" show-overflow-tooltip />
                            <el-table-column label="单价(元)" prop="unitPrice" width="150"></el-table-column>
@@ -47,155 +48,145 @@
              <template #default="scope">
                <el-button link type="primary" size="small" @click="openForm('edit', scope.row);">编辑</el-button>
              </template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper" :page="page.current"
  :limit="page.size" @pagination="paginationChange" />
</div>
</el-tab-pane> -->
      <el-tab-pane label="采购库存" name="purchase">
        <div class="search_form">
          <div>
            <span class="search_title ml10">产品名称:</span>
            <el-input v-model="searchForm.productCategory" style="width: 240px" placeholder="请输入" clearable />
            <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
          </div>
          <div>
            <el-button @click="handleOut">导出</el-button>
          </div>
        </div>
        <div class="table_list">
          <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
            :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
            :row-class-name="tableRowClassName" :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
            <el-table-column align="center" type="selection" width="55" />
            <el-table-column align="center" label="序号" type="index" width="60" />
            <el-table-column label="产品名称" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="产品高度" prop="specificationModel" width="100" show-overflow-tooltip>
              <template #default="scope">
                <div>{{ scope.row.specificationModel }}{{ scope.row.unit }}</div>
              </template>
            </el-table-column>
            <el-table-column label="每件数量/支" prop="boxNum" width="100" show-overflow-tooltip />
            <el-table-column label="入库数量" prop="inboundNum" width="100" show-overflow-tooltip />
            <el-table-column label="单价(元)/ä»¶" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
            <el-table-column label="已出库数量" prop="totalInboundNum" width="100" show-overflow-tooltip />
            <el-table-column label="剩余库存" prop="inboundNum0" width="100" show-overflow-tooltip />
            <el-table-column label="纸箱规格" prop="cartonSpecifications" width="150"></el-table-column>
          </el-table>
          <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
            :page="page.current" :limit="page.size" @pagination="paginationChange" />
        </div>
      </el-tab-pane>
      <el-tab-pane label="成品库存" name="manual">
     <el-tab-pane label="原料库存" name="purchase">
       <div class="search_form">
         <div>
           <span class="search_title ml10">入库日期:</span>
           <el-date-picker
             v-model="searchForm.timeStr"
             type="date"
             placeholder="请选择日期"
             value-format="YYYY-MM-DD"
             format="YYYY-MM-DD"
             clearable
             @change="handleQuery"
           />
                            <span class="search_title ml10">产品大类:</span>
                            <el-input
                                v-model="searchForm.productCategory"
                                style="width: 240px"
                                placeholder="请输入"
                                clearable
                            />
           <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
         </div>
         <div>
           <el-button @click="handleOut">导出</el-button>
<!--           <el-button type="danger" plain @click="handleDelete">删除</el-button>-->
         </div>
       </div>
       <div class="table_list">
         <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
           :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
           :row-class-name="tableRowClassName"
           :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
           <el-table-column align="center" type="selection" width="55" />
           <el-table-column align="center" label="序号" type="index" width="60" />
           <el-table-column label="入库日期" prop="createTime" width="100" show-overflow-tooltip />
                     <el-table-column label="采购合同号" prop="purchaseContractNumber" width="180" show-overflow-tooltip />
           <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
           <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
           <el-table-column label="单位" prop="unit" width="80" show-overflow-tooltip />
                            <el-table-column label="已出库数量" prop="totalInboundNum" show-overflow-tooltip />
                            <el-table-column label="剩余库存" prop="inboundNum0" show-overflow-tooltip />
                         <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                         <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" width="150"></el-table-column>
         </el-table>
         <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
           :page="page.current" :limit="page.size" @pagination="paginationChange" />
       </div>
     </el-tab-pane>
      <el-tab-pane label="材料库存" name="manual">
        <div class="search_form">
          <div>
            <!--            <span class="search_title">供应商名称:</span>-->
            <!--            <el-input v-model="searchForm.supplierName" style="width: 240px" placeholder="请输入" @change="handleQuery"-->
            <!--              clearable prefix-icon="Search" />-->
            <span class="search_title ml10">产品名称:</span>
            <el-input v-model="searchForm.productCategory" style="width: 240px" placeholder="请输入" clearable />
            <span class="search_title ml10">入库日期:</span>
            <el-date-picker
              v-model="searchForm.timeStr"
              type="date"
              placeholder="请选择日期"
              value-format="YYYY-MM-DD"
              format="YYYY-MM-DD"
              clearable
              @change="handleQuery"
            />
                            <span class="search_title ml10">产品大类:</span>
                            <el-input
                                v-model="searchForm.productCategory"
                                style="width: 240px"
                                placeholder="请输入"
                                clearable
                            />
            <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
          </div>
          <div>
            <el-button @click="handleOut">导出</el-button>
            <!--            <el-button type="danger" plain @click="handleDelete">删除</el-button>-->
<!--            <el-button type="danger" plain @click="handleDelete">删除</el-button>-->
          </div>
        </div>
        <div class="table_list">
          <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
            :expand-row-keys="expandedRowKeys" :row-key="row => row.id" show-summary style="width: 100%"
            :row-class-name="tableRowClassName" :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
            :row-class-name="tableRowClassName"
            :summary-method="summarizeMainTable" height="calc(100vh - 18.5em)">
            <el-table-column align="center" type="selection" width="55" />
            <el-table-column align="center" label="序号" type="index" width="60" />
            <el-table-column label="产品图片" width="100" align="center">
              <template #default="scope">
                <img class="barcode-img" :src="javaApiUrl + scope.row.url"></img>
              </template>
            </el-table-column>
            <el-table-column label="产品名称" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="产品高度" prop="specificationModel" width="100" show-overflow-tooltip>
              <template #default="scope">
                <div>{{ scope.row.specificationModel }}{{ scope.row.unit }}</div>
              </template>
            </el-table-column>
            <el-table-column label="单价(美元)/ä»¶" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
            <el-table-column label="每件数量/支" prop="boxNum" width="100" show-overflow-tooltip />
            <el-table-column label="入库数量" prop="inboundNum" width="100" show-overflow-tooltip />
            <el-table-column label="已出库数量" prop="totalInboundNum" width="100" show-overflow-tooltip />
            <el-table-column label="剩余库存" prop="inboundNum0" width="100" show-overflow-tooltip />
            <el-table-column label="纸箱规格" prop="cartonSpecifications" width="150"></el-table-column>
            <!-- <el-table-column fixed="right" label="操作" width="100" align="center">
            <el-table-column label="入库日期" prop="inboundDate" width="100" show-overflow-tooltip />
                        <el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
            <el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
            <el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
            <el-table-column label="单位" prop="unit" width="80" show-overflow-tooltip />
            <el-table-column label="物品类型" prop="itemType" width="120" show-overflow-tooltip />
                            <el-table-column label="已出库数量" prop="totalInboundNum" width="100" show-overflow-tooltip />
                            <el-table-column label="剩余库存" prop="inboundNum0" width="100" show-overflow-tooltip />
                            <el-table-column label="单价(元)" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
                            <el-table-column label="总价(元)" prop="taxInclusiveTotalPrice" width="150"></el-table-column>
            <el-table-column fixed="right" label="操作" width="100" align="center">
              <template #default="scope">
                <el-button link type="primary" size="small" @click="openForm('edit', scope.row);">编辑</el-button>
              </template>
            </el-table-column> -->
            </el-table-column>
          </el-table>
          <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
            :page="page.current" :limit="page.size" @pagination="paginationChange" />
        </div>
      </el-tab-pane>
    </el-tabs>
    <!-- æˆå“åº“存弹框 -->
    <FormDiaProduction v-model:dialogFormVisible="productionDialogVisible" :operationType="operationType"
      :formData="form" @submit="submitForm" @close="closeDia" />
    <FormDiaProduction
      v-model:dialogFormVisible="productionDialogVisible"
      :operationType="operationType"
      :formData="form"
      @submit="submitForm"
      @close="closeDia"
    />
    <!-- åŽŸæ–™åº“å­˜å¼¹æ¡† -->
    <FormDiaPurchase v-model:dialogFormVisible="purchaseDialogVisible" :operationType="operationType" :formData="form"
      @submit="submitForm" @close="closeDia" />
    <FormDiaPurchase
      v-model:dialogFormVisible="purchaseDialogVisible"
      :operationType="operationType"
      :formData="form"
      @submit="submitForm"
      @close="closeDia"
    />
    <!-- ææ–™åº“存弹框 -->
    <FormDiaManual v-model:dialogFormVisible="manualDialogVisible" :operationType="operationType" :formData="form"
      @submit="submitForm" @close="closeDia" />
    <FormDiaManual
      v-model:dialogFormVisible="manualDialogVisible"
      :operationType="operationType"
      :formData="form"
      @submit="submitForm"
      @close="closeDia"
    />
  </div>
  <el-dialog v-model="barcodeDia" title="产品信息" width="70%" @close="closeBarcodeDia">
    <div>
      <el-row :gutter="30">
        <el-col :span="12">
          <div class="barcode-item">
            <div class="barcode-label">名称</div>
            <div class="barcode-value">{{ barcodeDetail.barcode }}</div>
          </div>
        </el-col>
        <el-col :span="12">
          <div class="barcode-item">
            <div class="barcode-label">单价</div>
            <div class="barcode-value">xxx元</div>
          </div>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <div class="barcode-item">
            <div class="barcode-label">产品高度</div>
            <div class="barcode-value">222cm</div>
          </div>
        </el-col>
        <el-col :span="12">
          <div class="barcode-item">
            <div class="barcode-label">数量/ä»¶</div>
            <div class="barcode-value">120</div>
          </div>
        </el-col>
      </el-row>
      <el-row :gutter="30">
        <el-col :span="12">
          <div class="barcode-item">
            <div class="barcode-label">规格</div>
            <div class="barcode-value">12*20*30</div>
          </div>
        </el-col>
      </el-row>
    </div>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="closeBarcodeDia">关闭</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
@@ -204,7 +195,7 @@
import { ElMessageBox } from "element-plus";
import useUserStore from '@/store/modules/user'
import { userListNoPageByTenantId } from "@/api/system/user.js";
import { productTreeList, modelList } from "@/api/basicData/product.js"
import { productTreeList,modelList } from "@/api/basicData/product.js"
import {
  getStockManagePage,
  getStockManagePageByProduction,
@@ -212,9 +203,8 @@
  delStockManage,
} from "@/api/inventoryManagement/stockManage.js";
import {
  updateManagement, updateManagementByCustom, updateStockIn
    updateManagement, updateManagementByCustom, updateStockIn
} from "@/api/inventoryManagement/stockIn.js";
const javaApiUrl = __BASE_API__;
// å¯¼å…¥ä¸‰ä¸ªç‹¬ç«‹çš„弹框组件
import FormDiaProduction from './components/FormDiaProduction.vue'
@@ -240,7 +230,7 @@
const loading = ref(false);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref('')
const activeTab = ref('purchase')
const activeTab = ref('production')
// ä¸‰ä¸ªç‹¬ç«‹çš„弹框显示状态
const productionDialogVisible = ref(false)
@@ -250,8 +240,9 @@
const data = reactive({
  searchForm: {
    // supplierName: '',
    productCategory: '',
    productCategory:'',
    customerName: '',
    timeStr: getCurrentDate(),
  },
  form: {
    supplierId: null,
@@ -273,7 +264,7 @@
    inboundBatch: '',
    stockQuantity: '',
    boundTime: '',
    warnNum: '', // æ–°å¢žæœ€ä½Žåº“存字段
        warnNum: '', // æ–°å¢žæœ€ä½Žåº“存字段
    salesLedgerProductId: null,
  },
  rules: {
@@ -290,7 +281,7 @@
    boundTime: [{ required: true, message: '请选择库存时间', trigger: 'change' }],
    inboundTime: [{ required: true, message: '请选择入库时间', trigger: 'change' }],
    inboundPerson: [{ required: true, message: '请选择出库人', trigger: 'change' }],
    warnNum: [{ required: true, message: '请输入最低库存', trigger: 'blur' }],
        warnNum: [{ required: true, message: '请输入最低库存', trigger: 'blur' }],
  }
})
const { searchForm, form, rules } = toRefs(data)
@@ -309,6 +300,7 @@
const buildQueryParams = () => {
  const params = {
    ...page,
    timeStr: searchForm.value.timeStr,
  }
  params.productCategory = searchForm.value.productCategory
  if (activeTab.value === 'production') {
@@ -333,14 +325,14 @@
  apiCall.then(res => {
    tableLoading.value = false
    tableData.value = res.data.records
    // ä¸ºè¡¨æ ¼æ•°æ®è‡ªåŠ¨è®¡ç®—æ€»ä»·
    tableData.value = tableData.value.map(item => {
      // è®¡ç®—剩余库存
      const stockQuantity = parseFloat(item.inboundNum) || 0
      const outboundQuantity = parseFloat(item.totalInboundNum) || 0
      const remainingStock = Math.max(stockQuantity - outboundQuantity, 0)
      // æ ¹æ®æ ‡ç­¾é¡µç±»åž‹è®¡ç®—总价
      if (activeTab.value === 'production') {
        // æˆå“åº“存:总价 = å•ä»· Ã— å‰©ä½™åº“å­˜
@@ -355,10 +347,10 @@
        const taxInclusiveUnitPrice = parseFloat(item.taxInclusiveUnitPrice) || 0
        item.taxInclusiveTotalPrice = (taxInclusiveUnitPrice * remainingStock).toFixed(2)
      }
      return item
    })
    total.value = res.data.total
    // æ•°æ®åŠ è½½å®ŒæˆåŽæ£€æŸ¥åº“å­˜
    // checkStockAndCreatePurchase();
@@ -372,6 +364,7 @@
  page.current = 1
  // searchForm.value.supplierName = ''
  searchForm.value.customerName = ''
  searchForm.value.timeStr = ''
  selectedRows.value = []
  searchForm.value.productCategory = ''
  getList()
@@ -410,9 +403,9 @@
  userList.value = userLists.data
  if (type === 'edit') {
    form.value = { ...row }
    productTreeList().then(res => {
    productTreeList().then(res =>{
      productList.value = res
      productList.value.forEach(i => {
      productList.value.forEach(i =>{
        if (i.label === row.productCategory) {
          modelList({ id: i.id }).then((res) => {
            productModelList.value = res;
@@ -422,7 +415,7 @@
    })
  }
  form.value.entryDate = getCurrentDate() // è®¾ç½®é»˜è®¤å½•入日期为当前日期
  // æ ¹æ®å½“前标签页显示对应的弹框
  if (activeTab.value === 'production') {
    productionDialogVisible.value = true
@@ -436,10 +429,10 @@
// æäº¤è¡¨å•
const submitForm = (submittedData) => {
  console.log('子组件提交的数据:', submittedData)
  // ä½¿ç”¨å­ç»„件提交的数据,而不是父组件的form对象
  const submitData = { ...submittedData }
  // æ ¹æ®å½“前标签页移除对应的总价字段
  if (activeTab.value === 'production') {
    // æˆå“åº“存:移除总价字段
@@ -451,12 +444,12 @@
    // ææ–™åº“存:移除含税总价字段
    delete submitData.taxInclusiveTotalPrice
  }
  // ç§»é™¤å…¶ä»–可能的总价字段
  delete submitData.taxExclusiveTotalPrice
  console.log('提交给后端的数据(已移除总价字段):', submitData)
  submitData.inboundNum = parseFloat(submitData.inboundNum) || 0
  // æ ¹æ®å½“前标签页调用不同的提交接口
  let apiCall
  if (activeTab.value === 'production') {
@@ -469,7 +462,7 @@
    // åŽŸæ–™åº“å­˜ä½¿ç”¨ updateManagementByCustom æŽ¥å£
    apiCall = updateManagementByCustom(submitData)
  }
  apiCall.then(res => {
    proxy.$modal.msgSuccess("提交成功")
    closeDia()
@@ -551,7 +544,7 @@
    type: 'warning',
  }
  ).then(() => {
    delStockManage({ ids: ids }).then(res => {
    delStockManage({ids:ids}).then(res => {
      proxy.$modal.msgSuccess("删除成功")
      getList()
    })
@@ -567,54 +560,16 @@
  const day = String(today.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}
//扫码相关参数
const barcodeDia = ref(false);
const scanBarcodeInput = ref('');
const barcodeDetail = ref({})
// æ‰«ç å‡½æ•°
const scanBarcode = (e) => {
  if (!e || !e.target || !e.target.tagName) {
    return;
  }
  if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
    return
  }
  if (e.key === 'Enter') {
    let _value = scanBarcodeInput.value
    getDetail(_value)
    scanBarcodeInput.value = ""
  } else {
    scanBarcodeInput.value += e.key
  }
}
const getDetail = (barcode) => {
  barcodeDetail.value = {
    barcode: barcode
  }
  barcodeDia.value = true
}
const closeBarcodeDia = () => {
  barcodeDia.value = false
}
onMounted(() => {
  // æ·»åŠ æ‰«ç æžªç›‘å¬äº‹ä»¶
  // document.addEventListener('keypress', scanBarcode)
  getList()
  // checkStockAndCreatePurchase();
  // æ¯å°æ—¶æ£€æŸ¥ä¸€æ¬¡åº“å­˜
  // const intervalId = setInterval(checkStockAndCreatePurchase, 60 * 60 * 1000);
    // æ¯å°æ—¶æ£€æŸ¥ä¸€æ¬¡åº“å­˜
    // const intervalId = setInterval(checkStockAndCreatePurchase, 60 * 60 * 1000);
  // onUnmounted(() => {
  //   // ç»„件卸载时清除定时器
  //   clearInterval(intervalId);
  // });
})
onUnmounted(() => {
  // ç§»é™¤é”®ç›˜ç›‘听事件
  document.removeEventListener('keypress', scanBarcode)
  console.log('组件卸载')
// onUnmounted(() => {
//   // ç»„件卸载时清除定时器
//   clearInterval(intervalId);
// });
})
</script>
@@ -626,18 +581,5 @@
:deep(.row-low-stock:hover > td) {
  background-color: #fcd4d4;
}
.barcode-item {
  display: flex;
  justify-content: space-between;
  padding: 5px 0;
}
.barcode-img {
  width: 80px;
  height: 80px;
  border-radius: 5px;
  background-color: #F5F5F5;
}
</style>
src/views/personnelManagement/contractManagement/filesDia.vue
@@ -28,19 +28,12 @@
          :tableData="tableData"
          :tableLoading="tableLoading"
          :isSelection="true"
                    :isShowPage="false"
          @selection-change="handleSelectionChange"
          height="500"
                    :page="page"
                    @pagination="pagination"
      >
      </PIMTable>
            <pagination
                style="margin: 10px 0"
                v-show="total > 0"
                @pagination="paginationSearch"
                :total="total"
                :page="page.current"
                :limit="page.size"
            />
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
@@ -99,6 +92,7 @@
const page = reactive({
    current: 1,
    size: 100,
    total: 0
});
const total = ref(0);
const tableData = ref([]);
@@ -117,7 +111,7 @@
  currentId.value = row.id;
  getList()
}
const paginationSearch = (obj) => {
const pagination = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
@@ -125,7 +119,7 @@
const getList = () => {
  fileListPage({accountId: currentId.value,accountType:accountType.value, ...page}).then(res => {
    tableData.value = res.data.records;
        total.value = res.data.total;
        page.total = res.data.total;
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
src/views/personnelManagement/contractManagement/index.vue
@@ -70,11 +70,11 @@
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { onMounted, ref } from "vue";
import { Search, UploadFilled } from "@element-plus/icons-vue";
import { onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
import FormDia from "@/views/personnelManagement/contractManagement/components/formDia.vue";
import { ElMessageBox } from "element-plus";
import { staffOnJobListPage } from "@/api/personnelManagement/employeeRecord.js";
import { staffOnJobListPage, staffOnJobExportCopy } from "@/api/personnelManagement/employeeRecord.js";
import dayjs from "dayjs";
import { getToken } from "@/utils/auth.js";
import FilesDia from "./filesDia.vue";
@@ -183,7 +183,7 @@
    label: "操作",
    align: "center",
    fixed: 'right',
    width: 120,
    width: 180,
    operation: [
      {
        name: "详情",
@@ -197,6 +197,13 @@
        type: "text",
        clickFun: (row) => {
          openFilesFormDia(row);
        },
      },
      {
        name: "下载合同",
        type: "text",
        clickFun: (row) => {
          handleDownloadContract(row);
        },
      },
    ],
@@ -278,6 +285,33 @@
      proxy.$modal.msg("已取消");
    });
};
// ä¸‹è½½åˆåŒ
const handleDownloadContract = (row) => {
  const fileName = `${row.staffName || "合同"}劳动合同.docx`;
  proxy.$modal?.loading?.("正在生成合同,请稍候...");
  staffOnJobExportCopy({ ...row })
    .then((res) => {
      proxy.$modal?.closeLoading?.();
      if (res?.code === 200 && res?.msg) {
        const javaApi = proxy.javaApi || import.meta.env.VITE_JAVA_API || "";
        const downloadPath = res.msg.startsWith("/") ? res.msg : `/${res.msg}`;
        const downloadUrl = `${javaApi}${downloadPath}`;
        const link = document.createElement("a");
        link.href = downloadUrl;
        link.download = fileName;
        link.target = "_blank";
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
      } else {
        proxy.$modal.msgError(res?.msg || "合同生成失败");
      }
    })
    .catch(() => {
      proxy.$modal?.closeLoading?.();
      proxy.$modal.msgError("合同生成失败,请稍后重试");
    });
};
const upload = reactive({
  // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(合同导入)
  open: false,
src/views/personnelManagement/dimission/components/formDia.vue
@@ -13,7 +13,9 @@
          <el-col :span="12">
            <div class="info-item">
              <span class="info-label">姓名:</span>
              <el-select v-model="form.staffName" placeholder="请选择人员" style="width: 100%" @change="handleSelect">
              <el-select v-model="form.staffName"                 filterable
                         default-first-option
                         :reserve-keyword="false" placeholder="请选择人员" style="width: 100%" @change="handleSelect">
                <el-option
                  v-for="item in personList"
                  :key="item.id"
src/views/personnelManagement/employeeRecord/components/formDia.vue
@@ -6,16 +6,56 @@
        width="70%"
        @close="closeDia"
    >
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :tableLoading="tableLoading"
          height="600"
      ></PIMTable>
      <el-descriptions class="detail-descriptions" :column="2" border size="small">
        <el-descriptions-item label="员工编号">{{ formData.staffNo || '-' }}</el-descriptions-item>
        <el-descriptions-item label="姓名">{{ formData.staffName || '-' }}</el-descriptions-item>
        <el-descriptions-item label="性别">{{ formData.sex || '-' }}</el-descriptions-item>
        <el-descriptions-item label="年龄">{{ formData.age || '-' }}</el-descriptions-item>
        <el-descriptions-item label="身份证号">{{ formData.identityCard || '-' }}</el-descriptions-item>
        <el-descriptions-item label="户籍住址" :span="2">{{ formData.nativePlace || '-' }}</el-descriptions-item>
        <el-descriptions-item label="现住址" :span="2">{{ formData.adress || '-' }}</el-descriptions-item>
        <el-descriptions-item label="岗位">{{ formData.postJob || '-' }}</el-descriptions-item>
        <el-descriptions-item label="第一学历">{{ formData.firstStudy || '-' }}</el-descriptions-item>
        <el-descriptions-item label="专业">{{ formData.profession || '-' }}</el-descriptions-item>
        <el-descriptions-item label="联系电话">{{ formData.phone || '-' }}</el-descriptions-item>
        <el-descriptions-item label="紧急联系人">{{ formData.emergencyContact || '-' }}</el-descriptions-item>
        <el-descriptions-item label="紧急联系人电话">{{ formData.emergencyContactPhone || '-' }}</el-descriptions-item>
        <el-descriptions-item label="合同签订日期">{{ formData.signDate || '-' }}</el-descriptions-item>
        <el-descriptions-item label="劳动合同期限选择">
          <span v-if="formData.dateSelect === 'A'">A、有固定期限</span>
          <span v-else-if="formData.dateSelect === 'B'">B、无固定期限</span>
          <span v-else-if="formData.dateSelect === 'C'">C、以完成一定工作任务为期限</span>
          <span v-else>-</span>
        </el-descriptions-item>
        <el-descriptions-item label="合同年限">{{ formattedContractTerm }}</el-descriptions-item>
        <el-descriptions-item label="试用期开始日期" v-if="formData.dateSelect === 'A' || formData.dateSelect === 'B'">
          {{ formData.trialStartDate || '-' }}
        </el-descriptions-item>
        <el-descriptions-item label="试用期结束日期" v-if="formData.dateSelect === 'A' || formData.dateSelect === 'B'">
          {{ formData.trialEndDate || '-' }}
        </el-descriptions-item>
        <el-descriptions-item label="试用期工资" v-if="formData.dateSelect === 'A' || formData.dateSelect === 'B'">
          {{ formData.proSalary ? formData.proSalary.toFixed(2) : '-' }}
        </el-descriptions-item>
        <el-descriptions-item label="合同开始日期">{{ calculatedContractStart }}</el-descriptions-item>
        <el-descriptions-item label="合同结束日期">{{ formData.contractEndTime || '-' }}</el-descriptions-item>
        <el-descriptions-item label="工资报酬" :span="2">
          <span v-if="formData.salarySelect === 'A'">
            A、乙方的工资报酬按照甲方依法制定的规章制度中的内部工资分配办法确定,根据乙方的工作岗位确定其每月工资。
          </span>
          <span v-else-if="formData.salarySelect === 'B'">
            B、甲方对乙方实行基本工资和绩效工资相结合的内部工资分配办法,乙方的收入包括基本工资、误餐、交通、生活住宿等各项补助,如有变动根据内部工资分配办法调整其工资;绩效工资根据乙方的工作业绩、劳动成果和实际贡献按照内部分配办法考核确定。
          </span>
          <span v-else-if="formData.salarySelect === 'C'">
            C、甲方实行计件工资制,以甲方接到订单及公司生产计划,按照定额和计件单价,根据乙方完成的业绩,按时足额支付乙方的工资报酬。
          </span>
          <span v-else>-</span>
        </el-descriptions-item>
        <el-descriptions-item label="福利待遇" :span="2">{{ formData.remark || '-' }}</el-descriptions-item>
      </el-descriptions>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
          <el-button @click="closeDia">关闭</el-button>
        </div>
      </template>
    </el-dialog>
@@ -23,38 +63,82 @@
</template>
<script setup>
import {ref} from "vue";
import {staffOnJobInfo} from "@/api/personnelManagement/employeeRecord.js";
const { proxy } = getCurrentInstance()
import {ref, reactive, computed} from "vue";
import dayjs from "dayjs";
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const operationType = ref('')
const tableColumn = ref([
  // {
  //   label: "合同年限",
  //   prop: "contractTerm",
  // },
  {
    label: "合同开始日期",
    prop: "contractStartTime",
  },
  {
    label: "合同结束日期",
    prop: "contractEndTime",
  },
]);
const tableData = ref([]);
const tableLoading = ref(false);
const formData = reactive({
  staffNo: "",
  staffName: "",
  sex: "",
  identityCard: "",
  nativePlace: "",
  postJob: "",
  adress: "",
  firstStudy: "",
  profession: "",
  age: 0,
  phone: "",
  emergencyContact: "",
  emergencyContactPhone: "",
  dateSelect: "",
  trialStartDate: "",
  trialEndDate: "",
  proSalary: null,
  signDate: "",
  salarySelect: "",
  contractStartTime: "",
  contractEndTime: "",
  contractTerm: null,
  remark: "",
});
const formattedContractTerm = computed(() => {
  const value = formData.contractTerm;
  if (value === null || value === undefined || value === "") {
    return "-";
  }
  const numberValue = Number(value);
  if (!isNaN(numberValue)) {
    return `${numberValue}å¹´`;
  }
  return value;
});
const calculatedContractStart = computed(() => {
  const endDate = formData.contractEndTime;
  const termValue = formData.contractTerm;
  const numberValue = Number(termValue);
  if (!endDate || isNaN(numberValue)) {
    return formData.contractStartTime || "-";
  }
  const start = dayjs(endDate).subtract(numberValue, "year");
  if (!start.isValid()) {
    return formData.contractStartTime || "-";
  }
  return start.format("YYYY-MM-DD");
});
// æ‰“开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  if (operationType.value === 'edit') {
    staffOnJobInfo({staffNo: row.staffNo}).then(res => {
      tableData.value = res.data
    })
  // é‡ç½®è¡¨å•数据
  Object.keys(formData).forEach(key => {
    if (key === 'age') {
      formData[key] = 0;
    } else if (["proSalary", "contractTerm"].includes(key)) {
      formData[key] = null;
    } else {
      formData[key] = "";
    }
  });
  if (operationType.value === 'edit' && row) {
    // ç›´æŽ¥ä½¿ç”¨ row æ•°æ®èµ‹å€¼
    Object.assign(formData, row);
  }
}
@@ -69,5 +153,25 @@
</script>
<style scoped>
.detail-descriptions {
  margin-bottom: 16px;
  border-radius: 6px;
  overflow: hidden;
}
.detail-descriptions :deep(.el-descriptions__cell) {
  padding: 12px 16px !important;
}
.detail-descriptions :deep(.el-descriptions__label) {
  width: 140px;
  color: #606266;
  background-color: #f7f9fc;
  font-weight: 500;
}
.detail-descriptions :deep(.el-descriptions__content) {
  color: #303133;
  line-height: 20px;
}
</style>
src/views/personnelManagement/onboarding/components/formDia.vue
@@ -83,11 +83,11 @@
              <el-input v-model="form.emergencyContactPhone" placeholder="请输入" clearable/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="合同年限:" prop="contractTerm">
              <el-input-number v-model="form.contractTerm" :precision="0" :step="1" style="width: 100%" :disabled="true"/>
            </el-form-item>
          </el-col>
<!--          <el-col :span="12">-->
<!--            <el-form-item label="合同年限:" prop="contractTermcontractTerm">-->
<!--              <el-input-number v-model="form.contractTerm" :precision="0" :step="1" style="width: 100%" :disabled="true"/>-->
<!--            </el-form-item>-->
<!--          </el-col>-->
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
@@ -100,7 +100,6 @@
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
                                    @change="calculateContractTerm"
              />
            </el-form-item>
          </el-col>
@@ -114,7 +113,6 @@
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
                                    @change="calculateContractTerm"
              />
            </el-form-item>
          </el-col>
@@ -181,15 +179,14 @@
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
    form.value.id = ''
  if (operationType.value === 'edit') {
    getStaffJoinInfo(row.id).then(res => {
      form.value = {...res.data}
      // ç¼–辑时也计算一次合同年限
      calculateContractTerm();
      // calculateContractTerm();
    })
  } else {
        form.value.id = ''
    }
  }
}
// æäº¤äº§å“è¡¨å•
const submitForm = () => {
src/views/personnelManagement/onboarding/components/formDiaXJHT.vue
@@ -59,6 +59,11 @@
          </el-col>
        </el-row>
        <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="身份证号:" prop="identityCard">
                            <el-input v-model="form.identityCard" placeholder="请输入身份证号码" clearable maxlength="18" />
                        </el-form-item>
                    </el-col>
          <el-col :span="12">
            <el-form-item label="年龄:" prop="age">
              <el-input-number v-model="form.age" :precision="0" :step="1" style="width: 100%"/>
@@ -84,9 +89,9 @@
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="合同签订日期:" prop="trialStartDate">
            <el-form-item label="合同签订日期:" prop="signDate">
              <el-date-picker
                  v-model="form.trialStartDate"
                  v-model="form.signDate"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
@@ -116,9 +121,9 @@
        </el-row>
        <el-row :gutter="30" v-if="showProbationDates">
          <el-col :span="12">
            <el-form-item label="试用期开始日期:" prop="signDate">
            <el-form-item label="试用期开始日期:" prop="trialStartDate">
              <el-date-picker
                  v-model="form.signDate"
                  v-model="form.trialStartDate"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
@@ -142,13 +147,13 @@
            </el-form-item>
          </el-col>
        </el-row>
<!--        <el-row :gutter="30">-->
<!--          <el-col :span="12">-->
<!--            <el-form-item label="合同年限:" prop="contractTerm">-->
<!--              <el-input-number v-model="form.contractTerm" :precision="0" :step="1" style="width: 100%" :disabled="true"/>-->
<!--            </el-form-item>-->
<!--          </el-col>-->
<!--        </el-row>-->
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="合同年限:" prop="contractTerm">
              <el-input-number v-model="form.contractTerm" :precision="0" :step="1" style="width: 100%" :disabled="true"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="合同开始日期:" prop="contractStartTime">
@@ -160,6 +165,7 @@
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
                                    @change="calculateContractTerm"
              />
            </el-form-item>
          </el-col>
@@ -173,6 +179,7 @@
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
                                    @change="calculateContractTerm"
              />
            </el-form-item>
          </el-col>
@@ -234,6 +241,7 @@
    staffNo: "",
    staffName: "",
    sex: "",
    identityCard: "",
    nativePlace: "",
    postJob: "",
    adress: "",
@@ -244,20 +252,22 @@
    emergencyContact: "",
    emergencyContactPhone: "",
    dateSelect: "",
    signDate: "",
        trialStartDate: "",
    trialEndDate: "",
    proSalary: null,
    trialStartDate: "",
    signDate: "",
    salarySelect: "",
    // contractTerm: 0,
    contractTerm: 0,
    contractStartTime: "",
    contractEndTime: "",
    staffState: "",
        remark: "无",
  },
  rules: {
    staffNo: [{ required: true, message: "请输入", trigger: "blur" },],
    staffName: [{ required: true, message: "请输入", trigger: "blur" }],
    sex: [{ required: true, message: "请输入", trigger: "blur" }],
    identityCard: [{ required: true, message: "请输入身份证号码", trigger: "blur" }],
    nativePlace: [{ required: true, message: "请输入", trigger: "blur" }],
    postJob: [{ required: true, message: "请输入", trigger: "blur" }],
    adress: [{ required: true, message: "请输入", trigger: "blur" }],
@@ -268,13 +278,14 @@
    emergencyContact: [{ required: true, message: "请输入", trigger: "blur" }],
        remark: [{ required: false, message: "请输入", trigger: "blur" }],
    dateSelect: [{ required: true, message: "请选择劳动合同期限", trigger: "change" }],
    signDate: [{ required: true, message: "请选择", trigger: "change" }],
        trialStartDate: [{ required: true, message: "请选择", trigger: "change" }],
    trialEndDate: [{ required: true, message: "请选择", trigger: "change" }],
    trialStartDate: [{ required: true, message: "请选择", trigger: "change" }],
    signDate: [{ required: true, message: "请选择", trigger: "change" }],
    salarySelect: [{ required: true, message: "请选择工资报酬方式", trigger: "change" }],
    // contractTerm: [{ required: true, message: "请输入", trigger: "blur" }],
    contractTerm: [{ required: true, message: "请输入", trigger: "blur" }],
    contractStartTime: [{ required: true, message: "请输入", trigger: "blur" }],
    contractEndTime: [{ required: true, message: "请输入", trigger: "blur" }],
        proSalary: [{ required: true, message: "请输入", trigger: "blur" }],
  },
});
const { form, rules } = toRefs(data);
@@ -298,11 +309,12 @@
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
    form.value.id = ''
  if (operationType.value === 'edit') {
    getStaffJoinInfo(row.id).then(res => {
      form.value = {...res.data}
      // ç¼–辑时也计算一次合同年限
      // calculateContractTerm();
      calculateContractTerm();
    })
  }
}
@@ -326,32 +338,32 @@
  })
}
// è®¡ç®—合同年限
// const calculateContractTerm = () => {
//   if (form.value.contractStartTime && form.value.contractEndTime) {
//     const startDate = new Date(form.value.contractStartTime);
//     const endDate = new Date(form.value.contractEndTime);
//
//     if (endDate > startDate) {
//       // è®¡ç®—年份差
//       const yearDiff = endDate.getFullYear() - startDate.getFullYear();
//       const monthDiff = endDate.getMonth() - startDate.getMonth();
//       const dayDiff = endDate.getDate() - startDate.getDate();
//
//       let years = yearDiff;
//
//       // å¦‚果结束日期的月日小于开始日期的月日,则减去1å¹´
//       if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) {
//         years = yearDiff - 1;
//       }
//
//       form.value.contractTerm = Math.max(0, years);
//     } else {
//       form.value.contractTerm = 0;
//     }
//   } else {
//     form.value.contractTerm = 0;
//   }
// };
const calculateContractTerm = () => {
  if (form.value.contractStartTime && form.value.contractEndTime) {
    const startDate = new Date(form.value.contractStartTime);
    const endDate = new Date(form.value.contractEndTime);
    if (endDate > startDate) {
      // è®¡ç®—年份差
      const yearDiff = endDate.getFullYear() - startDate.getFullYear();
      const monthDiff = endDate.getMonth() - startDate.getMonth();
      const dayDiff = endDate.getDate() - startDate.getDate();
      let years = yearDiff;
      // å¦‚果结束日期的月日小于开始日期的月日,则减去1å¹´
      if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) {
        years = yearDiff - 1;
      }
      form.value.contractTerm = Math.max(0, years);
    } else {
      form.value.contractTerm = 0;
    }
  } else {
    form.value.contractTerm = 0;
  }
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
src/views/personnelManagement/onboarding/index.vue
@@ -57,8 +57,8 @@
<script setup>
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import FormDia from "@/views/personnelManagement/onboarding/components/formDia.vue";
// import FormDia from "@/views/personnelManagement/onboarding/components/formDiaXJHT.vue"; // æ–°ç–†é£Ÿå“å…¬å¸ç”¨çš„表单
// import FormDia from "@/views/personnelManagement/onboarding/components/formDia.vue";
import FormDia from "@/views/personnelManagement/onboarding/components/formDiaXJHT.vue"; // æ–°ç–†é£Ÿå“å…¬å¸ç”¨çš„表单
import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js";
import {ElMessageBox} from "element-plus";
import dayjs from "dayjs";
src/views/personnelManagement/payrollManagement/components/formDia.vue
@@ -27,128 +27,15 @@
                            <el-select v-model="form.staffId" placeholder="请选择人员" style="width: 100%" @change="handleSelect" :disabled="operationType === 'edit'">
                                <el-option
                                    v-for="item in personList"
                                    :key="item.id"
                                    :label="item.staffName"
                                    :value="item.id"
                                    :key="item.userId"
                                    :label="item.nickName"
                                    :value="item.userId"
                                />
                            </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="应出勤天数:" prop="shouldAttendedNum">
                            <el-input v-model="form.shouldAttendedNum" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="实际出勤天数:" prop="actualAttendedNum">
              <el-input v-model="form.actualAttendedNum" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="基本工资:" prop="basicSalary">
              <el-input v-model="form.basicSalary" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="岗位工资:" prop="postSalary">
              <el-input v-model="form.postSalary" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="入离职缺勤扣款:" prop="deductionAbsenteeism">
              <el-input v-model="form.deductionAbsenteeism" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="病假扣款:" prop="sickLeaveDeductions">
              <el-input v-model="form.sickLeaveDeductions" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="事假扣款:" prop="deductionPersonalLeave">
              <el-input v-model="form.deductionPersonalLeave" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="忘记打卡扣款:" prop="forgetClockDeduct">
              <el-input v-model="form.forgetClockDeduct" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="绩效得分:" prop="performanceScore">
              <el-input v-model="form.performanceScore" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="绩效工资:" prop="performancePay">
              <el-input v-model="form.performancePay" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="应发合计:" prop="payableWages">
              <el-input v-model="form.payableWages" placeholder="请输入" clearable type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="社保个人:" prop="socialSecurityIndividuals">
              <el-input v-model="form.socialSecurityIndividuals" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="社保公司:" prop="socialSecurityCompanies">
                            <el-input v-model="form.socialSecurityCompanies" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="社保合计:" prop="socialSecurityTotal">
                            <el-input v-model="form.socialSecurityTotal" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="公积金个人:" prop="providentFundIndividuals">
                            <el-input v-model="form.providentFundIndividuals" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="公积金公司:" prop="providentFundCompany">
                            <el-input v-model="form.providentFundCompany" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="公积金合计:" prop="providentFundTotal">
                            <el-input v-model="form.providentFundTotal" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="应税工资:" prop="taxableWaget">
                            <el-input v-model="form.taxableWaget" :precision="0" :step="1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="个人所得税:" prop="personalIncomeTax">
                            <el-input v-model="form.personalIncomeTax" :step="0.1" style="width: 100%" type="number"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="实发工资:" prop="actualWages">
                            <el-input v-model="form.actualWages" style="width: 100%" type="number"/>
@@ -170,6 +57,7 @@
import {ref} from "vue";
import {getStaffJoinInfo, getStaffOnJob, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js";
import {compensationAdd, compensationUpdate} from "@/api/personnelManagement/payrollManagement.js";
import {listUser} from "@/api/system/user.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
@@ -180,50 +68,11 @@
        payDate: "",
    staffId: "",
        name: "",
        shouldAttendedNum: "",
        actualAttendedNum: "",
        basicSalary: "",
        postSalary: "",
        deductionAbsenteeism: "",
        sickLeaveDeductions: "",
        deductionPersonalLeave: "",
        forgetClockDeduct: "",
        performanceScore: "",
        performancePay: "",
        payableWages: "",
        socialSecurityIndividuals: "",
        socialSecurityCompanies: "",
        socialSecurityTotal: "",
        providentFundIndividuals: "",
        providentFundCompany: "",
        providentFundTotal: "",
        taxableWaget: "",
        personalIncomeTax: "",
        actualWages: "",
  },
  rules: {
        payDate: [{ required: true, message: "请选择", trigger: "change" },],
        staffId: [{ required: true, message: "请选择", trigger: "change" },],
    staffName: [{ required: true, message: "请输入", trigger: "blur" }],
        shouldAttendedNum: [{ required: true, message: "请输入", trigger: "blur" }],
        actualAttendedNum: [{ required: true, message: "请输入", trigger: "blur" }],
        basicSalary: [{ required: true, message: "请输入", trigger: "blur" }],
        postSalary: [{ required: true, message: "请输入", trigger: "blur" }],
        deductionAbsenteeism: [{ required: true, message: "请输入", trigger: "blur" }],
        sickLeaveDeductions: [{ required: true, message: "请输入", trigger: "blur" }],
        deductionPersonalLeave: [{ required: true, message: "请输入", trigger: "blur" }],
        forgetClockDeduct: [{ required: true, message: "请输入", trigger: "blur" }],
        performanceScore: [{ required: true, message: "请输入", trigger: "blur" }],
        performancePay: [{ required: true, message: "请输入", trigger: "blur" }],
        payableWages: [{ required: true, message: "请输入", trigger: "blur" }],
        socialSecurityIndividuals: [{ required: true, message: "请输入", trigger: "blur" }],
        socialSecurityCompanies: [{ required: true, message: "请输入", trigger: "blur" }],
        socialSecurityTotal: [{ required: true, message: "请输入", trigger: "blur" }],
        providentFundIndividuals: [{ required: true, message: "请输入", trigger: "blur" }],
        providentFundCompany: [{ required: true, message: "请输入", trigger: "blur" }],
        providentFundTotal: [{ required: true, message: "请输入", trigger: "blur" }],
        taxableWaget: [{ required: true, message: "请输入", trigger: "blur" }],
        personalIncomeTax: [{ required: true, message: "请输入", trigger: "blur" }],
        actualWages: [{ required: true, message: "请输入", trigger: "blur" }],
  },
});
@@ -234,8 +83,8 @@
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
    getStaffOnJob().then(res => {
        personList.value = res.data
    listUser().then(res => {
        personList.value = res.rows
    })
    form.value = {}
  if (operationType.value === 'edit') {
@@ -246,10 +95,9 @@
  }
}
const handleSelect = (value) => {
    console.log('value', value)
    const index = personList.value.findIndex(row => row.id === value)
    const index = personList.value.findIndex(row => row.userId === value)
    if (index > -1) {
        form.value.name = personList.value[index].staffName
        form.value.name = personList.value[index].nickName
    }
}
// æäº¤äº§å“è¡¨å•
src/views/personnelManagement/payrollManagement/index.vue
@@ -27,9 +27,10 @@
                >
            </div>
            <div>
                <el-button @click="handleExport" style="margin-right: 10px">导出</el-button>
                <el-button @click="handleImport">导入</el-button>
                <el-button type="primary" @click="openForm('add')">新增薪资</el-button>
                <el-button type="danger" plain @click="handleDelete">删除</el-button>
                <el-button @click="handleExport">导出</el-button>
            </div>
        </div>
        <div class="table_list">
@@ -46,17 +47,56 @@
            ></PIMTable>
        </div>
        <form-dia ref="formDia" @close="handleQuery"></form-dia>
        <!-- å¯¼å…¥å¼¹çª— -->
        <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body @close="handleUploadClose">
            <el-upload
                ref="uploadRef"
                :limit="1"
                accept=".xlsx, .xls"
                :headers="upload.headers"
                :action="upload.url"
                :disabled="upload.isUploading"
                :on-progress="upload.onProgress"
                :on-success="upload.onSuccess"
                :on-error="upload.onError"
                :on-change="upload.onChange"
                :auto-upload="false"
                drag
            >
                <el-icon class="el-icon--upload"><upload-filled /></el-icon>
                <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
                <template #tip>
                    <div class="el-upload__tip text-center">
                        <span>仅允许导入xls、xlsx格式文件。</span>
                        <el-link
                            type="primary"
                            :underline="false"
                            style="font-size: 12px; vertical-align: baseline"
                            @click="importTemplate"
                        >下载模板</el-link>
                    </div>
                </template>
            </el-upload>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitFileForm">ç¡® å®š</el-button>
                    <el-button @click="handleUploadCancel">取 æ¶ˆ</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { Search, UploadFilled } from "@element-plus/icons-vue";
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick} from "vue";
import FormDia from "@/views/personnelManagement/payrollManagement/components/formDia.vue";
import {staffJoinDel} from "@/api/personnelManagement/onboarding.js";
import {ElMessageBox} from "element-plus";
import dayjs from "dayjs";
import {compensationDelete, compensationListPage} from "@/api/personnelManagement/payrollManagement.js";
import { getToken } from "@/utils/auth.js";
const data = reactive({
    searchForm: {
@@ -73,98 +113,6 @@
    {
        label: "姓名",
        prop: "name",
    },
    {
        label: "应出勤天数",
        prop: "shouldAttendedNum",
        width:100
    },
    {
        label: "实际出勤天数",
        prop: "actualAttendedNum",
        width:110
    },
    {
        label: "基本工资",
        prop: "basicSalary",
    },
    {
        label: "岗位工资",
        prop: "postSalary",
        width:100
    },
    {
        label: "入离职缺勤扣款",
        prop: "deductionAbsenteeism",
        width:130
    },
    {
        label: "病假扣款",
        prop: "sickLeaveDeductions",
        width:100
    },
    {
        label: "事假扣款",
        prop: "deductionPersonalLeave",
        width:100
    },
    {
        label: "忘记打卡扣款",
        prop: "forgetClockDeduct",
        width:110
    },
    {
        label: "绩效得分",
        prop: "performanceScore",
        width:150
    },
    {
        label: "绩效工资",
        prop: "performancePay",
        width: 120
    },
    {
        label: "应发合计",
        prop: "payableWages",
        width:150
    },
    {
        label: "社保个人",
        prop: "socialSecurityIndividuals",
    },
    {
        label: "社保公司",
        prop: "socialSecurityCompanies",
        width: 120
    },
    {
        label: "社保合计",
        prop: "socialSecurityTotal",
        width: 120
    },
    {
        label: "公积金个人",
        prop: "providentFundIndividuals",
        width: 120
    },
    {
        label: "公积金公司",
        prop: "providentFundCompany",
        width: 120
    },
    {
        label: "公积金合计",
        prop: "providentFundTotal",
        width: 120
    },
    {
        label: "应税工资",
        prop: "taxableWaget",
    },
    {
        label: "个人所得税",
        prop: "personalIncomeTax",
        width: 120
    },
    {
        label: "实发工资",
@@ -197,6 +145,56 @@
});
const formDia = ref()
const { proxy } = getCurrentInstance()
// å¯¼å…¥åŠŸèƒ½é…ç½®
const upload = reactive({
  // æ˜¯å¦æ˜¾ç¤ºå¼¹å‡ºå±‚(薪资导入)
  open: false,
  // å¼¹å‡ºå±‚标题(薪资导入)
  title: "",
  // æ˜¯å¦ç¦ç”¨ä¸Šä¼ 
  isUploading: false,
  // è®¾ç½®ä¸Šä¼ çš„请求头部
  headers: { Authorization: "Bearer " + getToken() },
  // ä¸Šä¼ çš„地址
  url: import.meta.env.VITE_APP_BASE_API + "/compensationPerformance/importData",
  // æ–‡ä»¶ä¸Šä¼ å‰çš„回调
  beforeUpload: (file) => {
    // å¯ä»¥åœ¨æ­¤å¤„做文件类型或大小校验
    const isValid = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
    if (!isValid) {
      proxy.$modal.msgError("只能上传 Excel æ–‡ä»¶");
    }
    return isValid;
  },
  // æ–‡ä»¶çŠ¶æ€æ”¹å˜æ—¶çš„å›žè°ƒ
  onChange: (file, fileList) => {
    console.log('文件状态改变', file, fileList);
  },
  // æ–‡ä»¶ä¸Šä¼ æˆåŠŸæ—¶çš„å›žè°ƒ
  onSuccess: (response, file, fileList) => {
    upload.isUploading = false;
    if(response.code === 200){
      proxy.$modal.msgSuccess("文件上传成功");
      handleUploadClose();
      getList();
    }else if(response.code === 500){
      proxy.$modal.msgError(response.msg);
    }else{
      proxy.$modal.msgWarning(response.msg);
    }
  },
  // æ–‡ä»¶ä¸Šä¼ å¤±è´¥æ—¶çš„回调
  onError: (error, file, fileList) => {
    console.error('上传失败', error, file, fileList);
    upload.isUploading = false;
    proxy.$modal.msgError("文件上传失败");
  },
  // æ–‡ä»¶ä¸Šä¼ è¿›åº¦å›žè°ƒ
  onProgress: (event, file, fileList) => {
    console.log('上传中...', event.percent);
  }
});
const handleDateChange = (value,type) => {
    searchForm.value.entryDateEnd = null
@@ -299,6 +297,39 @@
        });
};
/** å¯¼å…¥æŒ‰é’®æ“ä½œ */
function handleImport() {
  upload.title = "薪资导入";
  upload.open = true;
}
/** æäº¤ä¸Šä¼ æ–‡ä»¶ */
function submitFileForm() {
  upload.isUploading = true;
  proxy.$refs["uploadRef"].submit();
}
/** ä¸‹è½½æ¨¡æ¿ */
function importTemplate() {
  proxy.download("/compensationPerformance/exportTemplate", {}, "薪资导入模板.xlsx");
}
// å¤„理上传弹框取消
function handleUploadCancel() {
  upload.open = false;
  handleUploadClose();
}
// å¤„理上传弹框关闭
function handleUploadClose() {
  upload.open = false;
  upload.isUploading = false;
  // æ¸…空上传文件缓存
  if (proxy.$refs.uploadRef) {
    proxy.$refs.uploadRef.clearFiles();
  }
}
onMounted(() => {
    getList();
});
src/views/personnelManagement/scheduling/index.vue
@@ -11,34 +11,14 @@
              style="width: 150px"
          />
        </el-form-item>
        <el-form-item label="班次类型:">
          <el-select v-model="filterForm.shiftType" placeholder="请选择班次" clearable style="width: 120px">
            <el-option v-for="item in shift_type" :label="item.label" :value="item.value" :key="item.value"/>
          </el-select>
        </el-form-item>
        <el-form-item label="日期范围:">
          <el-date-picker
              v-model="filterForm.dateRange"
              type="daterange"
              range-separator="至"
              start-placeholder="开始日期"
              end-placeholder="结束日期"
              format="YYYY-MM-DD"
              value-format="YYYY-MM-DD"
              style="width: 250px"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleFilter">
            <el-icon><Search/></el-icon>
            ç­›é€‰
            æœç´¢
          </el-button>
          <el-button @click="resetFilter">
            <el-icon><Refresh/></el-icon>
            é‡ç½®
          </el-button>
          <el-button @click="handleExport">
            <el-icon><Download/></el-icon>
            å¯¼å‡º
          </el-button>
          <el-button type="primary" @click="openScheduleDialog('add')">
@@ -61,48 +41,53 @@
          @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="55"/>
        <el-table-column prop="staffName" label="员工姓名" width="120"/>
        <el-table-column prop="staffNo" label="员工工号" width="100"/>
        <el-table-column prop="department" label="部门" width="120">
        <el-table-column prop="staffName" label="员工姓名"/>
        <!-- <el-table-column prop="staffNo" label="员工工号" width="100"/> -->
        <!-- <el-table-column prop="department" label="部门" width="120">
          <template #default="scope">
              {{ (department_type.find(i => i.value === String(scope.row.department)) || {}).label }}
          </template>
        </el-table-column>
        <el-table-column prop="shiftType" label="班次类型" width="100">
        </el-table-column> -->
        <el-table-column prop="shiftType" label="班次类型" width="120">
          <template #default="scope">
            <el-tag :type="getShiftTagType(scope.row.shiftType)">
              {{ (shift_type.find(i => i.value === String(scope.row.shiftType)) || {}).label }}
              {{ (shift_type.find(i => i.value === String(scope.row.shiftType)) || {}).label || '未知' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="workDate" label="工作日期" width="120"/>
        <el-table-column prop="startTime" label="开始时间" width="100"/>
        <el-table-column prop="endTime" label="结束时间" width="100"/>
        <el-table-column prop="workHours" label="工作时长" width="100">
        <!-- <el-table-column prop="workDate" label="工作日期" width="120"/> -->
        <el-table-column prop="workStartTime" label="开始时间"/>
        <el-table-column prop="workEndTime" label="结束时间"/>
        <el-table-column prop="lunchTime" label="午休时间(h)">
          <template #default="scope">
            {{ scope.row.lunchTime }}小时
          </template>
        </el-table-column>
        <!-- <el-table-column prop="workHours" label="工作时长" width="100">
          <template #default="scope">
            {{ scope.row.workHours }}小时
          </template>
        </el-table-column>
        <el-table-column prop="status" label="状态" width="100">
        </el-table-column> -->
        <!-- <el-table-column prop="status" label="状态" width="100">
          <template #default="scope">
            <el-tag :type="getStatusTagType(scope.row.status)">
              {{ (schedule_status.find(i => i.value === String(scope.row.status)) || {}).label }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="remark" label="备注" min-width="150"/>
        <el-table-column label="操作" width="200" fixed="right">
        </el-table-column> -->
        <!-- <el-table-column prop="remark" label="备注" min-width="150"/> -->
        <el-table-column label="操作" width="200" fixed="right" align="center">
          <template #default="scope">
            <el-button
                type="primary"
                size="small"
                            link
                            type="primary"
                @click="openScheduleDialog('edit', scope.row)"
            >
              ç¼–辑
            </el-button>
            <el-button
                type="danger"
                size="small"
                            link
                            type="danger"
                @click="handleDelete(scope.row)"
            >
              åˆ é™¤
@@ -144,22 +129,36 @@
          label-width="120px"
      >
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="员工姓名:" prop="staffId">
              <el-select v-model="scheduleForm.staffId" placeholder="请输入员工姓名" style="width: 100%"
          <el-col :span="24">
            <el-form-item label="员工姓名:" prop="staffIds">
          <el-select v-model="scheduleForm.staffIds" placeholder="请选择员工姓名" style="width: 100%"
                         multiple filterable collapse-tags-tooltip
                         @change="handleSelectStaff">
                <el-option v-for="item in personList" :label="item.staffName" :value="item.id" :key="item.id"/>
              </el-select>
            </el-form-item>
            <el-option v-for="item in personList" :label="item.nickName" :value="item.userId" :key="item.userId"/>
          </el-select>
        </el-form-item>
          </el-col>
        </el-row>
        <!-- <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="员工工号:" prop="staffNo">
              <el-input :disabled="true" v-model="scheduleForm.staffNo" placeholder=""/>
            </el-form-item>
          </el-col>
        </el-row>
        </el-row> -->
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="班次类型:" prop="shiftType">
              <el-select v-model="scheduleForm.shiftType" placeholder="请选择班次类型" style="width: 100%">
                <el-option v-for="item in shift_type" :label="item.label" :value="item.value" :key="item.value"/>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <!-- <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="部门:" prop="department">
              <el-select v-model="scheduleForm.department" placeholder="请选择部门" style="width: 100%">
@@ -174,9 +173,9 @@
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        </el-row> -->
        <el-row :gutter="20">
        <!-- <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="工作日期:" prop="workDate">
              <el-date-picker
@@ -196,34 +195,49 @@
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        </el-row> -->
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="开始时间:" prop="startTime">
            <el-form-item label="开始时间:" prop="workStartTime">
              <el-time-picker
                  v-model="scheduleForm.startTime"
                  v-model="scheduleForm.workStartTime"
                  placeholder="选择开始时间"
                  style="width: 100%"
                  format="HH:mm"
                  value-format="HH:mm"
                  value-format="YYYY-MM-DD HH:mm:ss"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="结束时间:" prop="endTime">
            <el-form-item label="结束时间:" prop="workEndTime">
              <el-time-picker
                  v-model="scheduleForm.endTime"
                  v-model="scheduleForm.workEndTime"
                  placeholder="选择结束时间"
                  style="width: 100%"
                  format="HH:mm"
                  value-format="HH:mm"
                  value-format="YYYY-MM-DD HH:mm:ss"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="午休时间(h):" prop="lunchTime">
              <el-input-number
                  v-model="scheduleForm.lunchTime"
                  :min="0"
                  :max="8"
                  :step="0.5"
                  placeholder="请输入午休时间"
                  style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <!-- <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="备注:" prop="remark">
              <el-input
@@ -234,7 +248,7 @@
              />
            </el-form-item>
          </el-col>
        </el-row>
        </el-row> -->
      </el-form>
      <template #footer>
@@ -248,7 +262,7 @@
</template>
<script setup>
import {ref, reactive, computed, onMounted, getCurrentInstance} from 'vue'
import {ref, reactive, computed, onMounted, getCurrentInstance, watch} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import {useDict} from "@/utils/dict.js"
import {Plus, Download, Search, Refresh} from '@element-plus/icons-vue'
@@ -256,6 +270,7 @@
import {getStaffOnJob} from "@/api/personnelManagement/onboarding.js";
import dayjs from "dayjs";
import pagination from "@/components/PIMTable/Pagination.vue";
import {listUser} from "@/api/system/user.js";
const { proxy } = getCurrentInstance();
@@ -269,8 +284,6 @@
// ç­›é€‰è¡¨å•
const filterForm = reactive({
  staffName: '',
  shiftType: '',
  dateRange: [],
  current:1,
  size: 10
})
@@ -278,29 +291,31 @@
// æŽ’班表单
const scheduleForm = reactive({
  id: '',
  staffId: '',
  staffNo: '',
  department: '',
  staffIds: [],
  // staffNo: '',
  // department: '',
  shiftType: '',
  workDate: '',
  startTime: '',
  endTime: '',
  // workDate: '',
  workStartTime: '',
  workEndTime: '',
  workHours: 0,
  status: '',
  remark: ''
  lunchTime: 3,
  // workStartTime: '',
  // workEndTime: '',
  // workHours: 0,
  // status: '',
  // remark: ''
})
// è¡¨å•验证规则
const scheduleRules = reactive({
  staffId: [{required: true, message: '请选择员工', trigger: 'change'}],
  department: [{required: true, message: '请选择部门', trigger: 'change'}],
  staffIds: [{required: true, message: '请选择员工', trigger: 'change'}],
  // department: [{required: true, message: '请选择部门', trigger: 'change'}],
  shiftType: [{required: true, message: '请选择班次类型', trigger: 'change'}],
  workDate: [{required: true, message: '请选择工作日期', trigger: 'change'}],
  startTime: [{required: true, message: '请选择开始时间', trigger: 'change'}],
  endTime: [{required: true, message: '请选择结束时间', trigger: 'change'}],
  status: [{required: true, message: '请选择状态', trigger: 'change'}]
  // workDate: [{required: true, message: '请选择工作日期', trigger: 'change'}],
  workStartTime: [{required: true, message: '请选择开始时间', trigger: 'change'}],
  workEndTime: [{required: true, message: '请选择结束时间', trigger: 'change'}],
  lunchTime: [{required: true, message: '请输入午休时间', trigger: 'blur'}],
  // status: [{required: true, message: '请选择状态', trigger: 'change'}]
})
const tableLoading = ref(false)
@@ -317,9 +332,9 @@
 * èŽ·å–å½“å‰åœ¨èŒäººå‘˜åˆ—è¡¨
 */
const getPersonList = () => {
  getStaffOnJob().then(res => {
    personList.value = res.data
  })
    listUser().then(res => {
        personList.value = res.rows
    })
};
const paginationChange = (obj) => {
  filterForm.current = obj.page;
@@ -328,9 +343,9 @@
};
const handleSelectStaff = (val) => {
  let obj = personList.value.find(item => item.id === val)
  scheduleForm.staffNo = obj.staffNo
  // å¤šé€‰å‘˜å·¥ï¼Œä¸å†è‡ªåŠ¨è®¾ç½®å‘˜å·¥å·¥å·
  // let obj = personList.value.find(item => item.id === val)
  // scheduleForm.staffNo = obj.staffNo
}
// èŽ·å–ç­æ¬¡æ ‡ç­¾ç±»åž‹
@@ -349,19 +364,19 @@
const handleFilter = async () => {
  tableLoading.value = true
  let searchForm = {
    ...filterForm,
    ...(filterForm.dateRange.length > 0 && {
      startDate: filterForm.dateRange[0],
      endDate: filterForm.dateRange[1],
    })
    ...filterForm
  }
  let resp = await listPage(searchForm)
  tableCount.value = resp.data.total
  scheduleList.value = resp.data.records.map(it => {
    return {
      ...it,
      'startTime': dayjs(it.workStartTime).format('HH:mm'),
      'endTime': dayjs(it.workEndTime).format('HH:mm'),
      // ä¿å­˜åŽŸå§‹æ—¶é—´æ ¼å¼ç”¨äºŽç¼–è¾‘
      'originalWorkStartTime': it.workStartTime,
      'originalWorkEndTime': it.workEndTime,
      // æ ¼å¼åŒ–时间用于表格显示
      'workStartTime': dayjs(it.workStartTime).format('HH:mm'),
      'workEndTime': dayjs(it.workEndTime).format('HH:mm'),
    }
  })
  tableLoading.value = false
@@ -371,8 +386,6 @@
// é‡ç½®ç­›é€‰
const resetFilter = () => {
  filterForm.staffName = ''
  filterForm.shiftType = ''
  filterForm.dateRange = []
}
// æ‰“开排班对话框
@@ -381,16 +394,29 @@
  scheduleDialog.value = true
  getPersonList()
  if (type === 'edit' && data) {
    // ç¼–辑模式,复制数据
    Object.assign(scheduleForm, {...data})
  } else {
    // æ–°å¢žæ¨¡å¼ï¼Œé‡ç½®è¡¨å•
    Object.keys(scheduleForm).forEach(key => {
      scheduleForm[key] = ''
    // ç¼–辑模式,复制数据,将员工ID字符串转换为数组格式,并处理时间字段
    Object.assign(scheduleForm, {
      ...data,
        lunchTime: Number(data.lunchTime),
      staffIds: data.staffId ? data.staffId.split(',').map(id => parseInt(id)) : [],
      // ä½¿ç”¨åŽŸå§‹æ—¶é—´å­—ç¬¦ä¸²ï¼Œå› ä¸ºè¡¨æ ¼ä¸­æ˜¾ç¤ºçš„æ˜¯æ ¼å¼åŒ–åŽçš„HH:mm
      workStartTime: data.originalWorkStartTime || '',
      workEndTime: data.originalWorkEndTime || ''
    })
    // scheduleForm.status = '已安排'
    scheduleForm.workDate = new Date().toISOString().split('T')[0]
  }
  } else {
      // æ–°å¢žæ¨¡å¼ï¼Œé‡ç½®è¡¨å•
      Object.keys(scheduleForm).forEach(key => {
        if (key === 'staffIds') {
          scheduleForm[key] = []
        } else if (key === 'lunchTime') {
          scheduleForm[key] = 3
        } else {
          scheduleForm[key] = ''
        }
      })
      // scheduleForm.status = '已安排'
      // scheduleForm.workDate = new Date().toISOString().split('T')[0]
    }
}
// å…³é—­æŽ’班对话框
@@ -401,37 +427,86 @@
// è®¡ç®—工作时长
const calculateWorkHours = () => {
  if (scheduleForm.workDate && scheduleForm.startTime && scheduleForm.endTime) {
    // ä½¿ç”¨ workDate ä¸Ž startTime å’Œ endTime ç»„合
    const startDateTime = new Date(`${scheduleForm.workDate} ${scheduleForm.startTime}`)
    const endDateTime = new Date(`${scheduleForm.workDate} ${scheduleForm.endTime}`)
  if (!scheduleForm.workStartTime || !scheduleForm.workEndTime) {
    return;
  }
  try {
    // ä½¿ç”¨dayjs正确解析时间
    const startDayjs = dayjs(scheduleForm.workStartTime);
    const endDayjs = dayjs(scheduleForm.workEndTime);
    if (!startDayjs.isValid() || !endDayjs.isValid()) {
      return;
    }
    const startDateTime = startDayjs.toDate();
    const endDateTime = endDayjs.toDate();
    // å¤„理跨天情况(结束时间早于开始时间)
    if (endDateTime < startDateTime) {
      // è·¨å¤©ï¼Œå°†ç»“束日期加一天
      endDateTime.setDate(endDateTime.getDate() + 1)
      endDateTime.setDate(endDateTime.getDate() + 1);
    }
    // è®¡ç®—工作时长(小时)
    const diffMs = endDateTime - startDateTime
    const diffHours = diffMs / (1000 * 60 * 60)
    scheduleForm.workHours = Math.round(diffHours * 100) / 100
    scheduleForm.workStartTime = dayjs(startDateTime).format("YYYY-MM-DD HH:mm:ss")
    scheduleForm.workEndTime = dayjs(endDateTime).format("YYYY-MM-DD HH:mm:ss")
    // è®¡ç®—工作时长(小时)
    const diffMs = endDateTime - startDateTime;
    const diffHours = diffMs / (1000 * 60 * 60);
    scheduleForm.workHours = Math.round(diffHours * 100) / 100;
  } catch (error) {
    console.error('时间计算错误:', error);
  }
}
// ç›‘听时间字段变化,自动计算工作时长
watch(
  () => [scheduleForm.workStartTime, scheduleForm.workEndTime],
  () => {
    calculateWorkHours()
  },
  { deep: true }
)
// æäº¤æŽ’班表单
const submitScheduleForm = async () => {
  const valid = await scheduleFormRef.value.validate()
  if (!valid) return
  calculateWorkHours()
  const newSchedule = {...scheduleForm}
  // ç”±äºŽå‘˜å·¥æ˜¯å¤šé€‰ï¼Œéœ€è¦ä¸ºæ¯ä¸ªé€‰ä¸­çš„员工创建排班记录
  const selectedStaffIds = scheduleForm.staffIds || []
  if (selectedStaffIds.length === 0) {
    ElMessage.warning('请至少选择一个员工')
    return
  }
  try {
    // èŽ·å–é€‰ä¸­çš„å‘˜å·¥å§“ååˆ—è¡¨
    const selectedStaffNames = selectedStaffIds.map(staffId => {
      const staff = personList.value.find(item => item.userId === staffId)
      return staff ? staff.nickName : ''
    }).filter(name => name !== '')
    // å°†å‘˜å·¥å§“名组装成逗号分隔的字符串
    const staffNameString = selectedStaffNames.join(',')
    // åˆ›å»ºæŽ’班记录,将员工姓名保存为字符串格式
    const newSchedule = {
      ...scheduleForm,
      staffName: staffNameString,
      staffId: selectedStaffIds.join(','), // å°†å‘˜å·¥ID也保存为逗号分隔的字符串
      // è®¾ç½®å…¶ä»–必要字段的默认值
      staffNo: '', // å¯ä»¥æ ¹æ®éœ€è¦ä»ŽpersonList中获取
      department: '',
      shiftType: scheduleForm.shiftType,
      workDate: '',
      status: '',
      remark: ''
    }
    await save(newSchedule)
    ElMessage.success('保存排班成功')
    ElMessage.success(`成功为 ${selectedStaffNames.length} ä¸ªå‘˜å·¥åˆ›å»ºæŽ’班`)
    handleFilter()
    closeScheduleDialog()
@@ -491,11 +566,7 @@
// å¯¼å‡º
const handleExport = () => {
  let searchForm = {
    ...filterForm,
    ...(filterForm.dateRange.length > 0 && {
      startDate: filterForm.dateRange[0],
      endDate: filterForm.dateRange[1],
    })
    ...filterForm
  }
  proxy.download('/staff/staffScheduling/export', {}, '人员排班.xlsx')
}
src/views/procurementManagement/invoiceEntry/components/Modal.vue
@@ -1,160 +1,202 @@
<template>
  <el-dialog :title="modalOptions.title" v-model="visible" width="70%">
    <el-form
      ref="formRef"
      :model="form"
      :rules="rules"
      label-width="120px"
      label-position="top"
    >
      <el-row :gutter="30">
        <el-col :span="12">
          <el-form-item label="采购合同号:" prop="purchaseLedgerNo">
            <el-input v-model="form.purchaseLedgerNo" disabled />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="销售合同号:" prop="salesContractNo">
            <el-input
              v-model="form.salesContractNo"
              placeholder="自动填充"
              clearable
              disabled
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="供应商名称:" prop="supplierName">
            <el-input
              v-model="form.supplierName"
              placeholder="自动填充"
              clearable
              disabled
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="项目名称:" prop="projectName">
            <el-input
              v-model="form.projectName"
              placeholder="自动填充"
              clearable
              disabled
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="发票号:" prop="invoiceNumber">
            <el-input
              v-model="form.invoiceNumber"
              placeholder="请输入"
              clearable
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="发票金额(元):" prop="invoiceAmount">
            <el-input-number :step="0.01" :min="0" style="width: 100%"
              v-model="form.invoiceAmount"
              placeholder="自动填充"
              clearable
              :disabled="true"
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="录入人:" prop="issUer">
            <el-input
              v-model="form.issUer"
              placeholder="请输入"
              clearable
              disabled
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="开票日期:" prop="entryDate">
            <el-date-picker
              style="width: 100%"
              v-model="form.entryDate"
              type="date"
              value-format="YYYY-MM-DD"
              format="YYYY-MM-DD"
              clearable
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="上传附件">
            <FileUpload
              :showTip="false"
              accept="*"
              :autoUpload="true"
              :action="action"
              :headers="{
    <el-dialog :title="modalOptions.title" v-model="visible" width="70%">
        <el-form
            ref="formRef"
            :model="form"
            :rules="rules"
            label-width="120px"
            label-position="top"
        >
            <el-row :gutter="30">
                <el-col :span="12">
                    <el-form-item label="采购合同号:" prop="purchaseLedgerNo">
                        <el-input v-model="form.purchaseLedgerNo" disabled placeholder="多合同批量处理(具体合同号见产品列表)" />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="销售合同号:" prop="salesContractNo">
                        <el-input
                            v-model="form.salesContractNo"
                            placeholder="自动填充"
                            clearable
                            disabled
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="供应商名称:" prop="supplierName">
                        <el-input
                            v-model="form.supplierName"
                            placeholder="自动填充"
                            clearable
                            disabled
                        />
                    </el-form-item>
                </el-col>
<!--                <el-col :span="12">-->
<!--                    <el-form-item label="项目名称:" prop="projectName">-->
<!--                        <el-input-->
<!--                            v-model="form.projectName"-->
<!--                            placeholder="自动填充"-->
<!--                            clearable-->
<!--                            disabled-->
<!--                        />-->
<!--                    </el-form-item>-->
<!--                </el-col>-->
                <el-col :span="12">
                    <el-form-item label="发票号:" prop="invoiceNumber">
                        <el-input
                            v-model="form.invoiceNumber"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="发票金额(元):" prop="invoiceAmount">
                        <el-input-number :step="0.01" :min="0" style="width: 100%"
                                                         v-model="form.invoiceAmount"
                                                         placeholder="请输入发票金额"
                                                         clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="录入人:" prop="issUer">
                        <el-input
                            v-model="form.issUer"
                            placeholder="请输入"
                            clearable
                            disabled
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="开票日期:" prop="entryDate">
                        <el-date-picker
                            style="width: 100%"
                            v-model="form.entryDate"
                            type="date"
                            value-format="YYYY-MM-DD"
                            format="YYYY-MM-DD"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="录入日期:" prop="enterDate">
                        <el-date-picker
                            style="width: 100%"
                            v-model="form.enterDate"
                            type="date"
                            value-format="YYYY-MM-DD"
                            format="YYYY-MM-DD"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="上传附件">
                        <FileUpload
                            :showTip="false"
                            accept="*"
                            :autoUpload="true"
                            :action="action"
                            :headers="{
                Authorization: 'Bearer ' + getToken(),
              }"
              :limit="10"
              @success="uploadSuccess"
              @remove="removeFile"
            />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="录入日期:" prop="enterDate">
            <el-date-picker
              style="width: 100%"
              v-model="form.enterDate"
              type="date"
              value-format="YYYY-MM-DD"
              format="YYYY-MM-DD"
              clearable
            />
          </el-form-item>
        </el-col>
      </el-row>
      <el-form-item label="产品信息:"> </el-form-item>
      <PIMTable
        rowKey="id"
        :column="columns"
        :tableData="form.productData"
                :summaryMethod="summarizeChildrenTable"
                :isShowSummary="true"
        height="auto"
      >
        <template #ticketsNumRef="{ row }">
          <el-input-number
            v-model="row.ticketsNum"
            placeholder="请输入"
            :min="0"
            :step="0.1"
                        :precision="2"
            clearable
            style="width: 100%"
            @change="invoiceNumBlur(row)"
          />
        </template>
        <template #ticketsAmountRef="{ row }">
          <el-input-number
            v-model="row.ticketsAmount"
            placeholder="请输入"
            :min="0"
                        :precision="2"
            :step="0.1"
            clearable
            style="width: 100%"
            @change="invoiceAmountBlur(row)"
          />
        </template>
      </PIMTable>
    </el-form>
    <template #footer>
                            :limit="10"
                            @success="uploadSuccess"
                            @remove="removeFile"
                        />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-form-item label="产品信息:"> </el-form-item>
            <el-table
                :data="form.productData"
                border
                show-summary
                :summary-method="summarizeChildrenTable"
            >
                <el-table-column align="center" label="序号" type="index" width="60" />
                <el-table-column label="所属合同" prop="purchaseLedgerNo" width="200">
                    <template #default="{ row }">
                        <el-tag type="primary">{{ row.purchaseLedgerNo }}</el-tag>
                    </template>
                </el-table-column>
                <el-table-column label="产品大类" prop="productCategory" />
                <el-table-column label="规格型号" prop="specificationModel" width="150" />
                <el-table-column label="单位" prop="unit" width="70" />
                <el-table-column label="数量" prop="quantity" width="70" />
                <el-table-column label="税率(%)" prop="taxRate" width="80" />
                <el-table-column
                    label="含税单价(元)"
                    prop="taxInclusiveUnitPrice"
                    :formatter="formattedNumber"
                />
                <el-table-column
                    label="含税总价(元)"
                    prop="taxInclusiveTotalPrice"
                    :formatter="formattedNumber"
                />
                <el-table-column
                    label="不含税总价(元)"
                    prop="taxExclusiveTotalPrice"
                    :formatter="formattedNumber"
                />
                <el-table-column label="本次开票数" prop="ticketsNum" width="180">
                    <template #default="scope">
                        <el-input-number :step="0.1" :min="0" style="width: 100%"
                                                         :precision="2"
                                                         v-model="scope.row.ticketsNum"
                                                         @change="invoiceNumBlur(scope.row)"
                        />
                    </template>
                </el-table-column>
                <el-table-column
                    label="本次开票金额(元)"
                    prop="ticketsAmount"
                    width="180"
                >
                    <template #default="scope">
                        <el-input-number :step="0.01" :min="0" style="width: 100%"
                                                         :precision="2"
                                                         v-model="scope.row.ticketsAmount"
                                                         @change="invoiceAmountBlur(scope.row)"
                        />
                    </template>
                </el-table-column>
                <el-table-column
                    label="未来票数"
                    prop="futureTickets"
                    :formatter="formattedNumber"
                />
                <el-table-column
                    label="本次来票金额(元)"
                    prop="ticketsAmount"
                    :formatter="formattedNumber"
                />
                <el-table-column
                    label="未来票数"
                    prop="futureTickets"
                    :formatter="formattedNumber"
                />
                <el-table-column
                    label="未来票金额(元)"
                    prop="futureTicketsAmount"
                    :formatter="formattedNumber"
                />
            </el-table>
        </el-form>
        <template #footer>
            <el-button type="primary" :loading="modalLoading" @click="submitForm">
                {{ modalOptions.confirmText }}
                ç¡®è®¤
            </el-button>
      <el-button @click="closeModal">{{ modalOptions.cancelText }}</el-button>
    </template>
  </el-dialog>
            <el-button @click="closeModal">取消</el-button>
        </template>
    </el-dialog>
</template>
<script setup>
@@ -164,9 +206,9 @@
import useFormData from "@/hooks/useFormData";
import FileUpload from "@/components/Upload/FileUpload.vue";
import {
  getPurchaseNoById,
  getInfo,
  addOrUpdateRegistration,
    getPurchaseNoById,
    getInfo,
    addOrUpdateRegistration,
} from "@/api/procurementManagement/invoiceEntry.js";
import { getPurchaseById } from "@/api/procurementManagement/procurementLedger.js";
import { getToken } from "@/utils/auth";
@@ -174,7 +216,7 @@
import dayjs from "dayjs";
defineOptions({
  name: "来票登记模态框",
    name: "来票登记模态框",
});
const userStore = useUserStore();
@@ -182,155 +224,218 @@
const formRef = ref();
const { proxy } = getCurrentInstance();
const { form } = useFormData({
  purchaseLedgerNo: undefined, // é‡‡è´­åˆåŒå·
  salesContractNo: undefined, // é”€å”®åˆåŒå·
  supplierName: undefined, // ä¾›åº”商名称
  projectName: undefined, // é¡¹ç›®åç§°
  invoiceNumber: undefined, // å‘票号
  invoiceAmount: undefined, // å‘票金额(元)
  issUerId: userStore.id, // å½•入人
  issUer: userStore.nickName, // å½•入人
  entryDate: undefined, // å¼€ç¥¨æ—¥æœŸ
  salesContractNoId: undefined, // å¼€ç¥¨æ—¥æœŸ
  enterDate: dayjs().format("YYYY-MM-DD"),
  productData: [], // è¡¨æ ¼
  tempFileIds: [], // æ–‡ä»¶
    purchaseLedgerNo: undefined, // é‡‡è´­åˆåŒå·
    salesContractNo: undefined, // é”€å”®åˆåŒå·
    supplierName: undefined, // ä¾›åº”商名称
    projectName: undefined, // é¡¹ç›®åç§°
    invoiceNumber: undefined, // å‘票号
    invoiceAmount: undefined, // å‘票金额(元)
    issUerId: userStore.id, // å½•入人
    issUer: userStore.nickName, // å½•入人
    entryDate: undefined, // å¼€ç¥¨æ—¥æœŸ
    salesContractNoId: undefined, // å¼€ç¥¨æ—¥æœŸ
    enterDate: dayjs().format("YYYY-MM-DD"),
    productData: [], // è¡¨æ ¼
    tempFileIds: [], // æ–‡ä»¶
});
const selectedContracts = ref([]); // å­˜å‚¨é€‰ä¸­çš„合同数据
const rules = ref({
  invoiceNumber: [
    { required: true, message: "请输入发票号", trigger: "blur" },
    { type: "string" },
  ],
  invoiceAmount: [
    { required: true, message: "请输入发票金额", trigger: "blur" },
  ],
  entryDate: [{ required: true, message: "请选择开票日期", trigger: "change" }],
  enterDate: [{ required: true, message: "请选择录入日期", trigger: "change" }],
    invoiceNumber: [
        { required: true, message: "请输入发票号", trigger: "blur" },
        { type: "string" },
    ],
    invoiceAmount: [
        { required: true, message: "请输入发票金额", trigger: "blur" },
    ],
    entryDate: [{ required: true, message: "请选择开票日期", trigger: "change" }],
    enterDate: [{ required: true, message: "请选择录入日期", trigger: "change" }],
});
const {
  id,
  visible,
  loading: modalLoading,
  openModal,
  modalOptions,
  handleConfirm,
  closeModal,
    id,
    visible,
    loading: modalLoading,
    openModal,
    modalOptions,
    handleConfirm,
    closeModal,
} = useModal({
  title: "来票登记",
    title: "来票登记",
});
const emit = defineEmits(['refreshList']);
const columns = [
  {
    label: "产品大类",
    prop: "productCategory",
    {
        label: "产品大类",
        prop: "productCategory",
        width: 120,
  },
  {
    label: "规格型号",
    prop: "specificationModel",
    },
    {
        label: "规格型号",
        prop: "specificationModel",
        width: 120,
  },
  {
    label: "单位",
    prop: "unit",
    width: 80,
  },
  {
    label: "数量",
    prop: "quantity",
    width: 80,
  },
  {
    label: "税率(%)",
    prop: "taxRate",
    width: 80,
  },
  {
    label: "录入日期",
    prop: "registerDate",
    width: 120,
  },
  {
    label: "含税单价(元)",
    prop: "taxInclusiveUnitPrice",
    width: 150,
    formatData: (val) => {
      return val ? parseFloat(val).toFixed(2) : 0;
    },
  },
  {
    label: "含税总价(元)",
    prop: "taxInclusiveTotalPrice",
    width: 150,
    formatData: (val) => {
      return parseFloat(val).toFixed(2) ?? 0;
    },
  },
  {
    label: "不含税总价(元)",
    prop: "taxExclusiveTotalPrice",
    width: 150,
    formatData: (val) => {
      return parseFloat(val).toFixed(2) ?? 0;
    },
  },
  {
    label: "本次来票数",
    prop: "ticketsNum",
    dataType: "slot",
    slot: "ticketsNumRef",
    width: 180,
    align: "center",
  },
  {
    label: "本次来票金额(元)",
    prop: "ticketsAmount",
    dataType: "slot",
    slot: "ticketsAmountRef",
    width: 180,
    align: "center",
  },
  {
    label: "未来票数",
    prop: "futureTickets",
    },
    {
        label: "单位",
        prop: "unit",
        width: 80,
    },
    {
        label: "数量",
        prop: "quantity",
        width: 80,
    },
    {
        label: "税率(%)",
        prop: "taxRate",
        width: 80,
    },
    {
        label: "录入日期",
        prop: "registerDate",
        width: 120,
    },
    {
        label: "含税单价(元)",
        prop: "taxInclusiveUnitPrice",
        width: 150,
        formatData: (val) => {
            return val ? parseFloat(val).toFixed(2) : 0;
        },
    },
    {
        label: "含税总价(元)",
        prop: "taxInclusiveTotalPrice",
        width: 150,
        formatData: (val) => {
            return parseFloat(val).toFixed(2) ?? 0;
        },
    },
    {
        label: "不含税总价(元)",
        prop: "taxExclusiveTotalPrice",
        width: 150,
        formatData: (val) => {
            return parseFloat(val).toFixed(2) ?? 0;
        },
    },
    {
        label: "本次来票数",
        prop: "ticketsNum",
        dataType: "slot",
        slot: "ticketsNumRef",
        width: 180,
        align: "center",
    },
    {
        label: "本次来票金额(元)",
        prop: "ticketsAmount",
        dataType: "slot",
        slot: "ticketsAmountRef",
        width: 180,
        align: "center",
    },
    {
        label: "未来票数",
        prop: "futureTickets",
        width: 100,
  },
  {
    label: "未来票金额(元)",
    prop: "futureTicketsAmount",
    },
    {
        label: "未来票金额(元)",
        prop: "futureTicketsAmount",
        width: 200,
  },
    },
];
const getTableData = async (type, id) => {
  if (type == "add") {
    const { data } = await getPurchaseNoById({ id });
    form.purchaseLedgerNo = data.purchaseContractNumber;
    form.invoiceAmount = data.invoiceAmount;
    form.invoiceNumber = data.invoiceNumber;
    form.entryDate = data.entryDate;
    form.salesContractNoId = data.salesContractNoId;
    const { data: infoData } = await getInfo({ id });
    form.salesContractNo = infoData.salesContractNo;
    form.projectName = infoData.projectName;
    form.supplierName = infoData.supplierName;
    form.productData = infoData.productData;
  } else if (type == "edit") {
    const data = await getPurchaseById({ id, type: 2 });
    form.purchaseLedgerNo = data.purchaseContractNumber;
    form.invoiceAmount = data.invoiceAmount;
    form.invoiceNumber = data.invoiceNumber;
    form.salesContractNo = data.salesContractNo;
    form.projectName = data.projectName;
    form.supplierName = data.supplierName;
    form.entryDate = data.entryDate;
    form.productData = data.productData;
  }
const formattedNumber = (row, column, cellValue) => {
    if (cellValue == 0) {
        return parseFloat(cellValue).toFixed(2);
    }
    if (cellValue) {
        return parseFloat(cellValue).toFixed(2);
    } else {
        return cellValue;
    }
};
const getTableData = async (type, selectedRows) => {
    if (type == "add") {
        // æ£€æŸ¥æ‰€æœ‰é€‰æ‹©çš„合同是否具有相同的供应商名称
        const firstRow = selectedRows[0];
        const isSameSupplier = selectedRows.every(row =>
            row.supplierName === firstRow.supplierName
        );
        if (!isSameSupplier) {
            proxy.$modal.msgError("请选择相同供应商名称的合同");
            return;
        }
        // å…è®¸ä¸åŒçš„采购合同号批量处理,无需检查重复
        // æ¸…空表单数据
        Object.keys(form).forEach(key => {
            if (key !== 'productData') {
                form[key] = undefined;
            }
        });
        form.productData = [];
        // åŠ è½½æ‰€æœ‰é€‰ä¸­åˆåŒçš„äº§å“æ•°æ®
        const promises = selectedRows.map(row =>
            getInfo({ id: row.id })
        );
        Promise.all(promises).then(results => {
            // åˆå¹¶æ‰€æœ‰åˆåŒçš„产品数据,并为每个产品添加对应的合同信息
            const allProductData = [];
            results.forEach((result, index) => {
                const contract = selectedRows[index];
                const contractId = contract.id;
                if (result.data && result.data.productData) {
                    result.data.productData.forEach(item => {
                        allProductData.push({
                            ...item,
                            id: contractId, // æ˜Žç¡®è®¾ç½®åˆåŒID
                            purchaseLedgerNo: contract.purchaseContractNumber, // æ·»åŠ é‡‡è´­åˆåŒå·
                            supplierName: contract.supplierName, // æ·»åŠ ä¾›åº”å•†åç§°
                            projectName: contract.projectName // æ·»åŠ é¡¹ç›®åç§°
                        });
                    });
                }
            });
            // è®¾ç½®è¡¨å•数据(使用第一个合同的基本信息,采购合同号留空)
            form.purchaseLedgerNo = ""; // é‡‡è´­åˆåŒå·ç•™ç©ºï¼Œå› ä¸ºä¼šåœ¨äº§å“è¡¨æ ¼ä¸­åˆ†åˆ«æ˜¾ç¤º
            form.invoiceAmount = 0;
            form.invoiceNumber = "";
            form.entryDate = dayjs().format("YYYY-MM-DD");
            form.enterDate = dayjs().format("YYYY-MM-DD");
            form.salesContractNo = results[0].data.salesContractNo;
            form.projectName = results[0].data.projectName;
            form.supplierName = results[0].data.supplierName;
            // ä¿ç•™å½•入人信息
            form.issUerId = userStore.id;
            form.issUer = userStore.nickName;
            form.productData = allProductData;
            // å­˜å‚¨é€‰ä¸­çš„合同数据
            selectedContracts.value = selectedRows;
        });
    } else if (type == "edit") {
        const id = Array.isArray(selectedRows) ? selectedRows[0].id : selectedRows;
        const data = await getPurchaseById({ id, type: 2 });
        form.purchaseLedgerNo = data.purchaseContractNumber;
        form.invoiceAmount = data.invoiceAmount;
        form.invoiceNumber = data.invoiceNumber;
        form.salesContractNo = data.salesContractNo;
        form.projectName = data.projectName;
        form.supplierName = data.supplierName;
        form.entryDate = data.entryDate;
        form.productData = data.productData;
    }
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeChildrenTable = (param) => {
@@ -347,109 +452,186 @@
};
//本次来票数失焦操作
const invoiceNumBlur = (row) => {
  if (!row.ticketsNum || row.ticketsNum === "") {
    row.ticketsNum = 0;
  }
  if (Number(row.ticketsNum) > Number(row.tempFutureTickets)) {
    proxy.$modal.msgWarning("本次开票数不得大于未开票数");
    row.ticketsNum = 0;
    return;
  }
  // è®¡ç®—本次来票金额
  row.ticketsAmount = (row.ticketsNum * row.taxInclusiveUnitPrice).toFixed(2)
  // è®¡ç®—未来票数
  row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2)
  // è®¡ç®—未来票金额
  row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2)
  calculateinvoiceAmount();
    if (!row.ticketsNum || row.ticketsNum === "") {
        row.ticketsNum = 0;
    }
    if (Number(row.ticketsNum) > Number(row.tempFutureTickets)) {
        proxy.$modal.msgWarning("本次开票数不得大于未开票数");
        row.ticketsNum = 0;
        return;
    }
    // è®¡ç®—本次来票金额
    row.ticketsAmount = (row.ticketsNum * row.taxInclusiveUnitPrice).toFixed(2)
    // è®¡ç®—未来票数
    row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2)
    // è®¡ç®—未来票金额
    row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2)
    calculateinvoiceAmount();
};
// æœ¬æ¬¡æ¥ç¥¨é‡‘额失焦操作
const invoiceAmountBlur = (row) => {
  if (!row.ticketsAmount) {
    row.ticketsAmount = 0;
  }
  // è®¡ç®—是否超过来票总金额
  if (row.ticketsAmount > row.tempFutureTicketsAmount) {
    proxy.$modal.msgWarning("本次来票金额不得大于未来票金额");
    row.ticketsAmount = 0;
  }
  // è®¡ç®—本次来票数
  row.ticketsNum = Number(
    (row.ticketsAmount / row.taxInclusiveUnitPrice).toFixed(2)
  );
  // è®¡ç®—未来票数
  row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2)
  // è®¡ç®—未来票金额
  row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2)
  calculateinvoiceAmount();
    if (!row.ticketsAmount) {
        row.ticketsAmount = 0;
    }
    // è®¡ç®—是否超过来票总金额
    if (row.ticketsAmount > row.tempFutureTicketsAmount) {
        proxy.$modal.msgWarning("本次来票金额不得大于未来票金额");
        row.ticketsAmount = 0;
    }
    // è®¡ç®—本次来票数
    row.ticketsNum = Number(
        (row.ticketsAmount / row.taxInclusiveUnitPrice).toFixed(2)
    );
    // è®¡ç®—未来票数
    row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2)
    // è®¡ç®—未来票金额
    row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2)
    calculateinvoiceAmount();
};
const calculateinvoiceAmount = () => {
  let invoiceAmountTotal = 0;
  form.productData.forEach((item) => {
    if (item.ticketsAmount) {
      invoiceAmountTotal += Number(item.ticketsAmount);
    }
  });
  form.invoiceAmount = invoiceAmountTotal.toFixed(2);
    let invoiceAmountTotal = 0;
    form.productData.forEach((item) => {
        if (item.ticketsAmount) {
            invoiceAmountTotal += Number(item.ticketsAmount);
        }
    });
    form.invoiceAmount = invoiceAmountTotal.toFixed(2);
};
const open = (type, eid) => {
  openModal();
  getTableData(type, eid);
  id.value = eid;
const open = async (type, selectedRows) => {
    visible.value = true;
    // å¦‚果是批量操作,设置标题
    if (Array.isArray(selectedRows) && selectedRows.length > 1) {
        modalOptions.title = `批量新增 (${selectedRows.length}条)`;
    } else {
        modalOptions.title = type == "add" ? "新增" : "编辑";
    }
    // å¦‚果是单个操作,获取id
    if (!Array.isArray(selectedRows) || selectedRows.length === 1) {
        const idValue = Array.isArray(selectedRows) ? selectedRows[0].id : selectedRows;
        id.value = idValue;
    }
    await getTableData(type, selectedRows);
};
const uploadSuccess = (response) => {
  form.tempFileIds.push(response.data.tempId);
  console.log(form);
    form.tempFileIds.push(response.data.tempId);
    console.log(form);
};
const removeFile = (file) => {
  const { tempId } = file.response.data;
  form.tempFileIds = form.tempFileIds.filter((item) => item !== tempId);
    const { tempId } = file.response.data;
    form.tempFileIds = form.tempFileIds.filter((item) => item !== tempId);
};
const closeAndRefresh = () => {
  closeModal();
  emit('refreshList');
    closeModal();
    emit('refreshList');
};
const submitForm = () => {
  formRef.value.validate(async (valid, fields) => {
    if (valid) {
      // modalLoading.value = true;
      const { code } = await addOrUpdateRegistration({
        purchaseLedgerId: id.value,
        purchaseContractNumber: form.purchaseLedgerNo,
        invoiceNumber: form.invoiceNumber,
        invoiceAmount: form.invoiceAmount,
        salesContractNo: form.salesContractNo,
        projectName: form.projectName,
        productData: form.productData,
        issueDate: form.entryDate,
        issUerId: form.issUerId, // å½•入人id
        issUer: form.issUer, // å½•入人
        salesContractNoId: form.salesContractNoId,
        supplierName: form.supplierName,
        tempFileIds: form.tempFileIds,
        enterDate: form.enterDate,
        type: 4,
      });
      modalLoading.value = false;
      if (code == 200) {
        closeAndRefresh();
      }
    } else {
      modalLoading.value = false;
    }
  });
    proxy.$refs["formRef"].validate((valid) => {
        if (valid) {
            // å¦‚果是批量操作,将所有合同的数据放在一个数组里,只调用一次接口
            if (selectedContracts.value.length > 1) {
                // åˆ›å»ºåŒ…含所有合同数据的数组
                const batchData = selectedContracts.value.map(contract => {
                    // ç­›é€‰å‡ºå±žäºŽå½“前合同的产品数据
                    const contractProductData = form.productData.filter(item =>
                        item.id === contract.id
                    );
                    // ä¸ºæ¯ä¸ªé‡‡è´­åˆåŒåˆ›å»ºç‹¬ç«‹çš„对象
                    return {
                        // åŸºç¡€è¡¨å•数据
                        invoiceNumber: form.invoiceNumber,
                        invoiceAmount: form.invoiceAmount,
                        entryDate: form.entryDate,
                        enterDate: form.enterDate,
                        issUerId: form.issUerId, // å½•入人id
                        issUer: form.issUer, // å½•入人
                        tempFileIds: form.tempFileIds,
                        // åˆåŒå®žé™…信息
                        purchaseLedgerId: contract.id, // ä½¿ç”¨id作为字段名,值为purchaseLedgerId
                        purchaseContractNumber: contract.purchaseContractNumber, // ä½¿ç”¨å®žé™…的采购合同号
                        salesContractNo: contract.salesContractNo, // ä½¿ç”¨å®žé™…的销售合同号
                        supplierName: contract.supplierName, // ä½¿ç”¨å®žé™…的供应商名称
                        projectName: contract.projectName, // ä½¿ç”¨å®žé™…的项目名称
                        // äº§å“æ•°æ®
                        productData: proxy.HaveJson(contractProductData),
                        // æ‰¹é‡æ ‡è¯†
                        isBatch: true,
                        type: 4
                    };
                });
                // åªè°ƒç”¨ä¸€æ¬¡æŽ¥å£ï¼Œä¼ é€’包含所有合同数据的数组
                modalLoading.value = true;
                addOrUpdateRegistration(batchData).then((res) => {
                    modalLoading.value = false;
                    if (res.code === 200) {
                        proxy.$modal.msgSuccess("批量登记成功");
                        closeAndRefresh();
                    }
                }).catch(() => {
                    modalLoading.value = false;
                    proxy.$modal.msgError("批量登记失败");
                });
            } else {
                    // å•个合同提交逻辑 - ä»¥æ•°ç»„格式传递
                    const singleContract = selectedContracts.value[0];
                    const singleFormArray = [{
                        // åŸºç¡€è¡¨å•数据
                        invoiceNumber: form.invoiceNumber,
                        invoiceAmount: form.invoiceAmount,
                        entryDate: form.entryDate,
                        enterDate: form.enterDate,
                        issUerId: form.issUerId, // å½•入人id
                        issUer: form.issUer, // å½•入人
                        tempFileIds: form.tempFileIds,
                        // åˆåŒå®žé™…信息
                        purchaseLedgerId: singleContract.id, // ä½¿ç”¨id作为字段名,值为purchaseLedgerId
                        purchaseContractNumber: singleContract.purchaseContractNumber, // ä½¿ç”¨å®žé™…的采购合同号
                        salesContractNo: singleContract.salesContractNo, // ä½¿ç”¨å®žé™…的销售合同号
                        supplierName: singleContract.supplierName, // ä½¿ç”¨å®žé™…的供应商名称
                        projectName: singleContract.projectName, // ä½¿ç”¨å®žé™…的项目名称
                        // äº§å“æ•°æ®
                        productData: proxy.HaveJson(form.productData),
                        // æ‰¹é‡æ ‡è¯†
                        isBatch: false,
                        type: 4
                    }];
                    modalLoading.value = true;
                    addOrUpdateRegistration(singleFormArray).then((res) => {
                        modalLoading.value = false;
                        if (res.code === 200) {
                            proxy.$modal.msgSuccess("登记成功");
                            closeAndRefresh();
                        }
                    }).catch(() => {
                        modalLoading.value = false;
                        proxy.$modal.msgError("登记失败");
                    });
                }
        }
    });
};
defineExpose({
  open,
  closeAndRefresh,
    open,
    closeAndRefresh,
});
</script>
src/views/procurementManagement/invoiceEntry/index.vue
@@ -28,13 +28,6 @@
              clearable
          />
        </el-form-item>
        <el-form-item label="项目名称">
          <el-input
              v-model="filters.projectName"
              placeholder="请输入项目名称"
              clearable
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="getTableData"> æœç´¢ </el-button>
          <el-button @click="resetFilters"> é‡ç½® </el-button>
@@ -143,11 +136,6 @@
      width:300
    },
    {
      label: "项目名称",
      prop: "projectName",
      width:400
    },
    {
      label: "录入人",
      prop: "recorderName",
    },
@@ -198,11 +186,11 @@
};
const handleAdd = (type) => {
  if (selectedRows.value.length !== 1) {
    proxy.$modal.msgWarning("请先选中一条数据");
    return;
  }
  modalRef.value.open(type, selectedRows.value[0].id);
    if (selectedRows.value.length < 1) {
        proxy.$modal.msgWarning("请至少选中一条数据");
        return;
    }
    modalRef.value.open(type, selectedRows.value);
};
const handleEdit = (type, id) => {
src/views/procurementManagement/paymentEntry/index.vue
@@ -104,7 +104,6 @@
                                    size="small"
                                    @click="changeEditType(scope.row)"
                                    v-if="!scope.row.editType"
                                    :disabled="scope.row.registrant !== userStore.nickName"
                                >编辑</el-button
                                >
                                <el-button
@@ -113,7 +112,6 @@
                                    size="small"
                                    @click="saveReceiptPayment(scope.row)"
                                    v-if="scope.row.editType"
                                    :disabled="scope.row.registrant !== userStore.nickName"
                                >保存</el-button
                                >
                                <el-button
@@ -121,7 +119,6 @@
                                    type="primary"
                                    size="small"
                                    @click="handleDelete(scope.row)"
                                    :disabled="scope.row.registrant !== userStore.nickName"
                                >删除</el-button
                                >
                            </template>
@@ -243,7 +240,6 @@
                                v-model="form.registrant"
                                placeholder="请输入"
                                clearable
                                disabled
                            />
                        </el-form-item>
                    </el-col>
src/views/procurementManagement/paymentHistory/index.vue
@@ -118,9 +118,10 @@
const { form: searchForm } = useFormData({
  searchText: undefined,
    purchaseContractNumber: undefined,
  paymentDate: [],
  paymentDateStart: undefined,
  paymentDateEnd: undefined,
  // è®¾ç½®ä»˜æ¬¾æ—¥æœŸèŒƒå›´ä¸ºå½“天
  paymentDate: [dayjs().startOf('day').format('YYYY-MM-DD'), dayjs().endOf('day').format('YYYY-MM-DD')],
  paymentDateStart: dayjs().startOf('day').format('YYYY-MM-DD'),
  paymentDateEnd: dayjs().endOf('day').format('YYYY-MM-DD'),
});
// æŸ¥è¯¢åˆ—表
@@ -137,7 +138,8 @@
const getList = () => {
  tableLoading.value = true;
  const { paymentDate, ...rest } = searchForm;
  paymentHistoryListPage({ ...rest, ...page }).then((res) => {
  const { total, ...pageParams } = page;
  paymentHistoryListPage({ ...rest, ...pageParams }).then((res) => {
    tableLoading.value = false;
    tableData.value = res.records;
        page.total = res.total;
src/views/procurementManagement/paymentLedger/index.vue
@@ -86,8 +86,7 @@
            :tableLoading="tableLoadingSon"
            :isShowSummary="isShowSummarySon"
            :summaryMethod="summarizeMainTable1"
            :isShowPage="false"
            height="calc(100vh - 18.5em)"
                        height="calc(100vh - 18.5em)"
          >
            <template #payableAmountSlot="{ row }">
              <el-text type="danger">
@@ -95,14 +94,14 @@
              </el-text>
            </template>
          </PIMTable>
<!--          <pagination-->
<!--            v-show="sonTotal > 0"-->
<!--            :total="sonTotal"-->
<!--            @pagination="sonPaginationSearch"-->
<!--            :layout="page.layout"-->
<!--            :page="sonPage.current"-->
<!--            :limit="sonPage.size"-->
<!--          />-->
          <pagination
            v-show="sonTotal > 0"
            :total="sonTotal"
            @pagination="sonPaginationSearch"
            :layout="page.layout"
            :page="sonPage.current"
            :limit="sonPage.size"
          />
        </div>
      </el-col>
    </el-row>
@@ -246,6 +245,8 @@
  paymentLedgerList({
    ...searchForm.value,
    ...page,
    detailPageNum: detailPageNum.value, // æ–°å¢ž
    detailPageSize: detailPageSize.value, // æ–°å¢ž
  }).then((res) => {
    let result = res.data;
    tableLoading.value = false;
src/views/procurementManagement/procurementInvoiceLedger/Form/EditForm.vue
@@ -1,48 +1,48 @@
<template>
    <el-form :model="form">
        <el-row :gutter="20">
            <el-col :span="12">
                <el-form-item label="采购合同号:">
                    <el-tag size="large">{{ form.purchaseContractNumber }}</el-tag>
                </el-form-item>
            </el-col>
            <el-col :span="12">
                <el-form-item label="销售合同号:">
                    <el-text>{{ form.salesContractNo }}</el-text>
                </el-form-item>
            </el-col>
            <el-col :span="12">
                <el-form-item label="含税单价(元):">
                    <el-text type="primary">{{ form.taxInclusiveUnitPrice }}</el-text>
                </el-form-item>
            </el-col>
            <el-col :span="12">
                <el-form-item label="创建时间:">
                    <el-text>{{ form.createdAt }}</el-text>
                </el-form-item>
            </el-col>
            <el-col :span="12">
                <el-form-item label="发票号:">
                    <el-input v-model="form.invoiceNumber" />
                </el-form-item>
            </el-col>
            <el-col :span="12">
                <el-form-item label="来票数:">
                    <el-input-number :step="0.1" :min="0" style="width: 100%" v-model="form.ticketsNum" @change="inputTicketsNum" :precision="2"/>
                </el-form-item>
            </el-col>
            <el-col :span="12">
                <el-form-item label="本次来票金额(元):">
  <el-form :model="form">
    <el-row :gutter="20">
      <el-col :span="12">
        <el-form-item label="采购合同号:">
          <el-tag size="large">{{ form.purchaseContractNumber }}</el-tag>
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="销售合同号:">
          <el-text>{{ form.salesContractNo }}</el-text>
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="含税单价(元):">
          <el-text type="primary">{{ form.taxInclusiveUnitPrice }}</el-text>
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="创建时间:">
          <el-text>{{ form.createdAt }}</el-text>
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="发票号:">
          <el-input v-model="form.invoiceNumber" />
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="来票数:">
          <el-input-number :step="0.1" :min="0" style="width: 100%" v-model="form.ticketsNum" @change="inputTicketsNum" :precision="2"/>
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="本次来票金额(元):">
                    <el-input-number :step="0.1" :min="0" style="width: 100%" v-model="form.ticketsAmount" @change="inputTicketsAmount" :precision="2"/>
                </el-form-item>
            </el-col>
            <el-col :span="12">
                <el-form-item label="未来票数:">
                    <el-text type="success">{{ form.futureTickets }}</el-text>
                </el-form-item>
            </el-col>
        </el-row>
    </el-form>
        </el-form-item>
      </el-col>
      <el-col :span="12">
        <el-form-item label="未来票数:">
          <el-text type="success">{{ form.futureTickets }}</el-text>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>
<script setup>
@@ -51,43 +51,36 @@
const { proxy } = getCurrentInstance()
defineOptions({
    name: "来票台账表单",
  name: "来票台账表单",
});
const temFutureTickets = ref(0) // åˆå§‹æœªæ¥ç¥¨æ•°
const initialTicketsNum = ref(0) // åˆå§‹æ¥ç¥¨æ•°
const initialTicketsAmount = ref(0) // åˆå§‹æ¥ç¥¨é‡‘额
const quantity = ref(0) // æ€»æ•°é‡
const temFutureTickets = ref(0)
const { form, resetForm } = useFormData({
    id: undefined,
    purchaseContractNumber: undefined, // é‡‡è´­åˆåŒå·
    salesContractNo: undefined, // é”€å”®åˆåŒå·
    createdAt: undefined, // åˆ›å»ºæ—¶é—´
    invoiceNumber: undefined, // å‘票号
    ticketsNum: undefined, // æ¥ç¥¨æ•°
    ticketsAmount: undefined, // æ¥ç¥¨é‡‘额
  id: undefined,
  purchaseContractNumber: undefined, // é‡‡è´­åˆåŒå·
  salesContractNo: undefined, // é”€å”®åˆåŒå·
  createdAt: undefined, // åˆ›å»ºæ—¶é—´
  invoiceNumber: undefined, // å‘票号
  ticketsNum: undefined, // æ¥ç¥¨æ•°
  ticketsAmount: undefined, // æ¥ç¥¨é‡‘额
    taxInclusiveUnitPrice: undefined, // å«ç¨Žå•ä»·
    ticketRegistrationId: undefined, // å«ç¨Žå•ä»·
});
const load = async (id) => {
    const { code, data } = await getProductRecordById({ id });
    if (code === 200) {
        form.id = data.id;
        form.purchaseContractNumber = data.purchaseContractNumber;
        form.salesContractNo = data.salesContractNo;
        form.createdAt = data.createdAt;
        form.invoiceNumber = data.invoiceNumber;
        form.ticketsNum = data.ticketsNum;
        form.ticketsAmount = data.ticketsAmount ? Number(data.ticketsAmount).toFixed(2) : 0;
        form.taxInclusiveUnitPrice = data.taxInclusiveUnitPrice;
        form.futureTickets = data.futureTickets;
        temFutureTickets.value = data.futureTickets;
        initialTicketsNum.value = data.ticketsNum || 0;
        initialTicketsAmount.value = data.ticketsAmount || 0;
  const { code, data } = await getProductRecordById({ id });
  if (code === 200) {
    form.id = data.id;
    form.purchaseContractNumber = data.purchaseContractNumber;
    form.salesContractNo = data.salesContractNo;
    form.createdAt = data.createdAt;
    form.invoiceNumber = data.invoiceNumber;
    form.ticketsNum = data.ticketsNum;
    form.ticketsAmount = data.ticketsAmount.toFixed(2);
    form.taxInclusiveUnitPrice = data.taxInclusiveUnitPrice;
    form.futureTickets = data.futureTickets;
    temFutureTickets.value = data.futureTickets;
        form.ticketRegistrationId = data.ticketRegistrationId;
        // èŽ·å–æ€»æ•°é‡ï¼Œå¦‚æžœæ•°æ®ä¸­æœ‰ quantity å­—段则使用,否则使用来票数+未来票数
        quantity.value = data.quantity || (Number(data.ticketsNum || 0) + Number(data.futureTickets || 0));
    }
  }
};
const inputTicketsNum = (val) => {
@@ -96,44 +89,15 @@
        proxy.$modal.msgWarning("含税单价不能为零或未定义");
        return;
    }
    const newTicketsNum = Number(form.ticketsNum) || 0;
    const currentTicketsNum = Number(initialTicketsNum.value) || 0;
    // è®¡ç®—新增的来票数
    const addedTicketsNum = newTicketsNum - currentTicketsNum;
    // è®¡ç®—新的未来票数 = åˆå§‹æœªæ¥ç¥¨æ•° - æ–°å¢žçš„æ¥ç¥¨æ•°
    const newFutureTickets = Number(temFutureTickets.value) - addedTicketsNum;
    // éªŒè¯ï¼šæ–°çš„æ¥ç¥¨æ•° + æ–°çš„æœªæ¥ç¥¨æ•° â‰¤ quantity
    if (newTicketsNum + newFutureTickets > Number(quantity.value)) {
        proxy.$modal.msgWarning(`来票数+未来票数不能大于总数量(${quantity.value})`);
        // é™åˆ¶æ¥ç¥¨æ•°ï¼Œä½¿å…¶æ»¡è¶³ï¼šæ¥ç¥¨æ•° + æœªæ¥ç¥¨æ•° â‰¤ quantity
        // æœ€å¤§æ¥ç¥¨æ•° = quantity - åˆå§‹æœªæ¥ç¥¨æ•° + åˆå§‹æ¥ç¥¨æ•°
        const maxTicketsNum = Number(quantity.value) - Number(temFutureTickets.value) + Number(initialTicketsNum.value);
        form.ticketsNum = Math.max(0, Math.min(maxTicketsNum, newTicketsNum));
        // é‡æ–°è®¡ç®—
        const recalculatedAddedTicketsNum = Number(form.ticketsNum) - Number(initialTicketsNum.value);
        const recalculatedFutureTickets = Number(temFutureTickets.value) - recalculatedAddedTicketsNum;
        form.futureTickets = Number(recalculatedFutureTickets.toFixed(2));
        const ticketsAmount = Number(form.ticketsNum) * Number(form.taxInclusiveUnitPrice);
        form.ticketsAmount = Number(ticketsAmount.toFixed(2));
        return;
    }
    // æ£€æŸ¥æ–°å¢žçš„æ¥ç¥¨æ•°æ˜¯å¦å¤§äºŽåˆå§‹æœªæ¥ç¥¨æ•°
    if (addedTicketsNum > Number(temFutureTickets.value)) {
        proxy.$modal.msgWarning("新增开票数不得大于未开票数");
        form.ticketsNum = Number(initialTicketsNum.value) + Number(temFutureTickets.value);
    if (Number(form.ticketsNum) > Number(temFutureTickets.value)) {
        proxy.$modal.msgWarning("开票数不得大于未开票数");
        form.ticketsNum = temFutureTickets.value
    }
    
    // ç¡®ä¿æ‰€æœ‰æ•°å€¼éƒ½è½¬æ¢ä¸ºæ•°å­—类型进行计算
    const finalTicketsNum = Number(form.ticketsNum) || 0;
    const finalAddedTicketsNum = finalTicketsNum - Number(initialTicketsNum.value);
    const finalFutureTickets = Number(temFutureTickets.value) - finalAddedTicketsNum;
    const ticketsAmount = finalTicketsNum * Number(form.taxInclusiveUnitPrice);
    form.futureTickets = Number(finalFutureTickets.toFixed(2));
    const ticketsAmount = Number(form.ticketsNum) * Number(form.taxInclusiveUnitPrice);
    const futureTickets = Number(temFutureTickets.value) - Number(form.ticketsNum);
    form.futureTickets = Number(futureTickets.toFixed(2));
    form.ticketsAmount = Number(ticketsAmount.toFixed(2));
};
const inputTicketsAmount = (val) => {
@@ -143,53 +107,23 @@
        return;
    }
    
    const newTicketsAmount = Number(val) || 0;
    // è®¡ç®—新的来票数
    const newTicketsNum = newTicketsAmount / Number(form.taxInclusiveUnitPrice);
    const currentTicketsNum = Number(initialTicketsNum.value) || 0;
    // è®¡ç®—新增的来票数
    const addedTicketsNum = newTicketsNum - currentTicketsNum;
    // è®¡ç®—新的未来票数 = åˆå§‹æœªæ¥ç¥¨æ•° - æ–°å¢žçš„æ¥ç¥¨æ•°
    const newFutureTickets = Number(temFutureTickets.value) - addedTicketsNum;
    // éªŒè¯ï¼šæ–°çš„æ¥ç¥¨æ•° + æ–°çš„æœªæ¥ç¥¨æ•° â‰¤ quantity
    if (newTicketsNum + newFutureTickets > Number(quantity.value)) {
        proxy.$modal.msgWarning(`来票数+未来票数不能大于总数量(${quantity.value})`);
        // é™åˆ¶æ¥ç¥¨æ•°ï¼Œä½¿å…¶æ»¡è¶³ï¼šæ¥ç¥¨æ•° + æœªæ¥ç¥¨æ•° â‰¤ quantity
        const maxTicketsNum = Number(quantity.value) - Number(temFutureTickets.value) + Number(initialTicketsNum.value);
        form.ticketsNum = Math.max(0, Math.min(maxTicketsNum, newTicketsNum));
        form.ticketsAmount = Number((form.ticketsNum * Number(form.taxInclusiveUnitPrice)).toFixed(2));
        const recalculatedAddedTicketsNum = Number(form.ticketsNum) - Number(initialTicketsNum.value);
        const recalculatedFutureTickets = Number(temFutureTickets.value) - recalculatedAddedTicketsNum;
        form.futureTickets = Number(recalculatedFutureTickets.toFixed(2));
        return;
    }
    // æ£€æŸ¥æ–°å¢žçš„æ¥ç¥¨é‡‘额是否大于初始未来票数对应的金额
    const maxAddedAmount = Number(temFutureTickets.value * form.taxInclusiveUnitPrice);
    if (addedTicketsNum > 0 && addedTicketsNum * Number(form.taxInclusiveUnitPrice) > maxAddedAmount) {
        proxy.$modal.msgWarning("新增来票金额不得大于未开票金额");
        form.ticketsAmount = Number((initialTicketsAmount.value + maxAddedAmount).toFixed(2));
        form.ticketsNum = Number((currentTicketsNum + Number(temFutureTickets.value)).toFixed(2));
        form.futureTickets = 0;
    if (Number(val) > Number(form.futureTickets*form.taxInclusiveUnitPrice)) {
        proxy.$modal.msgWarning("本次来票金额不得大于总金额");
        form.ticketsAmount = (form.futureTickets*form.taxInclusiveUnitPrice).toFixed(2)
        const ticketsNum = Number(form.ticketsAmount) / Number(form.taxInclusiveUnitPrice);
        form.ticketsNum = Number(ticketsNum.toFixed(2))
        return;
    }
    
    // ç¡®ä¿æ‰€æœ‰æ•°å€¼éƒ½è½¬æ¢ä¸ºæ•°å­—类型进行计算
    const finalTicketsNum = Number(newTicketsNum.toFixed(2));
    const finalAddedTicketsNum = finalTicketsNum - Number(initialTicketsNum.value);
    const finalFutureTickets = Number(temFutureTickets.value) - finalAddedTicketsNum;
    form.ticketsNum = finalTicketsNum;
    form.futureTickets = Number(finalFutureTickets.toFixed(2));
    const ticketsNum = Number(val) / Number(form.taxInclusiveUnitPrice);
    form.ticketsNum = Number(ticketsNum.toFixed(2));
};
defineExpose({
    load,
    form,
    resetForm,
  load,
  form,
  resetForm,
});
</script>
src/views/procurementManagement/procurementInvoiceLedger/fileList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
<template>
  <el-dialog v-model="dialogVisible" title="附件" width="50%" :before-close="handleClose">
    <el-table :data="tableData" border height="40vh">
      <el-table-column label="附件名称" prop="name" min-width="400" show-overflow-tooltip />
      <el-table-column fixed="right" label="操作" width="200" align="center">
        <template #default="scope">
          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">下载</el-button>
          <el-button link type="primary" size="small" @click="lookFile(scope.row)">预览</el-button>
          <el-button link type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-dialog>
  <filePreview ref="filePreviewRef" />
  <UploadModal ref="uploadModalRef" @uploadSuccess="handleUploadSuccess" />
</template>
<script setup>
import { ref } from 'vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import filePreview from '@/components/filePreview/index.vue'
import UploadModal from './Modal/UploadModal.vue'
import { delCommonFileInvoiceLedger} from '@/api/publicApi/commonFile.js'
const dialogVisible = ref(false)
const tableData = ref([])
const currentId = ref('')
const { proxy } = getCurrentInstance();
const filePreviewRef = ref()
const uploadModalRef = ref()
const handleClose = () => {
  dialogVisible.value = false
}
const open = (list, id = '') => {
  dialogVisible.value = true
  tableData.value = list
  currentId.value = id
}
const handleUpload = () => {
  if (!currentId.value) {
    ElMessage.warning('无法获取当前记录ID,请关闭后重新打开附件窗口')
    return
  }
  uploadModalRef.value.handleImport(currentId.value)
}
const handleUploadSuccess = (data) => {
  ElMessage.success('上传成功')
  // è¿™é‡Œå¯ä»¥æ·»åŠ åˆ·æ–°é™„ä»¶åˆ—è¡¨çš„é€»è¾‘
  // æš‚时先关闭上传模态框
}
const downLoadFile = (row) => {
  proxy.$download.name(row.url);
}
const lookFile = (row) => {
  filePreviewRef.value.open(row.url)
}
// åˆ é™¤é™„ä»¶
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除附件"${row.name}"吗?`, '删除确认', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }).then(() => {
        delCommonFileInvoiceLedger([row.id]).then(() => {
      ElMessage.success('删除成功')
      // ä»Žåˆ—表中移除已删除的附件
      const index = tableData.value.findIndex(item => item.id === row.id)
      if (index !== -1) {
        tableData.value.splice(index, 1)
      }
    }).catch(() => {
      ElMessage.error('删除失败')
    })
  }).catch(() => {
    proxy.$modal.msg('已取消删除')
  })
}
defineExpose({
  open
})
</script>
<style></style>
src/views/procurementManagement/procurementInvoiceLedger/index.vue
@@ -63,13 +63,6 @@
            <el-button link :icon="Files" type="danger"> é™„ä»¶ </el-button>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item
                  v-if="row.commonFiles.length !== 0"
                  :icon="Download"
                  command="download"
                >
                  ä¸‹è½½
                </el-dropdown-item>
                <el-dropdown-item :icon="Upload" command="upload">
                  ä¸Šä¼ 
                </el-dropdown-item>
@@ -80,16 +73,15 @@
        <template #operation="{ row }">
          <el-button
            type="primary"
            text
                        link
            @click="openEdit(row)"
                        :disabled="row.issUerId !== userStore.id"
          >
            ç¼–辑
          </el-button>
                    <el-button link type="primary" size="small" @click="downLoadFile(row)">附件</el-button>
          <el-button
            type="primary"
            text
                        :disabled="row.issUerId !== userStore.id"
                        link
            @click="handleDelete(row)"
          >
            åˆ é™¤
@@ -97,6 +89,7 @@
        </template>
      </PIMTable>
    </div>
        <FileList ref="fileListRef" />
    <UploadModal ref="modalRef" @uploadSuccess="uploadSuccess"></UploadModal>
    <EditModal ref="editmodalRef" @success="getTableData"></EditModal>
  </div>
@@ -111,19 +104,21 @@
  Search,
  Upload,
  EditPen,
  Delete,
} from "@element-plus/icons-vue";
import {
    delRegistration,
    productRecordPage,
    productUploadFile,
} from "@/api/procurementManagement/procurementInvoiceLedger.js";
import { delCommonFile } from "@/api/publicApi/commonFile.js";
import { onMounted } from "vue";
import { ElMessageBox } from "element-plus";
import { ElMessageBox, ElMessage } from "element-plus";
import UploadModal from "./Modal/UploadModal.vue";
import EditModal from "./Modal/EditModal.vue";
import useUserStore from "@/store/modules/user.js";
import {delInvoiceLedgerByRegProductId} from "@/api/salesManagement/invoiceLedger.js";
const userStore = useUserStore();
import dayjs from "dayjs";
import FileList from "./fileList.vue";
defineOptions({
  name: "来票台账",
@@ -148,7 +143,8 @@
  {
    purchaseContractNumber: undefined, // é‡‡è´­åˆåŒå·
    supplierName: undefined, // ä¾›åº”商
    createdAt: [], // æ¥ç¥¨æ—¥æœŸ
    // è®¾ç½®æ¥ç¥¨æ—¥æœŸèŒƒå›´ä¸ºå½“天
    createdAt: [dayjs().startOf('day').format('YYYY-MM-DD'), dayjs().endOf('day').format('YYYY-MM-DD')], // æ¥ç¥¨æ—¥æœŸ
  },
  [
    {
@@ -160,11 +156,6 @@
      label: "销售合同号",
      prop: "salesContractNo",
      width: 150,
    },
    {
      label: "项目名称",
      prop: "projectName",
      width: 240,
    },
    {
      label: "供应商名称",
@@ -231,7 +222,7 @@
    },
    {
      fixed: "right",
      width: 150,
      width: 190,
      label: "操作",
      dataType: "slot",
      slot: "operation",
@@ -267,7 +258,12 @@
const handleSelectionChange = (val) => {
  multipleVal.value = val;
};
//附件相关
const fileListRef = ref(null)
//查看附件
const downLoadFile = (row) => {
    fileListRef.value.open(row.commonFiles, row.id)
}
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
@@ -304,6 +300,29 @@
      console.log(row.commonFiles);
      openUoload(row.ticketRegistrationId);
      break;
    case "delete":
      // åˆ é™¤æ‰€æœ‰é™„ä»¶
      if (row.commonFiles.length > 0) {
        ElMessageBox.confirm(`确认删除该记录的所有附件吗?`, '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          // èŽ·å–æ‰€æœ‰é™„ä»¶çš„ID
          const fileIds = row.commonFiles.map(file => file.id);
          delCommonFile(fileIds).then(() => {
            ElMessage.success('删除成功')
            // åˆ·æ–°æ•°æ®
            getTableData();
          }).catch(() => {
            ElMessage.error('删除失败')
          })
        }).catch(() => {
          ElMessage.info('已取消删除')
        })
      }
      break;
  }
};
src/views/procurementManagement/procurementLedger/index.vue
@@ -21,10 +21,6 @@
            <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
                      @change="handleQuery" />
          </el-form-item>
          <el-form-item label="项目名称:">
            <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search"
                      @change="handleQuery" />
          </el-form-item>
          <el-form-item label="录入日期:">
            <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                            placeholder="请选择" clearable @change="changeDaterange" />
@@ -39,7 +35,7 @@
    <div class="table_list">
      <div style="display: flex;justify-content: flex-end;margin-bottom: 20px;">
        <el-button type="primary" @click="openForm('add')">新增台账</el-button>
        <!-- <el-button type="success" @click="openScanAddDialog">扫码新增</el-button> -->
        <el-button type="success" @click="openScanAddDialog">扫码新增</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
@@ -110,12 +106,12 @@
          prop="supplierName"
          show-overflow-tooltip
        />
        <el-table-column
          label="项目名称"
          prop="projectName"
          width="420"
          show-overflow-tooltip
        />
                <el-table-column
                    label="签订日期"
                    prop="executionDate"
                    width="100"
                    show-overflow-tooltip
                />
        <el-table-column
          label="付款方式"
          width="100"
@@ -132,7 +128,7 @@
        <el-table-column
          label="录入人"
          prop="recorderName"
           width="100"
           width="120"
          show-overflow-tooltip
        />
        <el-table-column
@@ -144,7 +140,7 @@
        <el-table-column
          fixed="right"
          label="操作"
          min-width="100"
          width="180"
          align="center"
        >
          <template #default="scope">
@@ -154,6 +150,13 @@
              size="small"
              @click="openForm('edit', scope.row)"
              >编辑</el-button
            >
            <el-button
              link
              type="success"
              size="small"
              @click="showQRCode(scope.row)"
              >生成二维码</el-button
            >
            <el-button
              link
@@ -205,7 +208,6 @@
                placeholder="请选择"
                                filterable
                clearable
                @change="salesLedgerChange"
              >
                <el-option
                  v-for="item in salesContractList"
@@ -235,17 +237,6 @@
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="项目名称:" prop="projectName">
              <el-input
                v-model="form.projectName"
                placeholder="请输入"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="付款方式">
                            <el-input
@@ -255,6 +246,8 @@
                            />
                        </el-form-item>
                    </el-col>
        </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="签订日期:" prop="executionDate">
                            <el-date-picker
@@ -276,8 +269,9 @@
                v-model="form.recorderId"
                placeholder="请选择"
                clearable
                disabled
                                filterable
                filterable
                default-first-option
                :reserve-keyword="false"
              >
                <el-option
                  v-for="item in userList"
@@ -637,15 +631,6 @@
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="项目名称:" prop="projectName">
              <el-input
                v-model="scanAddForm.projectName"
                placeholder="请输入"
                clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="合同金额(元):" prop="contractAmount">
              <el-input-number
                v-model="scanAddForm.contractAmount"
@@ -723,11 +708,6 @@
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="项目名称:">
              <el-input v-model="scanForm.projectName" disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="扫码时间:">
              <el-input v-model="scanForm.scanTime" disabled />
@@ -853,7 +833,6 @@
    supplierName: "", // ä¾›åº”商名称
    purchaseContractNumber: "", // é‡‡è´­åˆåŒç¼–号
    salesContractNo: "", // é”€å”®åˆåŒç¼–号
    projectName: "", // é¡¹ç›®åç§°
    entryDate: null, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
@@ -861,7 +840,6 @@
  form: {
    purchaseContractNumber: "",
    salesLedgerId: "",
    projectName: "",
    recorderId: "",
    entryDate: "",
    productData: [],
@@ -874,14 +852,19 @@
    purchaseContractNumber: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    projectName: [{ required: true, message: "请输入", trigger: "blur" }],
    supplierId: [{ required: true, message: "请输入", trigger: "blur" }],
        entryDate: [{ required: true, message: "请选择", trigger: "change" }],
        executionDate: [{ required: true, message: "请选择", trigger: "change" }],
  },
});
const {  form, rules } = toRefs(data);
const { form: searchForm } = useFormData(data.searchForm);
const { form: searchForm } = useFormData({
  ...data.searchForm,
  // è®¾ç½®å½•入日期范围为当天
  entryDate: [dayjs().startOf('day').format('YYYY-MM-DD'), dayjs().endOf('day').format('YYYY-MM-DD')],
  entryDateStart: dayjs().startOf('day').format('YYYY-MM-DD'),
  entryDateEnd: dayjs().endOf('day').format('YYYY-MM-DD')
});
// äº§å“è¡¨å•弹框数据
const productFormVisible = ref(false);
@@ -912,7 +895,7 @@
      { required: true, message: "请输入", trigger: "blur" },
    ],
    taxRate: [{ required: true, message: "请选择", trigger: "change" }],
        warnNum: [{ required: false, message: "请选择", trigger: "change" }],
        warnNum: [{ required: true, message: "请选择", trigger: "change" }],
    taxInclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
@@ -1123,6 +1106,12 @@
      }
      form.value.tempFileIds = tempFileIds;
      form.value.type = 2;
      // å¦‚æžœsalesLedgerId为空,则不传递salesContractNo
      if (!form.value.salesLedgerId) {
        form.value.salesContractNo = ''
      }
      addOrEditPurchase(form.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
@@ -1156,7 +1145,6 @@
const getModels = (value) => {
  if (value) {
    productForm.value.productCategory = findNodeById(productOptions.value, value) || "";
    productForm.value.productId = value;
    modelList({ id: value }).then((res) => {
      modelOptions.value = res;
    });
@@ -1240,13 +1228,6 @@
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  // ä¿è¯è‡³å°‘保留一条产品信息
  const remainingCount =
    productData.value.length - productSelectedRows.value.length;
  if (remainingCount < 1) {
    proxy.$modal.msgWarning("至少保留一条产品信息,无法全部删除");
    return;
  }
  if (operationType.value === "add") {
    productSelectedRows.value.forEach((selectedRow) => {
      const index = productData.value.findIndex(
@@ -1270,7 +1251,7 @@
        delProduct(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          closeProductDia();
                    getPurchaseById({ id: currentId.value, type: 2 }).then(
          getSalesLedgerWithProducts({ id: currentId.value, type: 2 }).then(
            (res) => {
              productData.value = res.productData;
            }
@@ -1408,7 +1389,6 @@
  var index = salesContractList.value.findIndex((item) => item.id == row);
  console.log("index", index);
  if (index > -1) {
    form.value.projectName = salesContractList.value[index].projectName;
    await querygProductInfoByContractNo();
  }
};
@@ -1474,7 +1454,6 @@
  scanContent: "",
  purchaseContractNumber: "",
  supplierName: "",
  projectName: "",
  contractAmount: "",
  paymentMethod: "",
  recorderName: "",
@@ -1483,7 +1462,6 @@
const scanAddRules = {
  purchaseContractNumber: [{ required: true, message: "请输入采购合同号", trigger: "blur" }],
  supplierName: [{ required: true, message: "请输入供应商名称", trigger: "blur" }],
  projectName: [{ required: true, message: "请输入项目名称", trigger: "blur" }],
};
// æ‰«ç ç™»è®°å¯¹è¯æ¡†ç›¸å…³å˜é‡
@@ -1491,7 +1469,6 @@
const scanForm = reactive({
  purchaseContractNumber: "",
  supplierName: "",
  projectName: "",
  scanTime: "",
  scannerName: "",
  scanStatus: "未扫码",
@@ -1507,7 +1484,6 @@
  scanAddForm.scanContent = "";
  scanAddForm.purchaseContractNumber = "";
  scanAddForm.supplierName = "";
  scanAddForm.projectName = "";
  scanAddForm.contractAmount = "";
  scanAddForm.paymentMethod = "";
  scanAddForm.recorderName = userStore.nickName;
@@ -1520,14 +1496,13 @@
  if (!content) return;
  
  // æ¨¡æ‹Ÿè§£æžäºŒç»´ç å†…容,这里可以根据实际需求调整解析逻辑
  // å‡è®¾æ‰«ç å†…容格式为:合同号|供应商|项目|金额|付款方式
  // å‡è®¾æ‰«ç å†…容格式为:合同号|供应商|金额|付款方式
  const parts = content.split('|');
  if (parts.length >= 3) {
  if (parts.length >= 2) {
    scanAddForm.purchaseContractNumber = parts[0] || "";
    scanAddForm.supplierName = parts[1] || "";
    scanAddForm.projectName = parts[2] || "";
    scanAddForm.contractAmount = parts[3] || "";
    scanAddForm.paymentMethod = parts[4] || "";
    scanAddForm.contractAmount = parts[2] || "";
    scanAddForm.paymentMethod = parts[3] || "";
  }
};
@@ -1545,7 +1520,6 @@
      const newData = {
        purchaseContractNumber: scanAddForm.purchaseContractNumber,
        supplierName: scanAddForm.supplierName,
        projectName: scanAddForm.projectName,
        contractAmount: scanAddForm.contractAmount,
        paymentMethod: scanAddForm.paymentMethod,
        recorderName: scanAddForm.recorderName,
@@ -1568,7 +1542,6 @@
const openScanDialog = (row) => {
  scanForm.purchaseContractNumber = row.purchaseContractNumber;
  scanForm.supplierName = row.supplierName;
  scanForm.projectName = row.projectName;
  scanForm.scanTime = getCurrentDateTime();
  scanForm.scannerName = userStore.nickName;
  scanForm.scanStatus = "未扫码";
@@ -1620,4 +1593,4 @@
});
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss"></style>
src/views/procurementManagement/procurementReport/index.vue
@@ -280,6 +280,7 @@
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Document, List, TrendCharts, Shop, Search, Refresh, Download } from '@element-plus/icons-vue'
import dayjs from "dayjs";
// å“åº”式数据
const loading = ref(false)
@@ -690,14 +691,10 @@
}
onMounted(() => {
  // è®¾ç½®é»˜è®¤æ—¶é—´èŒƒå›´ä¸ºæœ€è¿‘30天
  const endDate = new Date()
  const startDate = new Date()
  startDate.setDate(startDate.getDate() - 30)
  // è®¾ç½®é»˜è®¤æ—¶é—´èŒƒå›´ä¸ºå½“天
  searchForm.dateRange = [
    startDate.toISOString().split('T')[0],
    endDate.toISOString().split('T')[0]
    dayjs().startOf('day').format('YYYY-MM-DD'),
    dayjs().endOf('day').format('YYYY-MM-DD')
  ]
})
</script>
src/views/productionManagement/operationScheduling/components/formDia.vue
@@ -8,20 +8,47 @@
    >
      <el-button type="primary" @click="addRow" style="margin-bottom: 10px;">新增</el-button>
            <span style="font-size: 18px;margin-left: 10px">待排产数量:{{pendingNum}}</span>
<!--            <div style="margin-bottom: 10px; margin-left: 10px;">-->
<!--                <el-form-item label="领用:" style="margin-bottom: 0;">-->
<!--                    <el-input v-model="receive" placeholder="请输入领用" style="width: 200px;" />-->
<!--                </el-form-item>-->
<!--            </div>-->
      <el-table :data="tableData" border style="width: 100%" :summary-method="summarizeMainTable" show-summary :row-key="row => row.id">
        <el-table-column label="序号" width="60">
        <el-table-column label="序号" width="60" align="center">
          <template #default="scope">
            {{ scope.$index + 1 }}
          </template>
        </el-table-column>
        <el-table-column label="工序" prop="process">
        <el-table-column label="工序" prop="process" width="150">
          <template #default="scope">
                        <el-input v-model="scope.row.process" placeholder="请输入工序" />
          </template>
        </el-table-column>
        <el-table-column label="单位" prop="unit">
        <el-table-column label="产线" prop="productionLine" width="150">
          <template #default="scope">
            <el-select
              v-model="scope.row.productionLine"
              placeholder="选择产线"
              style="width: 100%;"
              clearable
            >
              <el-option
                v-for="line in productionLines"
                :key="line.value"
                :label="line.label"
                :value="line.value"
              />
            </el-select>
          </template>
        </el-table-column>
        <el-table-column label="单位" prop="unit" width="90">
          <template #default="scope">
            <el-input v-model="scope.row.unit" placeholder="请输入单位" />
          </template>
        </el-table-column>
        <el-table-column label="口味/品名/规格" prop="type" width="150">
          <template #default="scope">
            <el-input v-model="scope.row.type" placeholder="请输入" />
          </template>
        </el-table-column>
        <el-table-column label="排产数量" width="200" prop="schedulingNum">
@@ -50,17 +77,20 @@
                        />
          </template>
        </el-table-column>
        <el-table-column label="排产日期" prop="schedulingDate">
        <el-table-column label="排产日期" prop="schedulingDate" width="200">
          <template #default="scope">
            <el-date-picker v-model="scope.row.schedulingDate" type="date" placeholder="选择日期" style="width: 100%;" value-format="YYYY-MM-DD" format="YYYY-MM-DD"/>
          </template>
        </el-table-column>
        <el-table-column label="排产人" prop="schedulingUserId">
        <el-table-column label="排产人" prop="schedulingUserId" width="150">
          <template #default="scope">
                        <el-select
                            v-model="scope.row.schedulingUserId"
                            placeholder="选择人员"
                            style="width: 100%;"
              filterable
              default-first-option
              :reserve-keyword="false"
                        >
                            <el-option
                                v-for="user in userList"
@@ -69,6 +99,11 @@
                                :value="user.userId"
                            />
                        </el-select>
          </template>
        </el-table-column>
        <el-table-column label="备注" prop="remark" width="200">
          <template #default="scope">
            <el-input v-model="scope.row.remark" placeholder="请输入备注" />
          </template>
        </el-table-column>
        <el-table-column label="操作" width="80">
@@ -88,7 +123,7 @@
</template>
<script setup>
import {ref} from "vue";
import {ref, getCurrentInstance} from "vue";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import {processScheduling} from "@/api/productionManagement/operationScheduling.js";
const { proxy } = getCurrentInstance()
@@ -97,33 +132,56 @@
const dialogFormVisible = ref(false);
const operationType = ref('')
const tableData = ref([
    { process: '', schedulingDate: '', schedulingNum: '', schedulingUserId: '', workHours: '', unit: '' }
]);
const tableData = ref([]);
const unitFromRow = ref('');
const idFromRow = ref('');
const pendingNum = ref('');
const specificationModelFromRow = ref('');
const pendingNum = ref(0);
const userList = ref([])
const receive = ref('')
const sunqianUserId = ref('')
// äº§çº¿é€‰é¡¹
const productionLines = ref([
  { label: '产线1', value: '产线1' },
  { label: '产线2', value: '产线2' },
  { label: '产线3', value: '产线3' },
  { label: '产线4', value: '产线4' }
])
// æ‰“开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
    pendingNum.value = row?.pendingNum ?? 0;
    unitFromRow.value = row?.unit ?? '';
    idFromRow.value = row?.id ?? '';
    specificationModelFromRow.value = row?.specificationModel ?? '';
    userListNoPageByTenantId().then((res) => {
        userList.value = res.data;
        // æ‰¾åˆ°å­™å€©çš„用户ID并设置为默认值
        const sunqianUser = userList.value.find(user => user.nickName === '孙倩');
        if (sunqianUser) {
            sunqianUserId.value = sunqianUser.userId;
        }
        // åœ¨ç”¨æˆ·åˆ—表加载完成后创建行数据,并将产线数据带入
        tableData.value = [createRow(row)];
    });
    pendingNum.value = row.pendingNum
  if (row && row.unit !== undefined) {
    unitFromRow.value = row.unit;
    idFromRow.value = row.id;
    tableData.value.forEach(item => {
      item.unit = row.unit;
      item.id = row.id;
    });
  } else {
    unitFromRow.value = '';
  }
}
const createRow = (row) => ({
    id: idFromRow.value,
    process: '包装',
    schedulingDate: '',
    schedulingNum: null,
    schedulingUserId: sunqianUserId.value, // é»˜è®¤è®¾ç½®ä¸ºå­™å€©çš„用户ID
    workHours: null,
    unit: unitFromRow.value,
    remark: '',
    type: specificationModelFromRow.value,
    productionLine: row?.productionLine ?? '', // ä»Žè¡Œæ•°æ®ä¸­èŽ·å–äº§çº¿ä¿¡æ¯
});
const submitForm = () => {
    // 1. æ£€æŸ¥æ¯ä¸€è¡Œæ˜¯å¦å¡«å†™å®Œæ•´
    for (let i = 0; i < tableData.value.length; i++) {
@@ -134,7 +192,8 @@
            row.schedulingNum === '' || row.schedulingNum === null ||
            !row.schedulingUserId ||
            row.workHours === '' || row.workHours === null ||
            !row.unit
            !row.unit ||
            !row.productionLine
        ) {
            proxy.$modal.msgError(`第${i + 1}行数据未填写完整`);
            return;
@@ -148,7 +207,15 @@
        proxy.$modal.msgError('排产数量合计不能超过待排产数量');
        return;
    }
    processScheduling(tableData.value).then((res) => {
    // 3. å°† receive å­—段添加到每条数据中,并移除 loss å­—段
    const submitData = tableData.value.map(row => {
        const { loss, ...rest } = row;
        return {
            ...rest,
            receive: receive.value
        };
    });
    processScheduling(submitData).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
    })
@@ -159,6 +226,12 @@
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  dialogFormVisible.value = false;
  receive.value = '';
  tableData.value = [];
  unitFromRow.value = '';
  idFromRow.value = '';
  specificationModelFromRow.value = '';
  pendingNum.value = 0;
  emit('close')
};
defineExpose({
@@ -166,7 +239,7 @@
});
const addRow = () => {
  tableData.value.push({ id: idFromRow.value, process: '', unit: unitFromRow.value, schedulingNum: '', workHours: '', schedulingDate: '', schedulingUserId: '' });
  tableData.value.push(createRow());
};
const removeRow = (index) => {
  tableData.value.splice(index, 1);
src/views/productionManagement/operationScheduling/index.vue
@@ -7,11 +7,16 @@
                                        style="width: 200px;"
                                        @change="handleQuery" />
                </el-form-item>
                <el-form-item label="项目名称:">
                    <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search"
                <el-form-item label="合同号:">
                    <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
                                        style="width: 200px;"
                                        @change="handleQuery" />
                </el-form-item>
<!--                <el-form-item label="项目名称:">-->
<!--                    <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search"-->
<!--                                        style="width: 200px;"-->
<!--                                        @change="handleQuery" />-->
<!--                </el-form-item>-->
                <el-form-item label="派工日期:">
                    <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                                                    placeholder="请选择" clearable @change="changeDaterange" />
@@ -60,10 +65,12 @@
const data = reactive({
    searchForm: {
        staffName: "",
        customerName: "",
        salesContractNo: "",
        status: 1,
        entryDate: null, // å½•入日期
        entryDateStart: undefined,
        entryDateEnd: undefined,
        entryDate: [dayjs().format("YYYY-MM-DD"), dayjs().format("YYYY-MM-DD")], // å½•入日期,默认当天
        entryDateStart: dayjs().format("YYYY-MM-DD"),
        entryDateEnd: dayjs().format("YYYY-MM-DD"),
    },
});
const { searchForm } = toRefs(data);
@@ -105,21 +112,21 @@
        prop: "salesContractNo",
        width: 200,
    },
    {
        label: "客户合同号",
        prop: "customerContractNo",
        width: 200,
    },
    // {
    //     label: "客户合同号",
    //     prop: "customerContractNo",
    //     width: 200,
    // },
    {
        label: "客户名称",
        prop: "customerName",
        width: 200,
    },
    {
        label: "项目名称",
        prop: "projectName",
        width:300
    },
    // {
    //     label: "项目名称",
    //     prop: "projectName",
    //     width:300
    // },
    {
        label: "产品大类",
        prop: "productCategory",
@@ -131,6 +138,16 @@
        width: 150,
    },
    {
        label: "绑定机器",
        prop: "speculativeTradingName",
        width: 220,
    },
    // {
    //     label: "产线",
    //     prop: "productionLine",
    //     width: 220,
    // },
    {
        label: "单位",
        prop: "unit",
    },
src/views/productionManagement/processRoute/Edit.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,168 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="编辑工艺路线"
        width="400"
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
        <el-form-item label="产品大类:" prop="productId">
          <el-tree-select
              v-model="formState.productId"
              placeholder="请选择"
              clearable
              check-strictly
              @change="getModels"
              :data="productOptions"
              :render-after-expand="false"
              style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="规格型号:" prop="productModelId">
          <el-select
              v-model="formState.productModelId"
              placeholder="请选择"
              clearable
          >
            <el-option
                v-for="item in productModelsOptions"
                :key="item.id"
                :label="item.model"
                :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="备注" prop="description">
          <el-input v-model="formState.description" type="textarea" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {ref, computed, getCurrentInstance, onMounted} from "vue";
import {update} from "@/api/productionManagement/processRoute.js";
import {modelList, productTreeList} from "@/api/basicData/product.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  record: {
    type: Object,
    required: true,
  }
});
const emit = defineEmits(['update:visible', 'completed']);
// å“åº”式数据(替代选项式的 data)
const formState = ref({});
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
let { proxy } = getCurrentInstance()
const productModelsOptions = ref([])
const productOptions = ref([])
const closeModal = () => {
  isShow.value = false;
};
const setFormData = () => {
  formState.value = props.record
}
const getProductOptions = () => {
  productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
  });
};
const getModels = (value) => {
  formState.value.productModelId = undefined;
  productModelsOptions.value = [];
  if (value) {
    modelList({ id: value }).then((res) => {
      productModelsOptions.value = res;
    });
  }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹çš„label
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œç›´æŽ¥è¿”å›žï¼ˆå·²ç»æ˜¯label字符串)
      }
    }
  }
  return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
};
function convertIdToValue(data) {
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id, // å°† id æ”¹ä¸º value
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      update(formState.value).then(res => {
        // å…³é—­æ¨¡æ€æ¡†
        isShow.value = false;
        // å‘ŠçŸ¥çˆ¶ç»„件已完成
        emit('completed');
        proxy.$modal.msgSuccess("提交成功");
      })
    }
  })
};
defineExpose({
  closeModal,
  handleSubmit,
  isShow,
});
onMounted(() => {
  getProductOptions()
  getModels(props.record.productId)
  nextTick(() => {
    setFormData()
  });
})
</script>
src/views/productionManagement/processRoute/ItemsForm.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,531 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="工艺路线项目"
        width="800px"
        @close="closeModal"
    >
      <div class="operate-button">
        <el-button
            type="primary"
            @click="isShowProductSelectDialog = true"
            class="mb5"
            style="margin-bottom: 10px;"
        >
          é€‰æ‹©äº§å“
        </el-button>
        <el-switch
            v-model="isTable"
            inline-prompt
            active-text="表格"
            inactive-text="列表"
            @change="handleViewChange"
        />
      </div>
      <el-table
          v-if="isTable"
          ref="multipleTable"
          v-loading="tableLoading"
          border
          :data="routeItems"
          :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
          row-key="id"
          tooltip-effect="dark"
          class="lims-table"
          style="cursor: move;"
      >
        <el-table-column align="center" label="序号" width="60">
          <template #default="scope">
            {{ scope.$index + 1 }}
          </template>
        </el-table-column>
        <el-table-column
            v-for="(item, index) in tableColumn"
            :key="index"
            :label="item.label"
            :width="item.width"
            show-overflow-tooltip
        >
          <template #default="scope" v-if="item.dataType === 'action'">
            <el-button
                v-for="(op, opIndex) in item.operation"
                :key="opIndex"
                :type="op.type"
                :link="op.link"
                size="small"
                @click.stop="op.clickFun(scope.row)"
            >
              {{ op.name }}
            </el-button>
          </template>
          <template #default="scope" v-else>
            <template v-if="item.prop === 'processId'">
              <el-select
                  v-model="scope.row[item.prop]"
                  style="width: 100%;"
                  @mousedown.stop
              >
                <el-option
                    v-for="process in processOptions"
                    :key="process.id"
                    :label="process.name"
                    :value="process.id"
                />
              </el-select>
            </template>
            <template v-else>
              {{ scope.row[item.prop] || '-' }}
            </template>
          </template>
        </el-table-column>
      </el-table>
      <!-- ä½¿ç”¨æ™®é€šdiv替代el-steps -->
      <div
          v-else
          ref="stepsContainer"
          class="mb5 custom-steps"
          style="padding: 10px 0; display: flex; flex-wrap: nowrap; gap: 20px; align-items: flex-start;"
      >
        <div
            v-for="(item, index) in routeItems"
            :key="item.id"
            class="custom-step draggable-step"
            :data-id="item.id"
            style="cursor: move; flex: 0 0 auto; min-width: 220px;"
        >
          <div class="step-content">
            <div class="step-number">{{ index + 1 }}</div>
            <el-card
                :header="item.productName"
                class="step-card"
                style="cursor: move;"
            >
              <div class="step-card-content">
                <p>{{ item.model }}</p>
                <p>{{ item.unit }}</p>
                <el-select
                    v-model="item.processId"
                    style="width: 100%;"
                    @mousedown.stop
                >
                  <el-option
                      v-for="process in processOptions"
                      :key="process.id"
                      :label="process.name"
                      :value="process.id"
                  />
                </el-select>
              </div>
              <template #footer>
                <div class="step-card-footer">
                  <el-button type="danger" link size="small" @click.stop="removeItemByID(item.id)">删除</el-button>
                </div>
              </template>
            </el-card>
          </div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <ProductSelectDialog
        v-model="isShowProductSelectDialog"
        @confirm="handelSelectProducts"
    />
  </div>
</template>
<script setup>
import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import { findProcessRouteItemList, addOrUpdateProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
import { processList } from "@/api/productionManagement/productionProcess.js";
import Sortable from 'sortablejs';
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
    default: false
  },
  record: {
    type: Object,
    required: true,
    default: () => ({})
  }
});
const emit = defineEmits(['update:visible', 'completed']);
const processOptions = ref([]);
const tableLoading = ref(false);
const isShowProductSelectDialog = ref(false);
const routeItems = ref([]);
let tableSortable = null;
let stepsSortable = null;
const multipleTable = ref(null);
const stepsContainer = ref(null);
const isTable = ref(true);
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  }
});
const tableColumn = ref([
  { label: "产品名称", prop: "productName", width: 180 },
  { label: "规格名称", prop: "model", width: 150 },
  { label: "单位", prop: "unit", width: 80 },
  { label: "工序名称", prop: "processId", width: 180 },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 100,
    operation: [
      {
        name: "删除",
        type: "danger",
        link: true,
        clickFun: (row) => {
          const idx = routeItems.value.findIndex(item => item.id === row.id);
          if (idx > -1) {
            removeItem(idx)
          }
        }
      }
    ]
  }
]);
const removeItem = (index) => {
  routeItems.value.splice(index, 1);
  nextTick(() => initSortable());
};
const removeItemByID = (id) => {
  const idx = routeItems.value.findIndex(item => item.id === id);
  if (idx > -1) {
    routeItems.value.splice(idx, 1);
    nextTick(() => initSortable());
  }
};
const closeModal = () => {
  isShow.value = false;
};
const handelSelectProducts = (products) => {
  destroySortable();
  const newData = products.map(({ id, ...product }) => ({
    ...product,
    productModelId: id,
    routeId: props.record.id,
    id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
    processId: undefined
  }));
  console.log('选择产品前数组:', routeItems.value);
  routeItems.value.push(...newData);
  routeItems.value = [...routeItems.value];
  console.log('选择产品后数组:', routeItems.value);
  // å»¶è¿Ÿåˆå§‹åŒ–,确保DOM完全渲染
  nextTick(() => {
    // å¼ºåˆ¶é‡æ–°æ¸²æŸ“组件
    if (proxy?.$forceUpdate) {
      proxy.$forceUpdate();
    }
    const temp = [...routeItems.value];
    routeItems.value = [];
    nextTick(() => {
      routeItems.value = temp;
      initSortable();
    });
  });
};
const findProcessRouteItems = () => {
  tableLoading.value = true;
  findProcessRouteItemList({ routeId: props.record.id })
      .then(res => {
        tableLoading.value = false;
        routeItems.value = res.data.map(item => ({
          ...item,
          processId: item.processId === 0 ? undefined : item.processId
        }));
        // å»¶è¿Ÿåˆå§‹åŒ–,确保DOM完全渲染
        nextTick(() => {
          setTimeout(() => initSortable(), 100);
        });
      })
      .catch(err => {
        tableLoading.value = false;
        console.error("获取列表失败:", err);
      });
};
const findProcessList = () => {
  processList({})
      .then(res => {
        processOptions.value = res.data;
      })
      .catch(err => {
        console.error("获取工序失败:", err);
      });
};
const { proxy } = getCurrentInstance() || {};
const handleSubmit = () => {
  const hasEmptyProcess = routeItems.value.some(item => !item.processId);
  if (hasEmptyProcess) {
    proxy?.$modal?.msgError("请为所有项目选择工序");
    return;
  }
  addOrUpdateProcessRouteItem({
    routeId: props.record.id,
    processRouteItem: routeItems.value.map(({ id, ...item }) => item)
  })
      .then(res => {
        isShow.value = false;
        emit('completed');
        proxy?.$modal?.msgSuccess("提交成功");
      })
      .catch(err => {
        proxy?.$modal?.msgError(`提交失败:${err.msg || "网络异常"}`);
      });
};
const destroySortable = () => {
  if (tableSortable) {
    tableSortable.destroy();
    tableSortable = null;
  }
  if (stepsSortable) {
    stepsSortable.destroy();
    stepsSortable = null;
  }
};
const initSortable = () => {
  destroySortable();
  if (isTable.value) {
    if (!multipleTable.value) return;
    const tbody = multipleTable.value.$el.querySelector('.el-table__body tbody') ||
        multipleTable.value.$el.querySelector('.el-table__body-wrapper > table > tbody');
    if (!tbody) return;
    tableSortable = new Sortable(tbody, {
      animation: 150,
      ghostClass: 'sortable-ghost',
      handle: '.el-table__row',
      filter: '.el-button, .el-select',
      onEnd: (evt) => {
        if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex]) return;
        // ä½¿ç”¨æ•°ç»„ splice æ–¹æ³•重新排序,与表格模式保持一致
        const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
        routeItems.value.splice(evt.newIndex, 0, moveItem);
        routeItems.value = [...routeItems.value];
        console.log('排序后数组:', routeItems.value);
      }
    });
  } else {
    if (!stepsContainer.value) return;
    // ä¿®æ”¹ï¼šç›´æŽ¥ä½¿ç”¨stepsContainer.value作为拖拽容器
    const stepsList = stepsContainer.value;
    if (!stepsList) {
      console.warn('未找到步骤条拖拽容器');
      return;
    }
    // ä¿®æ”¹ï¼šç®€åŒ–拖拽配置
    stepsSortable = new Sortable(stepsList, {
      animation: 150,
      ghostClass: 'sortable-ghost',
      draggable: '.draggable-step', // å¯æ‹–拽元素
      handle: '.draggable-step, .step-card', // æ‹–拽手柄
      filter: '.el-button, .el-select, .el-input', // è¿‡æ»¤æŒ‰é’®/选择器
      forceFallback: true,
      fallbackClass: 'sortable-fallback',
      preventOnFilter: true,
      scroll: true,
      scrollSensitivity: 30,
      scrollSpeed: 10,
      bubbleScroll: true,
      onEnd: (evt) => {
        if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex]) return;
        // ä½¿ç”¨æ•°ç»„ splice æ–¹æ³•重新排序
        const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
        routeItems.value.splice(evt.newIndex, 0, moveItem);
        routeItems.value = [...routeItems.value];
      }
    });
    // è°ƒè¯•:打印容器和实例,确认绑定成功
    console.log('步骤条拖拽容器:', stepsList);
    console.log('Sortable实例:', stepsSortable);
  }
};
const handleViewChange = () => {
  destroySortable();
  // å»¶è¿Ÿåˆå§‹åŒ–,确保视图切换后DOM完全渲染
  nextTick(() => {
    setTimeout(() => initSortable(), 100);
  });
};
onMounted(() => {
  findProcessRouteItems();
  findProcessList();
});
onUnmounted(() => {
  destroySortable();
});
defineExpose({
  closeModal,
  handleSubmit,
  isShow
});
</script>
<style scoped>
:deep(.sortable-ghost) {
  opacity: 0.6;
  background-color: #f5f7fa !important;
}
:deep(.el-table__row) {
  transition: background-color 0.2s;
}
:deep(.el-table__row:hover) {
  background-color: #f9fafc !important;
}
:deep(.el-card__footer){
  padding: 0 !important;
}
.operate-button {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
/* ä¿®æ”¹ï¼šè‡ªå®šä¹‰æ­¥éª¤æ¡å®¹å™¨æ ·å¼ */
.custom-steps {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 20px;
  min-height: 100px;
}
/* ä¿®æ”¹ï¼šè‡ªå®šä¹‰æ­¥éª¤é¡¹æ ·å¼ */
.custom-step {
  cursor: move !important;
  padding: 8px;
  position: relative;
  transition: all 0.2s ease;
  flex: 0 0 auto;
  min-width: 220px;
  touch-action: none;
}
/* æ‹–拽悬浮样式,提示可拖拽 */
.custom-step:hover {
  background-color: rgba(64, 158, 255, 0.05);
  transform: translateY(-2px);
}
.sortable-ghost {
  opacity: 0.4;
  background-color: #f5f7fa !important;
  border: 2px dashed #409eff;
  margin: 10px;
  transform: scale(1.02);
}
.sortable-fallback {
  opacity: 0.9;
  background-color: #f5f7fa;
  border: 1px solid #409eff;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  transform: rotate(2deg);
  margin: 10px;
}
.step-card {
  cursor: move !important;
  transition: box-shadow 0.2s ease;
  user-select: none;
  -webkit-user-select: none;
  pointer-events: auto;
  margin: 10px;
  height: 240px;
}
.step-card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.step-content {
  width: 220px;
  user-select: none;
}
.step-card-content {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.step-card-footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: 10px;
}
/* è‡ªå®šä¹‰åºå·æ ·å¼ä¼˜åŒ– */
.step-number {
  font-weight: bold;
  text-align: center;
  width: 36px;
  height: 36px;
  line-height: 36px;
  margin: 0 auto 10px;
  background: #409eff;
  color: #fff;
  border-radius: 50%;
  font-size: 14px;
}
</style>
src/views/productionManagement/processRoute/New.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,163 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="新增工艺路线"
        width="400"
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
        <el-form-item label="产品大类:" prop="productId">
          <el-tree-select
              v-model="formState.productId"
              placeholder="请选择"
              clearable
              check-strictly
              @change="getModels"
              :data="productOptions"
              :render-after-expand="false"
              style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="规格型号:" prop="productModelId">
          <el-select
              v-model="formState.productModelId"
              placeholder="请选择"
              clearable
          >
            <el-option
                v-for="item in productModelsOptions"
                :key="item.id"
                :label="item.model"
                :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="备注" prop="description">
          <el-input v-model="formState.description" type="textarea" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {ref, computed, getCurrentInstance, onMounted} from "vue";
import {add} from "@/api/productionManagement/processRoute.js";
import {modelList, productTreeList} from "@/api/basicData/product.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
});
const emit = defineEmits(['update:visible', 'completed']);
// å“åº”式数据(替代选项式的 data)
const formState = ref({
  productId: undefined,
  productModelId: undefined,
  description: '',
});
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
const productModelsOptions = ref([])
const productOptions = ref([])
let { proxy } = getCurrentInstance()
const closeModal = () => {
  isShow.value = false;
};
const getProductOptions = () => {
  productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
  });
};
const getModels = (value) => {
  formState.value.productId = undefined;
  formState.value.productModelId = undefined;
  productModelsOptions.value = [];
  if (value) {
    formState.value.productId = findNodeById(productOptions.value, value) || undefined;
    modelList({ id: value }).then((res) => {
      productModelsOptions.value = res;
    });
  }
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹çš„label
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œç›´æŽ¥è¿”å›žï¼ˆå·²ç»æ˜¯label字符串)
      }
    }
  }
  return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
};
function convertIdToValue(data) {
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id, // å°† id æ”¹ä¸º value
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      add(formState.value).then(res => {
        // å…³é—­æ¨¡æ€æ¡†
        isShow.value = false;
        // å‘ŠçŸ¥çˆ¶ç»„件已完成
        emit('completed');
        proxy.$modal.msgSuccess("提交成功");
      })
    }
  })
};
defineExpose({
  closeModal,
  handleSubmit,
  isShow,
});
onMounted(() => {
  getProductOptions()
})
</script>
src/views/productionManagement/processRoute/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,184 @@
<template>
    <div class="app-container">
        <div class="search_form">
            <el-form :model="searchForm" :inline="true">
                <el-form-item label="规格名称:">
                    <el-input v-model="searchForm.model" placeholder="请输入" clearable prefix-icon="Search"
                                        style="width: 200px;"
                                        @change="handleQuery" />
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="handleQuery">搜索</el-button>
                </el-form-item>
            </el-form>
        </div>
        <div class="table_list">
            <div style="text-align: right" class="mb10">
                <el-button type="primary" @click="showNewModal">新增工艺路线</el-button>
                <el-button type="danger" @click="handleDelete" :disabled="selectedRows.length === 0" plain>删除工艺路线</el-button>
            </div>
            <PIMTable
                rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :isSelection="true"
                @selection-change="handleSelectionChange"
                :tableLoading="tableLoading"
                @pagination="pagination"
                :total="page.total"
            />
        </div>
        <new-process
      v-if="isShowNewModal"
      v-model:visible="isShowNewModal"
      @completed="getList"
    />
    <edit-process
      v-if="isShowEditModal"
      v-model:visible="isShowEditModal"
      :record="record"
      @completed="getList"
    />
    <route-item-form
        v-if="isShowItemModal"
        v-model:visible="isShowItemModal"
        :record="record"
        @completed="getList"
    />
    </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import NewProcess from "@/views/productionManagement/processRoute/New.vue";
import EditProcess from "@/views/productionManagement/processRoute/Edit.vue";
import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue";
import {listPage, del} from "@/api/productionManagement/processRoute.js";
const data = reactive({
    searchForm: {
    model: "",
    },
});
const { searchForm } = toRefs(data);
const tableColumn = ref([
  {
    label: "产品名称",
    prop: "productName",
  },
    {
        label: "规格名称",
        prop: "model",
    },
    {
        label: "描述",
        prop: "description",
    },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 280,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          showEditModal(row);
        }
      },
      {
        name: "路线项目",
        type: "text",
        clickFun: (row) => {
          showItemModal(row);
        }
      }
    ]
  }
]);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const isShowNewModal = ref(false);
const isShowEditModal = ref(false);
const isShowItemModal = ref(false);
const record = ref({});
const page = reactive({
    current: 1,
    size: 100,
    total: 0,
});
const { proxy } = getCurrentInstance()
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
    page.current = 1;
    getList();
};
const pagination = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList = () => {
    tableLoading.value = true;
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined
  listPage(params).then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records.map(item => ({
            ...item,
        }));
        page.total = res.data.total;
    }).catch(err => {
        tableLoading.value = false;
    })
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
    selectedRows.value = selection;
};
// æ‰“开新增弹框
const showNewModal = () => {
  isShowNewModal.value = true
};
const showEditModal = (row) => {
  isShowEditModal.value = true
  record.value = row
};
const showItemModal = (row) => {
  isShowItemModal.value = true
  record.value = row
};
// åˆ é™¤
function handleDelete() {
  const ids = selectedRows.value.map((item) => item.id);
  proxy.$modal
      .confirm('是否确认删除已勾选的数据项?')
      .then(function () {
        return del(ids);
      })
      .then(() => {
        getList();
        proxy.$modal.msgSuccess("删除成功");
      })
      .catch(() => {});
}
onMounted(() => {
    getList();
});
</script>
<style scoped></style>
src/views/productionManagement/productStructure/StructureEdit.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,304 @@
<template>
  <el-dialog
      v-model="visible"
      title="结构"
      width="1200"
      close-on-click-modal
      @close="visible = false"
  >
    <el-button
        v-if="dataValue.isEdit"
        type="primary"
        @click="addItem"
        style="margin-bottom: 10px"
    >添加
    </el-button>
    <el-button
        v-if="!dataValue.isEdit"
        type="primary"
        @click="dataValue.isEdit = true"
        style="margin-bottom: 10px"
    >编辑
    </el-button>
    <el-button
        v-if="dataValue.isEdit"
        type="primary"
        @click="cancelEdit"
        style="margin-bottom: 10px"
    >取消
    </el-button>
    <el-form
        ref="form"
        :model="dataValue"
    >
      <el-table :data="dataValue.dataList" style="width: 100%">
        <el-table-column prop="productName" label="产品" width="150"/>
        <el-table-column prop="model" label="规格" width="150">
          <template #default="{ row, $index }">
            <el-form-item
                v-if="dataValue.isEdit"
                :prop="`dataList.${$index}.model`"
                :rules="[{ required: true, message: '请选择规格', trigger: ['blur','change'] }]"
                style="margin: 0"
            >
              <el-select
                  v-model="row.model"
                  placeholder="请选择产品"
                  clearable
                  :disabled="!dataValue.isEdit"
                  style="width: 100%"
                  @visible-change="(v) => { if (v) openDialog($index) }"
              >
                <el-option v-if="row.model" :label="row.model" :value="row.model" />
              </el-select>
            </el-form-item>
          </template>
        </el-table-column>
        <el-table-column prop="processId" label="消耗工序" width="150">
          <template #default="{ row, $index }">
            <el-form-item
                :prop="`dataList.${$index}.processId`"
                :rules="[{ required: true, message: '请选择消耗工序', trigger: 'change' }]"
                style="margin: 0"
            >
              <el-select
                  v-model="row.processId"
                  placeholder="请选择"
                  filterable
                  clearable
                  style="width: 100%"
                  :disabled="!dataValue.isEdit"
              >
                <el-option
                    v-for="item in dataValue.processOptions"
                    :key="item.id"
                    :label="item.name"
                    :value="item.id"
                />
              </el-select>
            </el-form-item>
          </template>
        </el-table-column>
        <el-table-column prop="unitQuantity" label="单位产出所需数量" width="150">
          <template #default="{ row, $index }">
            <el-form-item
                :prop="`dataList.${$index}.unitQuantity`"
                :rules="[{ required: true, message: '请输入单位产出所需数量', trigger: ['blur','change'] }]"
                style="margin: 0"
            >
              <el-input-number
                  v-model="row.unitQuantity"
                  :min="0"
                  :precision="2"
                  :step="1"
                  controls-position="right"
                  style="width: 100%"
                  :disabled="!dataValue.isEdit"
              />
            </el-form-item>
          </template>
        </el-table-column>
        <el-table-column prop="demandedQuantity" label="需求总量" width="150">
          <template #default="{ row, $index }">
            <el-form-item
                :prop="`dataList.${$index}.demandedQuantity`"
                :rules="[{ required: true, message: '请输入需求总量', trigger: ['blur','change'] }]"
                style="margin: 0"
            >
              <el-input-number
                  v-model="row.demandedQuantity"
                  :min="0"
                  :precision="2"
                  :step="1"
                  controls-position="right"
                  style="width: 100%"
                  :disabled="!dataValue.isEdit"
              />
            </el-form-item>
          </template>
        </el-table-column>
        <el-table-column prop="unit" label="单位" width="150">
          <template #default="{ row, $index }">
            <el-form-item
                :prop="`dataList.${$index}.unit`"
                :rules="[{ required: true, message: '请输入单位', trigger: ['blur','change'] }]"
                style="margin: 0"
            >
              <el-input
                  v-model="row.unit"
                  placeholder="请输入单位"
                  clearable
                  :disabled="!dataValue.isEdit"
              />
            </el-form-item>
          </template>
        </el-table-column>
        <el-table-column prop="diskQuantity" label="盘数(盘)" width="150">
          <template #default="{ row, $index }">
            <el-form-item
                :prop="`dataList.${$index}.diskQuantity`"
                :rules="[{ required: true, message: '请输入盘数', trigger: ['blur','change'] }]"
                style="margin: 0"
            >
              <el-input-number
                  v-model="row.diskQuantity"
                  :min="0"
                  :precision="0"
                  :step="1"
                  controls-position="right"
                  style="width: 100%"
                  :disabled="!dataValue.isEdit"
              />
            </el-form-item>
          </template>
        </el-table-column>
        <el-table-column label="操作">
          <template #default="{ row, $index }">
            <el-button
                type="danger"
                text
                @click="dataValue.dataList.splice($index, 1)"
            >删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-form>
    <product-select-dialog
        v-if="dataValue.showProductDialog"
        v-model:model-value="dataValue.showProductDialog"
        @confirm="handleProduct"/>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="visible = false">取消</el-button>
        <el-button type="primary" :loading="dataValue.loading" @click="submit" :disabled="!dataValue.isEdit">
          ç¡®è®¤
        </el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup lang="ts">
import {computed, defineAsyncComponent, defineComponent, onMounted, reactive, ref} from "vue";
import {queryList, add} from '@/api/productionManagement/productStructure.js'
import {list} from '@/api/productionManagement/productionProcess'
import {ElMessage} from "element-plus";
defineComponent({
  name: "StructureEdit",
})
const ProductSelectDialog = defineAsyncComponent(() => import('@/views/basicData/product/ProductSelectDialog.vue'))
const form = ref()
const props = defineProps({
  showModel: {
    type: Boolean,
    default: false
  },
  productModelId: {
    type: Number,
    required: true
  }
})
const emits = defineEmits(['update:showModel'])
const visible = computed({
  get() {
    return props.showModel
  },
  set(val) {
    emits('update:showModel', val)
  }
})
const dataValue = reactive({
  dataList: [],
  productOptions: [],
  processOptions: [],
  showProductDialog: false,
  currentRowIndex: null,
  loading: false,
  isEdit: false,
});
const openDialog = (index) => {
  dataValue.currentRowIndex = index
  dataValue.showProductDialog = true
}
const fetchData = async () => {
  const {data} = await queryList(props.productModelId)
  dataValue.dataList = data
}
const fetchProcessOptions = async () => {
  const {data} = await list(props.productModelId)
  dataValue.processOptions = data
}
const handleProduct = (row) => {
  if (row?.length > 1) {
    ElMessage.error('只能选择一个产品')
  }
  dataValue.dataList[dataValue.currentRowIndex].productName = row[0].productName
  dataValue.dataList[dataValue.currentRowIndex].model = row[0].model
  dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id
  dataValue.showProductDialog = false
}
const submit = () => {
  form.value.validate(valid => {
    dataValue.loading = true
    if (valid) {
      add({
        parentId: props.productModelId,
        productStructureList: dataValue.dataList || []
      }).then(res => {
        ElMessage.success('保存成功')
        visible.value = false
        dataValue.loading = false
      })
    }
  }).finally(() => {
    dataValue.loading = false
  })
}
const addItem = () => {
  dataValue.dataList.push({
    productName: '',
    productId: '',
    model: undefined,
    productModelId: undefined,
    processId: '',
    unitQuantity: 0,
    demandedQuantity: 0,
    unit: '',
    diskQuantity: 0,
  })
}
const cancelEdit = () => {
  dataValue.isEdit = false
  dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined)
}
onMounted(() => {
  fetchData()
  fetchProcessOptions()
})
</script>
src/views/productionManagement/productStructure/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,101 @@
<template>
  <div class="app-container">
    <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
        @pagination="pagination"
    >
      <template #detail="{row}">
        <el-button
            type="primary"
            text
            @click="() =>{
              currentRowId = row.id;
              showEdit = true;
            }"
        >{{ row.productName }}
        </el-button>
      </template>
    </PIMTable>
    <StructureEdit v-if="showEdit" v-model:show-model="showEdit" :product-model-id="currentRowId"/>
  </div>
</template>
<script setup>
import {ref} from "vue";
import {productModelList} from "@/api/basicData/productModel.js";
const StructureEdit = defineAsyncComponent(() => import('@/views/productionManagement/productStructure/StructureEdit.vue'))
const tableColumn = ref([
  {
    label: "产品名称",
    prop: "productName",
    dataType: 'slot',
    slot: "detail"
  },
  {
    label: "规格型号",
    prop: "model",
  },
  {
    label: "单位",
    prop: "unit",
  }
]);
const tableData = ref([]);
const tableLoading = ref(false);
const showEdit = ref(false);
const selectedRows = ref([]);
const currentRowId = ref(0);
const page = reactive({
  current: 1,
  size: 10,
  total: 0,
});
const data = reactive({
  form: {
    productName: "",
  },
  rules: {
    productName: [{required: true, message: "请输入", trigger: "blur"}],
  },
  modelForm: {
    otherModel: '',
    model: "",
    unit: "",
    speculativeTradingName: [],
  },
});
const {form, rules} = toRefs(data);
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// æŸ¥è¯¢è§„格型号
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getModelList();
};
const getModelList = () => {
  tableLoading.value = true;
  productModelList({
    current: page.current,
    size: page.size,
  }).then((res) => {
    tableData.value = res.records;
    page.total = res.total;
    tableLoading.value = false;
  });
};
onMounted(() => {
  getModelList();
})
</script>
src/views/productionManagement/productionCosting/index.vue
@@ -14,6 +14,15 @@
                    clearable
                    prefix-icon="Search"
                />
                <span class="search_title ml10">合同号:</span>
                <el-input
                    v-model="searchForm.salesContractNo"
                    style="width: 240px"
                    placeholder="请输入"
                    @change="handleQuery"
                    clearable
                    prefix-icon="Search"
                />
                <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
                >搜索</el-button
                >
@@ -61,21 +70,21 @@
        prop: "salesContractNo",
        width: 220,
    },
    {
        label: "客户合同号",
        prop: "customerContractNo",
        width: 250,
    },
    // {
    //     label: "客户合同号",
    //     prop: "customerContractNo",
    //     width: 250,
    // },
    {
        label: "客户名称",
        prop: "customerName",
        width: 250,
    },
    {
        label: "项目名称",
        prop: "projectName",
        width:300
    },
    // {
    //     label: "项目名称",
    //     prop: "projectName",
    //     width:300
    // },
    {
        label: "产品大类",
        prop: "productCategory",
@@ -121,6 +130,7 @@
const data = reactive({
    searchForm: {
        schedulingUserName: "",
        salesContractNo: "",
        entryDate: [
            dayjs().format("YYYY-MM-DD"),
            dayjs().add(1, "day").format("YYYY-MM-DD"),
@@ -171,7 +181,7 @@
        type: "warning",
    })
        .then(() => {
            proxy.download("/salesLedger/productionAccounting/export", {}, "生产核算.xlsx");
            proxy.download("/basic/customer/export", {}, "生产核算.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
src/views/productionManagement/productionDispatching/components/autoDispatchDia.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,153 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        title="自动派工"
        width="80%"
        @close="closeDia"
    >
      <el-form :model="form" label-width="140px" label-position="top" ref="formRef">
        <el-divider content-position="left">派工列表</el-divider>
        <el-table
            :data="dispatchList"
            border
            style="width: 100%; margin-top: 20px;"
            :row-class-name="tableRowClassName"
        >
          <el-table-column label="序号" type="index" width="60" align="center" />
          <el-table-column label="合同号" prop="salesContractNo" width="200" />
          <el-table-column label="客户名称" prop="customerName" width="200" />
          <!-- <el-table-column label="项目名称" prop="projectName" width="250" /> -->
          <el-table-column label="产品大类" prop="productCategory" width="150" />
          <el-table-column label="规格型号" prop="specificationModel" width="200" />
          <el-table-column label="绑定机器" prop="speculativeTradingName" width="120" />
          <el-table-column label="总数量" prop="quantity" width="100" align="right" />
          <el-table-column label="已排产" prop="schedulingNum" width="100" align="right" fixed="right" />
          <el-table-column label="待排产" prop="pendingQuantity" width="100" align="right" fixed="right" />
          <el-table-column label="本次排产" width="150" align="center" fixed="right">
            <template #default="{ row }">
              <el-input-number
                  v-model="row.schedulingNum"
                  :min="0"
                  :max="row.pendingQuantity"
                  :step="1"
                  :precision="0"
                  size="small"
                  style="width: 120px"
                  @change="(value) => changeCurrentNum(value, row)"
              />
            </template>
          </el-table-column>
        </el-table>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认派工</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {ref, reactive, toRefs, computed} from "vue";
import {productionDispatch, productionDispatchList} from "@/api/productionManagement/productionOrder.js";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const dialogFormVisible = ref(false);
const operationType = ref('')
const data = reactive({
  form: {},
  dispatchList: [], // æ´¾å·¥åˆ—表数据
});
const { form, dispatchList } = toRefs(data);
// è¡¨æ ¼è¡Œæ ·å¼
const tableRowClassName = ({ rowIndex }) => {
  if (rowIndex % 2 === 1) {
    return 'even-row'
  }
  return ''
}
// ä¿®æ”¹æœ¬æ¬¡æŽ’产数量
const changeCurrentNum = (value, row) => {
  if (value > row.pendingQuantity) {
    row.schedulingNum = row.pendingQuantity
    proxy.$modal.msgWarning('排产数量不可大于待排产数量')
  }
}
// æ‰“开弹框
const openDialog = (type, rows) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  // å¤„理传入的数据
  dispatchList.value = rows.map(row => ({
    ...row,
    schedulingNum: 0, // åˆå§‹åŒ–本次排产数量为0
    pendingQuantity: (Number(row.quantity) || 0) - (Number(row.schedulingNum) || 0) // è®¡ç®—待排产数量
  }))
}
// æäº¤è¡¨å•
const submitForm = () => {
  // æ£€æŸ¥æ˜¯å¦æœ‰æŽ’产数据
  const hasSchedulingData = dispatchList.value.some(item => item.schedulingNum > 0)
  if (!hasSchedulingData) {
    proxy.$modal.msgWarning('请至少为一条记录设置排产数量')
    return
  }
  // æž„造提交数据 - ç›´æŽ¥ä¼ é€’数组,不过滤
  const submitData = dispatchList.value
  console.log('提交自动派工数据:', submitData)
  // è°ƒç”¨API(这里需要根据实际接口调整)
  productionDispatchList(submitData).then(res => {
    proxy.$modal.msgSuccess(res.msg);
    closeDia();
  }).catch(err => {
    proxy.$modal.msgError("派工失败");
    console.error('派工失败:', err);
  })
}
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  dispatchList.value = []
  emit('close')
};
defineExpose({
  openDialog,
});
</script>
<style scoped>
:deep(.even-row) {
  background-color: #fafafa;
}
:deep(.el-table .cell) {
  padding: 8px 12px;
}
:deep(.el-table th) {
  background-color: #f5f7fa;
  color: #606266;
  font-weight: 600;
}
</style>
src/views/productionManagement/productionDispatching/components/formDia.vue
@@ -7,7 +7,7 @@
        @close="closeDia"
    >
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-row :gutter="30">
        <!-- <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="项目名称:" prop="projectName">
              <el-input v-model="form.projectName" placeholder="请输入" clearable disabled/>
@@ -16,6 +16,18 @@
          <el-col :span="12">
            <el-form-item label="产品大类:" prop="productCategory">
              <el-input v-model="form.productCategory" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row> -->
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="specificationModel">
              <el-input v-model="form.specificationModel" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="绑定机器:" prop="speculativeTradingName">
              <el-input v-model="form.speculativeTradingName" placeholder="自动获取" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -46,6 +58,11 @@
                            />
                        </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="产品大类:" prop="productCategory">
              <el-input v-model="form.productCategory" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
                    <el-col :span="12">
@@ -54,6 +71,9 @@
                                v-model="form.schedulingUserId"
                                placeholder="选择人员"
                                style="width: 100%;"
                filterable
                default-first-option
                :reserve-keyword="false"
                            >
                                <el-option
                                    v-for="user in userList"
@@ -105,11 +125,13 @@
  form: {
        projectName: "",
        productCategory: "",
        specificationModel: "", // è§„格型号
        quantity: "",
        schedulingNum: "",
        schedulingUserId: "",
        schedulingDate: "",
        pendingQuantity: "",
        speculativeTradingName: "", // ç»‘定机器名称
  },
  rules: {
        schedulingNum: [{ required: true, message: "请输入", trigger: "blur" },],
@@ -121,6 +143,7 @@
const userList = ref([])
const userStore = useUserStore()
// æ‰“开弹框
const openDialog = (type, row) => {
  operationType.value = type;
src/views/productionManagement/productionDispatching/index.vue
@@ -1,5 +1,32 @@
<template>
    <div class="app-container">
        <!-- ç‚’机1-4 å±•示(总量 / æ­£åœ¨ç”Ÿäº§é‡ / ç©ºä½™é‡ï¼‰ -->
        <div class="machines-grid">
            <div v-for="machine in machines" :key="machine.id" class="machine-card">
                <div class="machine-title">{{ machine.name }}</div>
                <div class="machine-metrics">
                <div class="machine-control">
                    <span>总量(kg):</span>
                    <el-input-number v-model="machineData[machine.name].workLoad" :min="0" :step="1" size="small" />
                </div>
                <div><span> é¢„计投入量(kg):</span><span>{{ machineData[machine.name].currentWorkLoad }}</span></div>
                <div><span>空余工作量(kg):</span><span>{{ machineData[machine.name].vacant }}</span></div>
            </div>
            </div>
            <div class="save-button-container">
                <div class="loss-rate-container">
                    <span class="loss-rate-label">损耗率(%):</span>
                    <el-select v-model="rate" placeholder="请选择损耗率" style="width: 120px" size="small">
                        <el-option label="6" :value="6" />
                        <el-option label="7" :value="7" />
                        <el-option label="8" :value="8" />
                        <el-option label="9" :value="9" />
                        <el-option label="10" :value="10" />
                    </el-select>
                </div>
                <el-button type="primary" @click="saveMachineTotals" size="small">保存设置</el-button>
            </div>
        </div>
        <div class="search_form">
            <div>
                <span class="search_title">客户名称:</span>
@@ -11,24 +38,40 @@
                    clearable
                    prefix-icon="Search"
                />
                <span class="search_title ml10">项目名称:</span>
                <span class="search_title ml10">合同号:</span>
                <el-input
                    v-model="searchForm.projectName"
                    v-model="searchForm.salesContractNo"
                    style="width: 240px"
                    placeholder="请输入"
                    @change="handleQuery"
                    clearable
                    prefix-icon="Search"
                />
<!--                <span class="search_title ml10">项目名称:</span>-->
<!--                <el-input-->
<!--                    v-model="searchForm.projectName"-->
<!--                    style="width: 240px"-->
<!--                    placeholder="请输入"-->
<!--                    @change="handleQuery"-->
<!--                    clearable-->
<!--                    prefix-icon="Search"-->
<!--                />-->
                <span class="search_title ml10">录入日期:</span>
                <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                                                placeholder="请选择" clearable @change="changeDaterange" />
                                        placeholder="请选择" clearable @change="changeDaterange" />
                <el-checkbox
                    style="margin-left: 10px"
                    v-model="searchForm.status"
                    label="不显示待排数量为0"
                    @change="handleQuery"
                />
                <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
            </div>
            <div>
                <el-button type="primary" @click="openForm('add')">生产派工</el-button>
                <el-button @click="handleOut">导出</el-button>
            </div>
            <el-button type="primary" @click="openForm('add')">生产派工</el-button>
            <el-button type="success" @click="openAutoDispatch">自动派工</el-button>
            <el-button @click="handleOut">导出</el-button>
        </div>
        </div>
        <div class="table_list">
            <PIMTable
@@ -44,23 +87,27 @@
            ></PIMTable>
        </div>
        <form-dia ref="formDia" @close="handleQuery"></form-dia>
        <auto-dispatch-dia ref="autoDispatchDia" @close="handleQuery"></auto-dispatch-dia>
    </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick, computed, watch} from "vue";
import FormDia from "@/views/productionManagement/productionDispatching/components/formDia.vue";
import AutoDispatchDia from "@/views/productionManagement/productionDispatching/components/autoDispatchDia.vue";
import dayjs from "dayjs";
import {schedulingListPage} from "@/api/productionManagement/productionOrder.js";
import {schedulingListPage, schedulingList, addSpeculatTrading, updateSpeculatTrading, getLossRate, addLossRate, updateLossRate} from "@/api/productionManagement/productionOrder.js";
import { ElMessageBox } from "element-plus";
const data = reactive({
    searchForm: {
        customerName: "",
        salesContractNo: "",
        projectName: "",
        entryDate: null, // å½•入日期
        entryDateStart: undefined,
        entryDateEnd: undefined,
        status: true,
        entryDate: [dayjs().format("YYYY-MM-DD"), dayjs().format("YYYY-MM-DD")], // å½•入日期,默认当天
        entryDateStart: dayjs().format("YYYY-MM-DD"),
        entryDateEnd: dayjs().format("YYYY-MM-DD"),
    },
});
const { searchForm } = toRefs(data);
@@ -71,19 +118,9 @@
        width: 220,
    },
    {
        label: "客户合同号",
        prop: "customerContractNo",
        width: 250,
    },
    {
        label: "客户名称",
        prop: "customerName",
        width: 250,
    },
    {
        label: "项目名称",
        prop: "projectName",
        width:300
    },
    {
        label: "产品大类",
@@ -93,7 +130,12 @@
    {
        label: "规格型号",
        prop: "specificationModel",
        width: 220,
        width: 120,
    },
    {
        label: "绑定机器",
        prop: "speculativeTradingName",
        width: 160,
    },
    {
        label: "单位",
@@ -104,6 +146,32 @@
        label: "录入日期",
        prop: "entryDate",
        width: 120,
    },
    {
        label: "状态",
        prop: "status",
        dataType: "tag",
        formatType: (params) => {
            if (params == '生产中') {
                return "warning";
            } else if (params == '未开始') {
                return "danger";
            } else {
                return "success";
            }
        },
    },
    {
        label: "生产进度",
        prop: "progress",
        formatData: (cellValue) => {
            // å¦‚果值为空或undefined,显示空字符串
            if (cellValue === null || cellValue === undefined || cellValue === '') {
                return '';
            }
            // ç›´æŽ¥åœ¨æ•°å­—后面添加百分号
            return `${cellValue}%`;
        }
    },
    {
        label: "数量",
@@ -118,6 +186,7 @@
        label: "待排数量",
        prop: "pendingQuantity",
        width: 100,
        fixed: 'right',
    },
]);
const tableData = ref([]);
@@ -129,7 +198,140 @@
    total: 0,
});
const formDia = ref()
const autoDispatchDia = ref()
const { proxy } = getCurrentInstance()
// ç‚’机数据
const machineData = reactive({
    "炒机1": { workLoad: 0, currentWorkLoad: 0, vacant: 0 },
    "炒机2": { workLoad: 0, currentWorkLoad: 0, vacant: 0 },
    "炒机3": { workLoad: 0, currentWorkLoad: 0, vacant: 0 },
    "炒机4": { workLoad: 0, currentWorkLoad: 0, vacant: 0 }
})
// ç‚’机配置数组
const machines = [
    { id: 1, name: '炒机1' },
    { id: 2, name: '炒机2' },
    { id: 3, name: '炒机3' },
    { id: 4, name: '炒机4' }
]
// ä¿å­˜ç‚’机总量设置
const saveMachineTotals = () => {
    // éªŒè¯æŸè€—率是否已选择
    if (rate.value === null || rate.value === undefined || isNaN(rate.value)) {
        proxy.$message.warning('请选择损耗率');
        return;
    }
    // æž„造保存数据数组,使用machines数组循环构建
    const saveData = machines.map(machine => {
        const saveItem = {
            name: machine.name, // ç‚’机名称
            workLoad: machineData[machine.name].workLoad,  // æ€»é‡
            currentWorkLoad: machineData[machine.name].currentWorkLoad, //  é¢„计投入量
            vacant: machineData[machine.name].vacant   // ç©ºä½™é‡
        };
        // å¦‚果是修改操作,需要传递id字段
        if (hasQueryData.value) {
            const queryData = getMachineQueryData(machine.id);
            if (queryData && queryData.id) {
                saveItem.id = queryData.id;
            }
        }
        return saveItem;
    });
    // æž„造损耗率数据
    const rateData = {
        rate: rate.value
    };
    // å¦‚果有ID,说明是修改操作
    if (rateId.value) {
        rateData.id = rateId.value;
    }
    // æ ¹æ®æ˜¯å¦æœ‰æŸ¥è¯¢æ•°æ®å†³å®šè°ƒç”¨æ–°å¢žæŽ¥å£è¿˜æ˜¯ä¿®æ”¹æŽ¥å£
    const saveApi = hasQueryData.value ? updateSpeculatTrading : addSpeculatTrading;
    const successMessage = hasQueryData.value ? '炒机设置修改成功' : '炒机设置新增成功';
    // æ ¹æ®æ˜¯å¦æœ‰ID决定调用新增接口还是修改接口
    const rateApi = rateId.value ? updateLossRate : addLossRate;
    const rateSuccessMessage = rateId.value ? '损耗率修改成功' : '损耗率新增成功';
    // å¹¶è¡Œè°ƒç”¨ä¸¤ä¸ªæŽ¥å£
    Promise.all([
        saveApi(saveData),
        rateApi(rateData)
    ]).then(([saveRes, rateRes]) => {
        proxy.$message.success(successMessage);
        proxy.$message.success(rateSuccessMessage);
        // ä¿å­˜æˆåŠŸåŽï¼Œè®¾ç½®hasQueryData为true,下次保存将调用修改接口
        if (!hasQueryData.value) {
            hasQueryData.value = true;
        }
        // å¦‚果返回了ID,保存起来
        if (rateRes && rateRes.data && rateRes.data.id) {
            rateId.value = rateRes.data.id;
        }
        // ä¿å­˜æˆåŠŸåŽé‡æ–°è°ƒç”¨æŸ¥è¯¢é¡µé¢
        getList();
    }).catch(err => {
        proxy.$message.error('保存失败');
        console.error('保存失败:', err);
    });
}
// èŽ·å–ç‚’æœºæŸ¥è¯¢æ•°æ®
const machineQueryData = ref([]);
const getMachineQueryData = (machineId) => {
    return machineQueryData.value.find(item => item.id === machineId);
};
const getMachineIndex = (item) => {
    // å…¼å®¹å¤šç§å­—段命名,返回 1-4 ä¹‹ä¸€ï¼Œå¦åˆ™è¿”回 0(未知)
    const candidates = [item.machineId, item.machineNo, item.machine, item.deviceNo, item.deviceId]
    for (const v of candidates) {
        if (v === undefined || v === null) continue
        const n = Number(String(v).replace(/[^\d]/g, "")) // æŠ½å–æ•°å­—
        if ([1,2,3,4].includes(n)) return n
    }
    return 0
}
const computeTodaySummary = () => {
    const todayStr = dayjs().format("YYYY-MM-DD")
    // é‡ç½®æ‰€æœ‰ç‚’机数据
    machines.forEach(machine => {
        machineData[machine.name] = { workLoad: 0, currentWorkLoad: 0, vacant: 0 }
    })
    tableData.value.forEach(item => {
        // ä»…统计当天
        const isToday = dayjs(item.entryDate).format("YYYY-MM-DD") === todayStr
        if (!isToday) return
        // ä½¿ç”¨æ­£ç¡®çš„字段名:workLoad(炒机工作量), currentWorkLoad(炒机正在工作量)
        const workLoad = Number(item.workLoad) || 0
        const currentWorkLoad = Number(item.currentWorkLoad) || 0
        const machineName = item.speculativeTradingName || '炒机1'
        if (machineData[machineName]) {
            machineData[machineName].workLoad += workLoad
            machineData[machineName].currentWorkLoad += currentWorkLoad
            machineData[machineName].vacant = machineData[machineName].workLoad - machineData[machineName].currentWorkLoad
        }
    })
}
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
@@ -137,6 +339,56 @@
    page.current = 1;
    getList();
};
// æ˜¯å¦æœ‰æŸ¥è¯¢æ•°æ®
const hasQueryData = ref(false)
// æŸè€—率
const rate = ref(6)
// æŸè€—率ID
const rateId = ref(null)
// èŽ·å–ç‚’æœºæ­£åœ¨å·¥ä½œé‡æ•°æ®
const getMachineProductionData = () => {
    schedulingList().then((res) => {
        // å¤„理炒机正在工作量数据
        if (res.data && Array.isArray(res.data)) {
            // è®¾ç½®æ˜¯å¦æœ‰æŸ¥è¯¢æ•°æ®
            hasQueryData.value = res.data.length > 0
            // ä¿å­˜æŸ¥è¯¢æ•°æ®åˆ°machineQueryData
            machineQueryData.value = res.data;
            // é‡ç½®æ‰€æœ‰ç‚’机数据
            machines.forEach(machine => {
                machineData[machine.name] = { workLoad: 0, currentWorkLoad: 0, vacant: 0 }
            });
            // éåŽ†æ•°æ®ï¼Œæ ¹æ®æŸ¥è¯¢è¿”å›žçš„æ•°æ®ç»“æž„å¤„ç†
            res.data.forEach(item => {
                // æ ¹æ®name字段确定炒机
                const machineName = item.name || '炒机1';
                if (machineData[machineName]) {
                    // å¦‚果查询数据中有workLoad,则初始化炒机总量
                    if (item.workLoad !== null && item.workLoad !== undefined) {
                        machineData[machineName].workLoad = Number(item.workLoad) || 0;
                    }
                    // å¦‚果查询数据中有currentWorkLoad,则设置正在工作量
                    if (item.currentWorkLoad !== null && item.currentWorkLoad !== undefined) {
                        machineData[machineName].currentWorkLoad = Number(item.currentWorkLoad) || 0;
                    }
                    // è®¡ç®—空余工作量
                    machineData[machineName].vacant = machineData[machineName].workLoad - machineData[machineName].currentWorkLoad;
                }
            });
        }
    }).catch(err => {
        console.error('获取炒机正在工作量数据失败:', err);
    });
};
const changeDaterange = (value) => {
    if (value) {
        searchForm.value.entryDateStart = value[0];
@@ -165,9 +417,33 @@
            pendingQuantity: (Number(item.quantity) || 0) - (Number(item.schedulingNum) || 0)
        }));
        page.total = res.data.total;
        computeTodaySummary()
        // åŒæ—¶èŽ·å–ç‚’æœºæ­£åœ¨å·¥ä½œé‡æ•°æ®
        getMachineProductionData();
        // èŽ·å–æŸè€—çŽ‡æ•°æ®
        getLossRateData();
    }).catch(() => {
        tableLoading.value = false;
    })
};
// èŽ·å–æŸè€—çŽ‡æ•°æ®
const getLossRateData = () => {
    getLossRate().then((res) => {
        const data = res.data || res;
        if (data && data.rate !== undefined && data.rate !== null) {
            rate.value = Number(data.rate); // ç¡®ä¿è½¬æ¢ä¸ºæ•°å­—
            rateId.value = data.id || null;
        } else {
            rate.value = 6;
            rateId.value = null;
        }
    }).catch(err => {
        console.error('获取损耗率数据失败:', err);
        rate.value = 6;
        rateId.value = null;
    });
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
@@ -189,6 +465,26 @@
    })
};
// æ‰“开自动派工弹框
const openAutoDispatch = () => {
    if (selectedRows.value.length === 0) {
        proxy.$message.error("请选择至少一条数据");
        return;
    }
    // è¿‡æ»¤æŽ‰å¾…排产数量为0的数据
    const validRows = selectedRows.value.filter(row => row.pendingQuantity > 0);
    if (validRows.length === 0) {
        proxy.$message.warning("选中的数据无需派工");
        return;
    }
    nextTick(() => {
        autoDispatchDia.value?.openDialog('auto', validRows)
    })
};
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
@@ -206,7 +502,129 @@
onMounted(() => {
    getList();
    getLossRateData();
});
</script>
<style scoped></style>
<style scoped>
.summary-bar{
    display: flex;
    gap: 16px;
    margin: 10px 0 16px 0;
}
.summary-item{
    background: #f5f7fa;
    border: 1px solid #ebeef5;
    border-radius: 6px;
    padding: 10px 16px;
    min-width: 160px;
}
.summary-label{
    color: #909399;
    font-size: 12px;
    margin-bottom: 6px;
}
.summary-value{
    color: #303133;
    font-size: 20px;
    font-weight: 600;
}
.summary-control{
    display: flex;
    align-items: center;
    height: 28px;
}
.machines-grid{
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
    margin-bottom: 20px;
    padding: 16px;
    background: #f8f9fa;
    border-radius: 8px;
    border: 1px solid #e9ecef;
}
.machine-card{
    border: 1px solid #dee2e6;
    border-radius: 8px;
    padding: 16px;
    background: #fff;
    box-shadow: 0 2px 4px rgba(0,0,0,0.05);
    transition: all 0.3s ease;
}
.machine-card:hover{
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.machine-title{
    font-weight: 600;
    font-size: 16px;
    margin-bottom: 12px;
    color: #2c3e50;
    text-align: center;
    padding-bottom: 8px;
    border-bottom: 2px solid #3498db;
}
.machine-metrics{
    display: flex;
    flex-direction: column;
    gap: 10px;
    color: #495057;
}
.machine-control{
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 0;
    border-bottom: 1px solid #f1f3f4;
}
.machine-control span{
    font-size: 14px;
    white-space: nowrap;
    color: #6c757d;
    font-weight: 500;
}
.machine-metrics > div:not(.machine-control) {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 4px 0;
    font-size: 14px;
}
.machine-metrics > div:not(.machine-control) span:first-child {
    color: #6c757d;
}
.machine-metrics > div:not(.machine-control) span:last-child {
    font-weight: 600;
    color: #2c3e50;
}
.save-button-container{
    grid-column: 1 / -1;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 16px;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid #e9ecef;
}
.loss-rate-container{
    display: flex;
    align-items: center;
    gap: 8px;
}
.loss-rate-label{
    font-size: 14px;
    color: #6c757d;
    font-weight: 500;
    white-space: nowrap;
}
</style>
src/views/productionManagement/productionOrder/ProcessRouteItemForm.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,555 @@
<template>
  <div>
    <el-dialog v-model="isShow"
               title="工艺路线项目"
               width="800px"
               @close="closeModal">
      <div class="operate-button">
        <el-button type="primary"
                   @click="isShowProductSelectDialog = true"
                   class="mb5"
                   style="margin-bottom: 10px;">
          é€‰æ‹©äº§å“
        </el-button>
        <el-switch v-model="isTable"
                   inline-prompt
                   active-text="表格"
                   inactive-text="列表"
                   @change="handleViewChange" />
      </div>
      <el-table v-if="isTable"
                ref="multipleTable"
                v-loading="tableLoading"
                border
                :data="routeItems"
                :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
                row-key="id"
                tooltip-effect="dark"
                class="lims-table"
                style="cursor: move;">
        <el-table-column align="center"
                         label="序号"
                         width="60">
          <template #default="scope">
            {{ scope.$index + 1 }}
          </template>
        </el-table-column>
        <el-table-column v-for="(item, index) in tableColumn"
                         :key="index"
                         :label="item.label"
                         :width="item.width"
                         show-overflow-tooltip>
          <template #default="scope"
                    v-if="item.dataType === 'action'">
            <el-button v-for="(op, opIndex) in item.operation"
                       :key="opIndex"
                       :type="op.type"
                       :link="op.link"
                       size="small"
                       @click.stop="op.clickFun(scope.row)">
              {{ op.name }}
            </el-button>
          </template>
          <template #default="scope"
                    v-else>
            <template v-if="item.prop === 'processId'">
              <el-select v-model="scope.row[item.prop]"
                         style="width: 100%;"
                         @mousedown.stop>
                <el-option v-for="process in processOptions"
                           :key="process.id"
                           :label="process.name"
                           :value="process.id" />
              </el-select>
            </template>
            <template v-else>
              {{ scope.row[item.prop] || '-' }}
            </template>
          </template>
        </el-table-column>
      </el-table>
      <!-- ä½¿ç”¨æ™®é€šdiv替代el-steps -->
      <div v-else
           ref="stepsContainer"
           class="mb5 custom-steps"
           style="padding: 10px 0; display: flex; flex-wrap: nowrap; gap: 20px; align-items: flex-start;">
        <div v-for="(item, index) in routeItems"
             :key="item.id"
             class="custom-step draggable-step"
             :data-id="item.id"
             style="cursor: move; flex: 0 0 auto; min-width: 220px;">
          <div class="step-content">
            <div class="step-number">{{ index + 1 }}</div>
            <el-card :header="item.productName"
                     class="step-card"
                     style="cursor: move;">
              <div class="step-card-content">
                <p>{{ item.model }}</p>
                <p>{{ item.unit }}</p>
                <el-select v-model="item.processId"
                           style="width: 100%;"
                           @mousedown.stop>
                  <el-option v-for="process in processOptions"
                             :key="process.id"
                             :label="process.name"
                             :value="process.id" />
                </el-select>
              </div>
              <template #footer>
                <div class="step-card-footer">
                  <el-button type="danger"
                             link
                             size="small"
                             @click.stop="removeItemByID(item.id)">删除</el-button>
                </div>
              </template>
            </el-card>
          </div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary"
                     @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <ProductSelectDialog v-model="isShowProductSelectDialog"
                         @confirm="handelSelectProducts" />
  </div>
</template>
<script setup>
  import {
    ref,
    computed,
    getCurrentInstance,
    onMounted,
    onUnmounted,
    nextTick,
  } from "vue";
  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
  import {
    findProductProcessRouteItemList,
    addOrUpdateProductProcessRouteItem,
    deleteRouteItem,
  } from "@/api/productionManagement/productProcessRoute.js";
  import { processList } from "@/api/productionManagement/productionProcess.js";
  import Sortable from "sortablejs";
  const props = defineProps({
    visible: {
      type: Boolean,
      required: true,
      default: false,
    },
    record: {
      type: Object,
      required: true,
      default: () => ({}),
    },
  });
  const emit = defineEmits(["update:visible", "completed"]);
  const processOptions = ref([]);
  const tableLoading = ref(false);
  const isShowProductSelectDialog = ref(false);
  const routeItems = ref([]);
  let tableSortable = null;
  let stepsSortable = null;
  const multipleTable = ref(null);
  const stepsContainer = ref(null);
  const isTable = ref(true);
  const isShow = computed({
    get() {
      return props.visible;
    },
    set(val) {
      emit("update:visible", val);
    },
  });
  const tableColumn = ref([
    { label: "产品名称", prop: "productName", width: 180 },
    { label: "规格名称", prop: "model", width: 150 },
    { label: "单位", prop: "unit", width: 80 },
    { label: "工序名称", prop: "processId", width: 180 },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      fixed: "right",
      width: 100,
      operation: [
        {
          name: "删除",
          type: "danger",
          link: true,
          clickFun: row => {
            console.log(row.id, "删除");
            const dragSortx = routeItems.value.findIndex(
              item => item.dragSort === row.dragSort
            );
            const idx = routeItems.value.findIndex(item => item.id === row.id);
            console.log(idx, "idx");
            if (row.id) {
              deleteRouteItemByIds({ id: row.id }, idx);
            } else {
              removeItem(dragSortx);
            }
          },
        },
      ],
    },
  ]);
  const removeItem = index => {
    console.log("软删除", index);
    routeItems.value.splice(index, 1);
    updateDragSort();
    nextTick(() => initSortable());
  };
  const removeItemByID = id => {
    const idx = routeItems.value.findIndex(item => item.id === id);
    if (idx > -1) {
      routeItems.value.splice(idx, 1);
      updateDragSort();
      nextTick(() => initSortable());
    }
  };
  const deleteRouteItemByIds = (ids, index) => {
    deleteRouteItem(ids).then(res => {
      routeItems.value.splice(index, 1);
      updateDragSort();
      nextTick(() => initSortable());
    });
  };
  const closeModal = () => {
    isShow.value = false;
  };
  const updateDragSort = () => {
    routeItems.value.forEach((item, index) => {
      item.dragSort = index + 1;
    });
    routeItems.value = [...routeItems.value];
    console.log("更新后的数组:", routeItems.value);
  };
  const handelSelectProducts = products => {
    destroySortable();
    // è®¡ç®—æ–°çš„dragSort值起始点
    const maxDragSort =
      routeItems.value.length > 0
        ? Math.max(...routeItems.value.map(item => item.dragSort || 0))
        : 0;
    const newData = products.map(({ id, ...product }, index) => ({
      ...product,
      productModelId: id,
      routeId: props.record.id,
      // id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
      processId: undefined,
      dragSort: maxDragSort + index + 1,
    }));
    console.log("选择产品前数组:", routeItems.value);
    routeItems.value.push(...newData);
    updateDragSort();
    console.log("选择产品后数组:", routeItems.value);
    // å»¶è¿Ÿåˆå§‹åŒ–,确保DOM完全渲染
    nextTick(() => {
      // å¼ºåˆ¶é‡æ–°æ¸²æŸ“组件
      if (proxy?.$forceUpdate) {
        proxy.$forceUpdate();
      }
      const temp = [...routeItems.value];
      routeItems.value = [];
      nextTick(() => {
        routeItems.value = temp;
        initSortable();
      });
    });
  };
  const findProcessRouteItems = () => {
    tableLoading.value = true;
    findProductProcessRouteItemList({ orderId: props.record.id })
      .then(res => {
        tableLoading.value = false;
        routeItems.value = res.data.map(item => ({
          ...item,
          processId: item.processId === 0 ? undefined : item.processId,
        }));
        // å»¶è¿Ÿåˆå§‹åŒ–,确保DOM完全渲染
        nextTick(() => {
          setTimeout(() => initSortable(), 100);
        });
      })
      .catch(err => {
        tableLoading.value = false;
        console.error("获取列表失败:", err);
      });
  };
  const findProcessList = () => {
    processList({})
      .then(res => {
        processOptions.value = res.data;
      })
      .catch(err => {
        console.error("获取工序失败:", err);
      });
  };
  const { proxy } = getCurrentInstance() || {};
  const handleSubmit = () => {
    const hasEmptyProcess = routeItems.value.some(item => !item.processId);
    if (hasEmptyProcess) {
      proxy?.$modal?.msgError("请为所有项目选择工序");
      return;
    }
    addOrUpdateProductProcessRouteItem({
      routeId: props.record.id,
      processRouteItem: routeItems.value,
    })
      .then(res => {
        isShow.value = false;
        emit("completed");
        proxy?.$modal?.msgSuccess("提交成功");
      })
      .catch(err => {
        proxy?.$modal?.msgError(`提交失败:${err.msg || "网络异常"}`);
      });
  };
  const destroySortable = () => {
    if (tableSortable) {
      tableSortable.destroy();
      tableSortable = null;
    }
    if (stepsSortable) {
      stepsSortable.destroy();
      stepsSortable = null;
    }
  };
  const initSortable = () => {
    destroySortable();
    if (isTable.value) {
      if (!multipleTable.value) return;
      const tbody =
        multipleTable.value.$el.querySelector(".el-table__body tbody") ||
        multipleTable.value.$el.querySelector(
          ".el-table__body-wrapper > table > tbody"
        );
      if (!tbody) return;
      tableSortable = new Sortable(tbody, {
        animation: 150,
        ghostClass: "sortable-ghost",
        handle: ".el-table__row",
        filter: ".el-button, .el-select",
        onEnd: evt => {
          if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex])
            return;
          // ä½¿ç”¨æ•°ç»„ splice æ–¹æ³•重新排序,与表格模式保持一致
          const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
          routeItems.value.splice(evt.newIndex, 0, moveItem);
          updateDragSort();
          console.log("排序后数组:", routeItems.value);
        },
      });
    } else {
      if (!stepsContainer.value) return;
      // ä¿®æ”¹ï¼šç›´æŽ¥ä½¿ç”¨stepsContainer.value作为拖拽容器
      const stepsList = stepsContainer.value;
      if (!stepsList) {
        console.warn("未找到步骤条拖拽容器");
        return;
      }
      // ä¿®æ”¹ï¼šç®€åŒ–拖拽配置
      stepsSortable = new Sortable(stepsList, {
        animation: 150,
        ghostClass: "sortable-ghost",
        draggable: ".draggable-step", // å¯æ‹–拽元素
        handle: ".draggable-step, .step-card", // æ‹–拽手柄
        filter: ".el-button, .el-select, .el-input", // è¿‡æ»¤æŒ‰é’®/选择器
        forceFallback: true,
        fallbackClass: "sortable-fallback",
        preventOnFilter: true,
        scroll: true,
        scrollSensitivity: 30,
        scrollSpeed: 10,
        bubbleScroll: true,
        onEnd: evt => {
          if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex])
            return;
          // ä½¿ç”¨æ•°ç»„ splice æ–¹æ³•重新排序
          const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
          routeItems.value.splice(evt.newIndex, 0, moveItem);
          updateDragSort();
        },
      });
      // è°ƒè¯•:打印容器和实例,确认绑定成功
      console.log("步骤条拖拽容器:", stepsList);
      console.log("Sortable实例:", stepsSortable);
    }
  };
  const handleViewChange = () => {
    destroySortable();
    // å»¶è¿Ÿåˆå§‹åŒ–,确保视图切换后DOM完全渲染
    nextTick(() => {
      setTimeout(() => initSortable(), 100);
    });
  };
  onMounted(() => {
    findProcessRouteItems();
    findProcessList();
  });
  onUnmounted(() => {
    destroySortable();
  });
  defineExpose({
    closeModal,
    handleSubmit,
    isShow,
  });
</script>
<style scoped>
  :deep(.sortable-ghost) {
    opacity: 0.6;
    background-color: #f5f7fa !important;
  }
  :deep(.el-table__row) {
    transition: background-color 0.2s;
  }
  :deep(.el-table__row:hover) {
    background-color: #f9fafc !important;
  }
  :deep(.el-card__footer) {
    padding: 0 !important;
  }
  .operate-button {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  /* ä¿®æ”¹ï¼šè‡ªå®šä¹‰æ­¥éª¤æ¡å®¹å™¨æ ·å¼ */
  .custom-steps {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 20px;
    min-height: 100px;
  }
  /* ä¿®æ”¹ï¼šè‡ªå®šä¹‰æ­¥éª¤é¡¹æ ·å¼ */
  .custom-step {
    cursor: move !important;
    padding: 8px;
    position: relative;
    transition: all 0.2s ease;
    flex: 0 0 auto;
    min-width: 220px;
    touch-action: none;
  }
  /* æ‹–拽悬浮样式,提示可拖拽 */
  .custom-step:hover {
    background-color: rgba(64, 158, 255, 0.05);
    transform: translateY(-2px);
  }
  .sortable-ghost {
    opacity: 0.4;
    background-color: #f5f7fa !important;
    border: 2px dashed #409eff;
    margin: 10px;
    transform: scale(1.02);
  }
  .sortable-fallback {
    opacity: 0.9;
    background-color: #f5f7fa;
    border: 1px solid #409eff;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    transform: rotate(2deg);
    margin: 10px;
  }
  .step-card {
    cursor: move !important;
    transition: box-shadow 0.2s ease;
    user-select: none;
    -webkit-user-select: none;
    pointer-events: auto;
    margin: 10px;
    height: 240px;
  }
  .step-card:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  }
  .step-content {
    width: 220px;
    user-select: none;
  }
  .step-card-content {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .step-card-footer {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding: 10px;
  }
  /* è‡ªå®šä¹‰åºå·æ ·å¼ä¼˜åŒ– */
  .step-number {
    font-weight: bold;
    text-align: center;
    width: 36px;
    height: 36px;
    line-height: 36px;
    margin: 0 auto 10px;
    background: #409eff;
    color: #fff;
    border-radius: 50%;
    font-size: 14px;
  }
</style>
src/views/productionManagement/productionOrder/index.vue
@@ -1,198 +1,211 @@
<template>
    <div class="app-container">
        <div class="search_form">
            <div>
                <span class="search_title">客户名称:</span>
                <el-input
                    v-model="searchForm.customerName"
                    style="width: 240px"
                    placeholder="请输入"
                    @change="handleQuery"
                    clearable
                    prefix-icon="Search"
                />
                <span class="search_title ml10">项目名称:</span>
                <el-input
                    v-model="searchForm.projectName"
                    style="width: 240px"
                    placeholder="请输入"
                    @change="handleQuery"
                    clearable
                    prefix-icon="Search"
                />
                <span class="search_title ml10">录入日期:</span>
                <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                                                placeholder="请选择" clearable @change="changeDaterange" />
                <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
                >搜索</el-button
                >
            </div>
            <div>
                <el-button @click="handleOut">导出</el-button>
            </div>
        </div>
        <div class="table_list">
            <PIMTable
                rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :tableLoading="tableLoading"
                @pagination="pagination"
            ></PIMTable>
        </div>
    </div>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="客户名称:">
          <el-input v-model="searchForm.customerName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 200px;"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="合同号:">
          <el-input v-model="searchForm.salesContractNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 200px;"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="项目名称:">
          <el-input v-model="searchForm.projectName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 200px;"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="产品名称:">
          <el-input v-model="searchForm.productCategory"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 200px;"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="规格:">
          <el-input v-model="searchForm.specificationModel"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 200px;"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery">搜索</el-button>
        </el-form-item>
      </el-form>
      <div>
        <el-button @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :tableLoading="tableLoading"
                @pagination="pagination"></PIMTable>
    </div>
    <process-route-item-form v-if="isShowItemModal"
                             v-model:visible="isShowItemModal"
                             :record="record"
                             @completed="getList" />
  </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import { ElMessageBox } from "element-plus";
import dayjs from "dayjs";
import {schedulingListPage} from "@/api/productionManagement/productionOrder.js";
const { proxy } = getCurrentInstance();
  import { onMounted, ref } from "vue";
  import { ElMessageBox } from "element-plus";
  import dayjs from "dayjs";
  import { productOrderListPage } from "@/api/productionManagement/productionOrder.js";
  const { proxy } = getCurrentInstance();
  import ProcessRouteItemForm from "@/views/productionManagement/productionOrder/ProcessRouteItemForm.vue";
const tableColumn = ref([
    {
        label: "录入日期",
        prop: "entryDate",
        width: 120,
    },
    {
        label: "合同号",
        prop: "salesContractNo",
        width: 220,
    },
    {
        label: "客户合同号",
        prop: "customerContractNo",
        width: 250,
    },
    {
        label: "客户名称",
        prop: "customerName",
        width: 250,
    },
    {
        label: "项目名称",
        prop: "projectName",
        width:300
    },
    {
        label: "生产状态",
        prop: "status",
        dataType: "tag",
        formatType: (params) => {
            if (params == '未完成') {
                return "danger";
            } else if (params == '已完成') {
                return "success";
            } else {
                return null;
            }
        },
    },
    {
        label: "产品大类",
        prop: "productCategory",
        width: 160,
    },
    {
        label: "规格型号",
        prop: "specificationModel",
        width: 220,
    },
    {
        label: "单位",
        prop: "unit",
        width:90
    },
    {
        label: "数量",
        prop: "quantity",
    },
    {
        label: "排产数量",
        prop: "schedulingNum",
        width: 100,
    },
    {
        label: "完工数量",
        prop: "successNum",
        width: 100,
    },
]);
const tableData = ref([]);
const tableLoading = ref(false);
const page = reactive({
    current: 1,
    size: 100,
    total: 0,
});
  const tableColumn = ref([
    {
      label: "生产订单号",
      prop: "npsNo",
      width: 120,
    },
    {
      label: "销售合同号",
      prop: "salesContractNo",
      width: 220,
    },
    {
      label: "项目名称",
      prop: "projectName",
      width: 300,
    },
    {
      label: "客户名称",
      prop: "customerName",
      width: 250,
    },
    {
      label: "产品名称",
      prop: "productCategory",
      width: 250,
    },
    {
      label: "规格",
      prop: "specificationModel",
      width: 250,
    },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      fixed: "right",
      width: 200,
      operation: [
        {
          name: "工艺路线",
          type: "text",
          clickFun: row => {
            showRouteItemModal(row);
          },
        },
      ],
    },
  ]);
  const tableData = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
const data = reactive({
    searchForm: {
        customerName: "",
        projectName: "",
        entryDate: null, // å½•入日期
        entryDateStart: undefined,
        entryDateEnd: undefined,
    },
});
const { searchForm } = toRefs(data);
  const data = reactive({
    searchForm: {
      customerName: "",
      salesContractNo: "",
      projectName: "",
      productCategory: "",
      specificationModel: "",
    },
  });
  const { searchForm } = toRefs(data);
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
    page.current = 1;
    getList();
};
const pagination = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const changeDaterange = (value) => {
    if (value) {
        searchForm.value.entryDateStart = value[0];
        searchForm.value.entryDateEnd = value[1];
    } else {
        searchForm.value.entryDateStart = undefined;
        searchForm.value.entryDateEnd = undefined;
    }
    handleQuery();
};
const getList = () => {
    tableLoading.value = true;
    // æž„造一个新的对象,不包含entryDate字段
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined
    schedulingListPage(params).then((res) => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
    }).catch(() => {
        tableLoading.value = false;
    })
};
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const pagination = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const changeDaterange = value => {
    if (value) {
      searchForm.value.entryDateStart = value[0];
      searchForm.value.entryDateEnd = value[1];
    } else {
      searchForm.value.entryDateStart = undefined;
      searchForm.value.entryDateEnd = undefined;
    }
    handleQuery();
  };
  const getList = () => {
    tableLoading.value = true;
    // æž„造一个新的对象,不包含entryDate字段
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined;
    productOrderListPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download("/salesLedger/scheduling/export", {}, "生产订单.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
  const isShowItemModal = ref(false);
  const record = ref({});
  const showRouteItemModal = row => {
    isShowItemModal.value = true;
    record.value = row;
  };
onMounted(() => {
    getList();
});
  // å¯¼å‡º
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        proxy.download("/salesLedger/scheduling/export", {}, "生产订单.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  const handleConfirmRoute = () => {};
  onMounted(() => {
    getList();
  });
</script>
<style scoped lang="scss"></style>
src/views/productionManagement/productionProcess/Edit.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="编辑工序"
        width="400"
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
        <el-form-item
            label="工序名称:"
            prop="name"
            :rules="[
                {
                required: true,
                message: '请输入工序名称',
              },
              {
                max: 100,
                message: '最多100个字符',
              }
            ]">
          <el-input v-model="formState.name" />
        </el-form-item>
        <el-form-item label="工资定额" prop="salaryQuota">
          <el-input v-model="formState.salaryQuota" type="number" :step="0.001" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="formState.remark" type="textarea" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, computed, getCurrentInstance } from "vue";
import {update} from "@/api/productionManagement/productionProcess.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  record: {
    type: Object,
    required: true,
  }
});
const emit = defineEmits(['update:visible', 'completed']);
// å“åº”式数据(替代选项式的 data)
const formState = ref({
  id: props.record.id,
  name: props.record.name,
  remark: props.record.remark,
  salaryQuota: props.record.salaryQuota,
});
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
let { proxy } = getCurrentInstance()
const closeModal = () => {
  isShow.value = false;
};
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      update(formState.value).then(res => {
        // å…³é—­æ¨¡æ€æ¡†
        isShow.value = false;
        // å‘ŠçŸ¥çˆ¶ç»„件已完成
        emit('completed');
        proxy.$modal.msgSuccess("提交成功");
      })
    }
  })
};
defineExpose({
  closeModal,
  handleSubmit,
  isShow,
});
</script>
src/views/productionManagement/productionProcess/New.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="新增工序"
        width="400"
        @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
        <el-form-item
            label="工序名称:"
            prop="name"
            :rules="[
                {
                required: true,
                message: '请输入工序名称',
              },
              {
                max: 100,
                message: '最多100个字符',
              }
            ]">
          <el-input v-model="formState.name" />
        </el-form-item>
        <el-form-item label="工资定额" prop="salaryQuota">
          <el-input v-model="formState.salaryQuota" type="number" :step="0.001" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="formState.remark" type="textarea" />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
          <el-button @click="closeModal">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, computed, getCurrentInstance } from "vue";
import {add} from "@/api/productionManagement/productionProcess.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
});
const emit = defineEmits(['update:visible', 'completed']);
// å“åº”式数据(替代选项式的 data)
const formState = ref({
  name: '',
  remark: '',
  salaryQuota:  '',
});
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
let { proxy } = getCurrentInstance()
const closeModal = () => {
  isShow.value = false;
};
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      add(formState.value).then(res => {
        // å…³é—­æ¨¡æ€æ¡†
        isShow.value = false;
        // å‘ŠçŸ¥çˆ¶ç»„件已完成
        emit('completed');
        proxy.$modal.msgSuccess("提交成功");
      })
    }
  })
};
defineExpose({
  closeModal,
  handleSubmit,
  isShow,
});
</script>
src/views/productionManagement/productionProcess/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,189 @@
<template>
    <div class="app-container">
        <div class="search_form">
            <el-form :model="searchForm" :inline="true">
                <el-form-item label="工序名称:">
                    <el-input v-model="searchForm.name" placeholder="请输入" clearable prefix-icon="Search"
                                        style="width: 200px;"
                                        @change="handleQuery" />
                </el-form-item>
                <el-form-item label="工序编号:">
                    <el-input v-model="searchForm.no" placeholder="请输入" clearable prefix-icon="Search"
                                        style="width: 200px;"
                                        @change="handleQuery" />
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="handleQuery">搜索</el-button>
                </el-form-item>
            </el-form>
        </div>
        <div class="table_list">
            <div style="text-align: right" class="mb10">
                <el-button type="primary" @click="showNewModal">新增工序</el-button>
                <el-button type="danger" @click="handleDelete" :disabled="selectedRows.length === 0" plain>删除工序</el-button>
            </div>
            <PIMTable
                rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :isSelection="true"
                @selection-change="handleSelectionChange"
                :tableLoading="tableLoading"
                @pagination="pagination"
                :total="page.total"
            ></PIMTable>
        </div>
        <new-process
      v-if="isShowNewModal"
      v-model:visible="isShowNewModal"
      @completed="getList"
    />
    <edit-process
      v-if="isShowEditModal"
      v-model:visible="isShowEditModal"
      :record="record"
      @completed="getList"
    />
    </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import NewProcess from "@/views/productionManagement/productionProcess/New.vue";
import EditProcess from "@/views/productionManagement/productionProcess/Edit.vue";
import {listPage, del} from "@/api/productionManagement/productionProcess.js";
const data = reactive({
    searchForm: {
        name: "",
        no: "",
    },
});
const { searchForm } = toRefs(data);
const tableColumn = ref([
    {
        label: "工序名称",
        prop: "name",
    },
    {
        label: "工序编号",
        prop: "no",
    },
    {
        label: "工资定额",
        prop: "salaryQuota",
    },
    {
        label: "备注",
        prop: "remark",
    },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 280,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          showEditModal(row);
        }
      }
    ]
  }
]);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const isShowNewModal = ref(false);
const isShowEditModal = ref(false);
const record = ref({});
const page = reactive({
    current: 1,
    size: 100,
    total: 0,
});
const { proxy } = getCurrentInstance()
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
    page.current = 1;
    getList();
};
const pagination = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList = () => {
    tableLoading.value = true;
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined
  listPage(params).then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records.map(item => ({
            ...item,
        }));
        page.total = res.data.total;
    }).catch(err => {
        tableLoading.value = false;
    })
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
    selectedRows.value = selection;
};
// æ‰“开新增弹框
const showNewModal = () => {
  isShowNewModal.value = true
};
const showEditModal = (row) => {
  isShowEditModal.value = true
  record.value = row
};
// åˆ é™¤
function handleDelete() {
  const no = selectedRows.value.map((item) => item.no);
  const ids = selectedRows.value.map((item) => item.id);
  proxy.$modal
      .confirm('是否确认删除工序编号为"' + no + '"的数据项?')
      .then(function () {
        return del(ids);
      })
      .then(() => {
        getList();
        proxy.$modal.msgSuccess("删除成功");
      })
      .catch(() => {});
}
// å¯¼å‡º
// const handleOut = () => {
//     ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
//         confirmButtonText: "确认",
//         cancelButtonText: "取消",
//         type: "warning",
//     })
//         .then(() => {
//             proxy.download("/salesLedger/scheduling/exportTwo", {}, "工序排产.xlsx");
//         })
//         .catch(() => {
//             proxy.$modal.msg("已取消");
//         });
// };
onMounted(() => {
    getList();
});
</script>
<style scoped></style>
src/views/productionManagement/productionReporting/Input.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="投入"
        @close="closeModal"
    >
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="data"
          :page="page"
          :tableLoading="tableLoading"
          @pagination="pagination"
      ></PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="closeModal">关闭</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {ref, computed, onMounted} from "vue";
import { productionProductInputListPage } from "@/api/productionManagement/productionProductInput";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  productionProductMainId: {
    type: Number,
    required: true,
  },
});
const emit = defineEmits(['update:visible', 'completed']);
const page = reactive({
  current: 1,
  size: 100,
  total: 0
});
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  fetchData();
};
const tableLoading = ref(false);
const tableColumn = [
  {
    label: '报工单号',
    prop: 'productNo',
  },
  {
    label: '产品型号',
    prop: 'model',
  },
  {
    label: '投入数量',
    prop: 'quantity',
  },
]
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
const data = ref([])
const closeModal = () => {
  isShow.value = false;
};
const fetchData = () => {
  tableLoading.value = true;
  const params = { productMainId: props.productionProductMainId, ...page };
  productionProductInputListPage(params).then(res => {
    tableLoading.value = false;
    data.value = res.data.records;
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
};
defineExpose({
  closeModal,
  isShow,
});
onMounted(() => {
  fetchData()
})
</script>
src/views/productionManagement/productionReporting/Output.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="产出"
        @close="closeModal"
    >
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="data"
          :page="page"
          :tableLoading="tableLoading"
          @pagination="pagination"
      ></PIMTable>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="closeModal">关闭</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {ref, computed, onMounted} from "vue";
import { productionProductOutputListPage } from "@/api/productionManagement/productionProductOutput.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  productionProductMainId: {
    type: Number,
    required: true,
  },
});
const emit = defineEmits(['update:visible', 'completed']);
const page = reactive({
  current: 1,
  size: 100,
  total: 0
});
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  fetchData();
};
const tableLoading = ref(false);
const tableColumn = [
  {
    label: '报工单号',
    prop: 'productNo',
  },
  {
    label: '产品型号',
    prop: 'model',
  },
  {
    label: '投入数量',
    prop: 'quantity',
  },
]
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
  },
});
const data = ref([])
const closeModal = () => {
  isShow.value = false;
};
const fetchData = () => {
  tableLoading.value = true;
  const params = { productMainId: props.productionProductMainId, ...page };
  productionProductOutputListPage(params).then(res => {
    tableLoading.value = false;
    data.value = res.data.records;
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
};
defineExpose({
  closeModal,
  isShow,
});
onMounted(() => {
  fetchData()
})
</script>
src/views/productionManagement/productionReporting/components/formDia.vue
@@ -13,8 +13,15 @@
              <el-input v-model="form.schedulingNum" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="本次生产数量:" prop="finishedNum">
                    <el-col :span="12">
                        <el-form-item label="待生产数量:" prop="pendingNum">
                            <el-input v-model="form.pendingNum" placeholder="请输入" clearable disabled/>
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="本次生产数量:" prop="finishedNum">
                            <el-input-number
                                v-model="form.finishedNum"
                                placeholder="请输入"
@@ -25,13 +32,18 @@
                                style="width: 100%"
                                @change="changeNum"
                            />
                        </el-form-item>
                    </el-col>
          <el-col :span="12">
            <el-form-item label="单价(元):" prop="unitPrice">
              <el-input v-model="form.unitPrice" placeholder="请输入" clearable @input="calculateTotalPrice"/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="待生产数量:" prop="pendingNum">
              <el-input :value="form.pendingNum !== undefined && form.pendingNum !== null ? Number(form.pendingNum).toFixed(2) : ''" placeholder="请输入" clearable disabled/>
            <el-form-item label="总价(元):" prop="totalPrice">
              <el-input v-model="form.totalPrice" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -42,6 +54,9 @@
                                v-model="form.schedulingUserId"
                                placeholder="选择人员"
                                style="width: 100%;"
                filterable
                default-first-option
                :reserve-keyword="false"
                            >
                                <el-option
                                    v-for="user in userList"
@@ -88,7 +103,6 @@
const userList = ref([])
const dialogFormVisible = ref(false);
const operationType = ref('')
const pendingFinishNum = ref(0) // å¤–部传入的待生产数量(剩余可报工数量)
const data = reactive({
  form: {
        successNum: "",
@@ -96,6 +110,8 @@
        finishedNum: "",
        schedulingUserId: "",
        schedulingDate: "",
        unitPrice: "",
        totalPrice: "",
  },
  rules: {
        schedulingNum: [{ required: true, message: "请输入", trigger: "blur" },],
@@ -111,21 +127,27 @@
        userList.value = res.data;
    });
    form.value = {...row}
  pendingFinishNum.value = Number(row?.pendingFinishNum || 0)
  // å°†å¾…生产数量带入表单,方便展示与校验,保留两位小数
  form.value.pendingNum = Number(pendingFinishNum.value.toFixed(2))
}
const changeNum = (value) => {
    const maxPending = Number(pendingFinishNum.value) || 0
    // ä¸Šé™ï¼šä¸èƒ½è¶…过待生产数量
    if (Number(value) > maxPending) {
        form.value.finishedNum = Number(maxPending.toFixed(2));
        proxy.$modal.msgWarning('本次生产数量不可大于待生产数量')
    if (value > form.value.schedulingNum) {
        form.value.finishedNum = form.value.schedulingNum;
        proxy.$modal.msgWarning('本次生产数量不可大于排产数量')
    }
    // é‡æ–°è®¡ç®—剩余待生产数量,保留两位小数
    const finishedNum = Number(form.value.finishedNum) || 0
    form.value.pendingNum = Number((maxPending - finishedNum).toFixed(2));
    form.value.pendingNum = form.value.schedulingNum - form.value.finishedNum;
    calculateTotalPrice();
}
// è®¡ç®—总价
const calculateTotalPrice = () => {
    const quantity = Number(form.value.finishedNum ?? 0);
    const unitPrice = Number(form.value.unitPrice ?? 0);
    if (quantity > 0 && unitPrice > 0) {
        form.value.totalPrice = (quantity * unitPrice).toFixed(2);
    } else {
        form.value.totalPrice = '0.00';
    }
}
// æäº¤äº§å“è¡¨å•
const submitForm = () => {
src/views/productionManagement/productionReporting/index.vue
@@ -1,428 +1,469 @@
<template>
    <div class="app-container">
        <div class="search_form">
            <el-form :model="searchForm" :inline="true">
                <el-form-item label="客户名称:">
                    <el-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search"
                                        style="width: 200px;"
                                        @change="handleQuery" />
                </el-form-item>
                <el-form-item label="项目名称:">
                    <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search"
                                        style="width: 200px;"
                                        @change="handleQuery" />
                </el-form-item>
                <el-form-item label="排产日期:">
                    <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                                                    placeholder="请选择" clearable @change="changeDaterange" />
                </el-form-item>
                <el-form-item label="状态:">
                    <el-select v-model="searchForm.status" placeholder="请选择状态" style="width: 140px" clearable>
                        <el-option label="待生产" :value="1"></el-option>
                        <el-option label="已报工" :value="3"></el-option>
                        <el-option label="生产中" :value="2"></el-option>
                    </el-select>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="handleQuery">搜索</el-button>
                </el-form-item>
            </el-form>
        </div>
        <div class="table_list">
            <div style="text-align: right" class="mb10">
                <el-button type="primary" @click="openForm('add')">生产报工</el-button>
                <el-button @click="handleOut">导出</el-button>
            </div>
            <PIMTable
                rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :isSelection="true"
                :expandRowKeys="expandedRowKeys"
                @expand-change="expandChange"
                @selection-change="handleSelectionChange"
                :tableLoading="tableLoading"
                @pagination="pagination"
                :total="page.total"
            >
                <template #expand="{ row }">
                    <el-table
                        :data="expandData"
                        border
                        show-summary
                        :summary-method="summarizeMainTable"
                        v-loading="childrenLoading"
                    >
                        <el-table-column
                            align="center"
                            label="序号"
                            type="index"
                            width="60"
                        />
                        <el-table-column label="本次生产数量" prop="finishedNum" align="center" width="400">
                            <template #default="scope">
                                <el-input-number :step="0.01" :min="0" style="width: 100%"
                                                                 v-model="scope.row.finishedNum"
                                                                 :disabled="!scope.row.editType"
                                                                 :precision="2"
                                                                 placeholder="请输入"
                                                                 clearable
                                                                 @change="changeNum(scope.row)"
                                />
                            </template>
                        </el-table-column>
<!--                        <el-table-column label="待生产数量" prop="pendingNum" width="240" align="center"></el-table-column>-->
                        <el-table-column label="生产人" prop="schedulingUserId" width="400">
                            <template #default="scope">
                                <el-select
                                    v-model="scope.row.schedulingUserId"
                                    placeholder="选择人员"
                                    :disabled="!scope.row.editType"
                                    style="width: 100%;"
                                >
                                    <el-option
                                        v-for="user in userList"
                                        :key="user.userId"
                                        :label="user.nickName"
                                        :value="user.userId"
                                    />
                                </el-select>
                            </template>
                        </el-table-column>
                        <el-table-column label="生产日期" prop="schedulingDate" width="400">
                            <template #default="scope">
                                <el-date-picker
                                    v-model="scope.row.schedulingDate"
                                    type="date"
                                    :disabled="!scope.row.editType"
                                    placeholder="请选择日期"
                                    value-format="YYYY-MM-DD"
                                    format="YYYY-MM-DD"
                                    clearable
                                    style="width: 100%"
                                />
                            </template>
                        </el-table-column>
                        <el-table-column label="操作" width="60">
                            <template #default="scope">
                                <el-button
                                    link
                                    type="primary"
                                    size="small"
                                    @click="changeEditType(scope.row)"
                                    v-if="!scope.row.editType"
                                    :disabled="scope.row.parentStatus === 3"
                                >编辑</el-button
                                >
                                <el-button
                                    link
                                    type="primary"
                                    size="small"
                                    @click="saveReceiptPayment(scope.row)"
                                    v-if="scope.row.editType"
                                >保存</el-button
                                >
                            </template>
                        </el-table-column>
                    </el-table>
                </template>
            </PIMTable>
        </div>
        <form-dia ref="formDia" @close="handleQuery"></form-dia>
    </div>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="报工人员名称:">
          <el-input v-model="searchForm.nickName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 200px;"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="工单号:">
          <el-input v-model="searchForm.workOrderNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 200px;"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="工单状态:">
          <el-select v-model="searchForm.workOrderStatus"
                     placeholder="请选择工单状态"
                     style="width: 140px"
                     clearable>
            <el-option label="待确认"
                       :value="1"></el-option>
            <el-option label="待生产"
                       :value="2"></el-option>
            <el-option label="生产中"
                       :value="3"></el-option>
            <el-option label="已生产"
                       :value="4"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery">搜索</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="table_list">
      <div style="text-align: right"
           class="mb10">
        <el-button type="primary"
                   @click="openForm('add')">生产报工</el-button>
        <el-button @click="handleOut">导出</el-button>
      </div>
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :isSelection="true"
                :expandRowKeys="expandedRowKeys"
                @expand-change="expandChange"
                @selection-change="handleSelectionChange"
                :tableLoading="tableLoading"
                @pagination="pagination"
                :total="page.total">
        <template #expand="{ row }">
          <el-table :data="expandData"
                    border
                    show-summary
                    :summary-method="summarizeMainTable"
                    v-loading="childrenLoading">
            <el-table-column align="center"
                             label="序号"
                             type="index"
                             width="60" />
            <el-table-column label="本次生产数量"
                             prop="finishedNum"
                             align="center"
                             width="400">
              <template #default="scope">
                <el-input-number :step="0.01"
                                 :min="0"
                                 style="width: 100%"
                                 v-model="scope.row.finishedNum"
                                 :disabled="!scope.row.editType"
                                 :precision="2"
                                 placeholder="请输入"
                                 clearable
                                 @change="changeNum(scope.row)" />
              </template>
            </el-table-column>
            <!--                        <el-table-column label="待生产数量" prop="pendingNum" width="240" align="center"></el-table-column>-->
            <el-table-column label="生产人"
                             prop="schedulingUserId"
                             width="400">
              <template #default="scope">
                <el-select v-model="scope.row.schedulingUserId"
                           placeholder="选择人员"
                           :disabled="!scope.row.editType"
                           style="width: 100%;">
                  <el-option v-for="user in userList"
                             :key="user.userId"
                             :label="user.nickName"
                             :value="user.userId" />
                </el-select>
              </template>
            </el-table-column>
            <el-table-column label="生产日期"
                             prop="schedulingDate"
                             width="400">
              <template #default="scope">
                <el-date-picker v-model="scope.row.schedulingDate"
                                type="date"
                                :disabled="!scope.row.editType"
                                placeholder="请选择日期"
                                value-format="YYYY-MM-DD"
                                format="YYYY-MM-DD"
                                clearable
                                style="width: 100%" />
              </template>
            </el-table-column>
            <el-table-column label="操作"
                             width="60">
              <template #default="scope">
                <el-button link
                           type="primary"
                           size="small"
                           @click="changeEditType(scope.row)"
                           v-if="!scope.row.editType"
                           :disabled="scope.row.parentStatus === 3">编辑</el-button>
                <el-button link
                           type="primary"
                           size="small"
                           @click="saveReceiptPayment(scope.row)"
                           v-if="scope.row.editType">保存</el-button>
              </template>
            </el-table-column>
          </el-table>
        </template>
      </PIMTable>
    </div>
    <form-dia ref="formDia"
              @close="handleQuery"></form-dia>
    <input-modal v-if="isShowInput"
                 v-model:visible="isShowInput"
                 :production-product-main-id="isShowingId" />
    <output-modal v-if="isShowOutput"
                  v-model:visible="isShowOutput"
                  :production-product-main-id="isShowingId" />
  </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import FormDia from "@/views/productionManagement/productionReporting/components/formDia.vue";
import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js";
import {ElMessageBox} from "element-plus";
import dayjs from "dayjs";
import {
    productionReportUpdate,
    workListPage,
    workListPageById
} from "@/api/productionManagement/productionReporting.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
  import { onMounted, ref } from "vue";
  import FormDia from "@/views/productionManagement/productionReporting/components/formDia.vue";
  import { ElMessageBox } from "element-plus";
  import {
    productionReportUpdate,
    workListPageById,
    productionReportDelete,
  } from "@/api/productionManagement/productionReporting.js";
  import { productionProductMainListPage } from "@/api/productionManagement/productionProductMain.js";
  import { userListNoPageByTenantId } from "@/api/system/user.js";
  import InputModal from "@/views/productionManagement/productionReporting/Input.vue";
  import OutputModal from "@/views/productionManagement/productionReporting/Output.vue";
const data = reactive({
    searchForm: {
        staffName: "",
        entryDate: null, // å½•入日期
        entryDateStart: undefined,
        entryDateEnd: undefined,
    },
});
const { searchForm } = toRefs(data);
const expandedRowKeys = ref([]);
const expandData = ref([]);
const userList = ref([])
const tableColumn = ref([
    {
        type: "expand",
        dataType: "slot",
        slot: "expand",
    },
    {
        label: "状态",
        prop: "status",
        dataType: "tag",
        formatData: (params) => {
            if (params == 3) {
                return "已报工";
            } else if (params == 1) {
                return "待生产";
            } else {
                return '生产中';
            }
        },
        formatType: (params) => {
            if (params == 3) {
                return "success";
            } else if (params == 1) {
                return "primary";
            } else {
                return 'warning';
            }
        },
    },
    {
        label: "排产日期",
        prop: "schedulingDate",
        width: 120,
    },
    {
        label: "排产人",
        prop: "schedulingUserName",
    },
    {
        label: "合同号",
        prop: "salesContractNo",
        width: 200,
    },
    {
        label: "客户合同号",
        prop: "customerContractNo",
        width: 200,
    },
    {
        label: "客户名称",
        prop: "customerName",
        width: 200,
    },
    {
        label: "项目名称",
        prop: "projectName",
        width:300
    },
    {
        label: "产品大类",
        prop: "productCategory",
        width: 150,
    },
    {
        label: "规格型号",
        prop: "specificationModel",
        width: 150,
    },
    {
        label: "单位",
        prop: "unit",
    },
    {
        label: "工序",
        prop: "process",
    },
    {
        label: "排产数量",
        prop: "schedulingNum",
        width: 100,
    },
    {
        label: "生产数量",
        prop: "finishedNum",
        width: 100,
    },
    {
        label: "待生产数量",
        prop: "pendingFinishNum",
        width: 100,
    },
]);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const childrenLoading = ref(false);
const page = reactive({
    current: 1,
    size: 100,
    total: 0,
});
const formDia = ref()
const { proxy } = getCurrentInstance()
  const data = reactive({
    searchForm: {
      nickName: "",
      workOrderNo: "",
      workOrderStatus: "",
    },
  });
  const { searchForm } = toRefs(data);
  const expandedRowKeys = ref([]);
  const expandData = ref([]);
  const userList = ref([]);
  const tableColumn = ref([
    {
      label: "报工单号",
      prop: "productNo",
      width: 120,
    },
    {
      label: "报工人员",
      prop: "nickName",
      width: 120,
    },
    {
      label: "工单编号",
      prop: "workOrderNo",
      width: 120,
    },
    {
      label: "报工状态",
      prop: "status",
      dataType: "tag",
      formatData: params => {
        if (params == 3) {
          return "已报工";
        } else if (params == 1) {
          return "待生产";
        } else {
          return "生产中";
        }
      },
      formatType: params => {
        if (params == 3) {
          return "success";
        } else if (params == 1) {
          return "primary";
        } else {
          return "warning";
        }
      },
    },
    {
      label: "工单状态",
      prop: "workOrderStatus",
      dataType: "tag",
      formatData: params => {
        switch (params) {
          case "1":
            return "待确认";
          case "2":
            return "待生产";
          case "3":
            return "生产中";
          case "4":
            return "已生产";
          default:
            return "";
        }
      },
      formatType: params => {
        switch (params) {
          case "1":
            return "primary";
          case "2":
            return "info";
          case "3":
            return "warning";
          case "4":
            return "success";
          default:
            return "";
        }
      },
    },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      fixed: "right",
      width: 200,
      operation: [
        {
          name: "查看投入",
          type: "text",
          clickFun: row => {
            showInput(row);
          },
        },
        {
          name: "查看产出",
          type: "text",
          clickFun: row => {
            showOutput(row);
          },
        },
        {
          name: "删除",
          type: "danger",
          clickFun: row => {
            deleteReport(row);
          },
        },
      ],
    },
  ]);
  const tableData = ref([]);
  const selectedRows = ref([]);
  const tableLoading = ref(false);
  const childrenLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
  const formDia = ref();
  const { proxy } = getCurrentInstance();
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
    page.current = 1;
    getList();
};
const changeDaterange = (value) => {
    if (value) {
        searchForm.value.entryDateStart = value[0];
        searchForm.value.entryDateEnd = value[1];
    } else {
        searchForm.value.entryDateStart = undefined;
        searchForm.value.entryDateEnd = undefined;
    }
    handleQuery();
};
const pagination = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList = () => {
    tableLoading.value = true;
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined
    expandedRowKeys.value = []
    workListPage(params).then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records.map(item => ({
            ...item,
            pendingFinishNum: (Number(item.schedulingNum) || 0) - (Number(item.finishedNum) || 0)
        }));
        page.total = res.data.total;
    }).catch(err => {
        tableLoading.value = false;
    })
};
// å±•开行
const expandChange = (row, expandedRows) => {
    userListNoPageByTenantId().then((res) => {
        userList.value = res.data;
    });
    if (expandedRows.length > 0) {
        nextTick(() => {
            expandedRowKeys.value = [];
            try {
                childrenLoading.value = true;
                workListPageById({ id: row.id }).then((res) => {
                    childrenLoading.value = false;
                    const index = tableData.value.findIndex((item) => item.id === row.id);
                    if (index > -1) {
                        expandData.value = res.data.map(item => ({
                            ...item,
                            pendingNum: (Number(item.schedulingNum) || 0) - (Number(item.finishedNum) || 0),
                            parentStatus: row.status // æ–°å¢žçˆ¶è¡¨çŠ¶æ€
                        }));
                    }
                    expandedRowKeys.value.push(row.id);
                });
            } catch (error) {
                childrenLoading.value = false;
                console.log(error);
            }
        })
    } else {
        expandedRowKeys.value = [];
    }
};
const changeNum = (row) => {
    // æ‰¾åˆ°çˆ¶è¡¨æ ¼æ•°æ®
    const parentRow = tableData.value.find(item => item.id === expandedRowKeys.value[0]);
    // è®¡ç®—所有子表格 finishedNum çš„æ€»å’Œ
    const totalFinishedNum = expandData.value.reduce((sum, item) => sum + (Number(item.finishedNum) || 0), 0);
    // çˆ¶è¡¨æ ¼çš„æŽ’产数量
    const schedulingNum = parentRow ? Number(parentRow.schedulingNum) : 0;
    if (totalFinishedNum > schedulingNum) {
        // å›žé€€æœ¬æ¬¡è¾“å…¥
        row.finishedNum = schedulingNum - (totalFinishedNum - Number(row.finishedNum));
        proxy.$modal.msgWarning('所有本次生产数量之和不可大于排产数量');
    }
    row.pendingNum = row.schedulingNum - row.finishedNum;
}
// ç¼–辑修改状态
const changeEditType = (row) => {
    row.editType = !row.editType;
};
// ä¿å­˜è®°å½•
const saveReceiptPayment = (row) => {
    productionReportUpdate(row).then((res) => {
        row.editType = !row.editType;
        getList();
        proxy.$modal.msgSuccess("提交成功");
    });
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
    selectedRows.value = selection;
};
const summarizeMainTable = (param) => {
    return proxy.summarizeTable(param, [
        "finishedNum"
    ]);
};
// æ‰“开弹框
const openForm = (type, row) => {
    if (selectedRows.value.length !== 1) {
        proxy.$message.error("请选择一条数据");
        return;
    }
    if (selectedRows.value[0].pendingFinishNum == 0) {
        proxy.$message.warning("无需再报工");
        return;
    }
    nextTick(() => {
        const rowInfo = type === 'add' ? selectedRows.value[0] : row
        formDia.value?.openDialog(type, rowInfo)
    })
};
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const changeDaterange = value => {
    if (value) {
      searchForm.value.entryDateStart = value[0];
      searchForm.value.entryDateEnd = value[1];
    } else {
      searchForm.value.entryDateStart = undefined;
      searchForm.value.entryDateEnd = undefined;
    }
    handleQuery();
  };
  const deleteReport = row => {
    ElMessageBox.confirm("确定删除该报工吗?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      productionReportDelete({ id: row.id }).then(res => {
        if (res.code === 200) {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        } else {
          ElMessageBox.alert(res.msg || "删除失败", "提示", {
            confirmButtonText: "确定",
          });
        }
      });
    });
  };
  const pagination = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined;
    expandedRowKeys.value = [];
    productionProductMainListPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records.map(item => ({
          ...item,
          pendingFinishNum:
            (Number(item.schedulingNum) || 0) - (Number(item.finishedNum) || 0),
        }));
        page.total = res.data.total;
      })
      .catch(err => {
        tableLoading.value = false;
      });
  };
  // å±•开行
  const expandChange = (row, expandedRows) => {
    userListNoPageByTenantId().then(res => {
      userList.value = res.data;
    });
    if (expandedRows.length > 0) {
      nextTick(() => {
        expandedRowKeys.value = [];
        try {
          childrenLoading.value = true;
          workListPageById({ id: row.id }).then(res => {
            childrenLoading.value = false;
            const index = tableData.value.findIndex(item => item.id === row.id);
            if (index > -1) {
              expandData.value = res.data.map(item => ({
                ...item,
                pendingNum:
                  (Number(item.schedulingNum) || 0) -
                  (Number(item.finishedNum) || 0),
                parentStatus: row.status, // æ–°å¢žçˆ¶è¡¨çŠ¶æ€
              }));
            }
            expandedRowKeys.value.push(row.id);
          });
        } catch (error) {
          childrenLoading.value = false;
          console.log(error);
        }
      });
    } else {
      expandedRowKeys.value = [];
    }
  };
  const changeNum = row => {
    // æ‰¾åˆ°çˆ¶è¡¨æ ¼æ•°æ®
    const parentRow = tableData.value.find(
      item => item.id === expandedRowKeys.value[0]
    );
    // è®¡ç®—所有子表格 finishedNum çš„æ€»å’Œ
    const totalFinishedNum = expandData.value.reduce(
      (sum, item) => sum + (Number(item.finishedNum) || 0),
      0
    );
    // çˆ¶è¡¨æ ¼çš„æŽ’产数量
    const schedulingNum = parentRow ? Number(parentRow.schedulingNum) : 0;
// åˆ é™¤
const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
        ids = selectedRows.value.map((item) => item.id);
    } else {
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            staffJoinDel(ids).then((res) => {
                proxy.$modal.msgSuccess("删除成功");
                getList();
            });
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
// å¯¼å‡º
const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download("/salesLedger/work/export", {}, "生产报工.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
onMounted(() => {
    getList();
});
    if (totalFinishedNum > schedulingNum) {
      // å›žé€€æœ¬æ¬¡è¾“å…¥
      row.finishedNum =
        schedulingNum - (totalFinishedNum - Number(row.finishedNum));
      proxy.$modal.msgWarning("所有本次生产数量之和不可大于排产数量");
    }
    row.pendingNum = row.schedulingNum - row.finishedNum;
  };
  // ç¼–辑修改状态
  const changeEditType = row => {
    row.editType = !row.editType;
  };
  // ä¿å­˜è®°å½•
  const saveReceiptPayment = row => {
    productionReportUpdate(row).then(res => {
      row.editType = !row.editType;
      getList();
      proxy.$modal.msgSuccess("提交成功");
    });
  };
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
  };
  const summarizeMainTable = param => {
    return proxy.summarizeTable(param, ["finishedNum"]);
  };
  // æ‰“开弹框
  const openForm = (type, row) => {
    if (selectedRows.value.length !== 1) {
      proxy.$message.error("请选择一条数据");
      return;
    }
    if (selectedRows.value[0].pendingFinishNum == 0) {
      proxy.$message.warning("无需再报工");
      return;
    }
    nextTick(() => {
      const rowInfo = type === "add" ? selectedRows.value[0] : row;
      formDia.value?.openDialog(type, rowInfo);
    });
  };
  // æ‰“开投入模态框
  const isShowInput = ref(false);
  const isShowingId = ref(0);
  const showInput = row => {
    isShowInput.value = true;
    isShowingId.value = row.id;
  };
  // æ‰“开产出模态框
  const isShowOutput = ref(false);
  const showOutput = row => {
    isShowOutput.value = true;
    isShowingId.value = row.id;
  };
  // å¯¼å‡º
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        proxy.download("/salesLedger/work/export", {}, "生产报工.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  onMounted(() => {
    getList();
  });
</script>
<style scoped></style>
src/views/productionManagement/workOrder/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,559 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <div class="search-row">
        <div class="search-item">
          <span class="search_title">工单编号:</span>
          <el-input v-model="searchForm.workOrderNo"
                    style="width: 240px"
                    placeholder="请输入"
                    @change="handleQuery"
                    clearable
                    prefix-icon="Search" />
        </div>
        <div class="search-item">
          <el-button type="primary"
                     @click="handleQuery">搜索</el-button>
        </div>
      </div>
    </div>
    <div class="table_list">
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :tableLoading="tableLoading"
                @pagination="pagination"></PIMTable>
    </div>
    <el-dialog v-model="editDialogVisible"
               title="编辑时间"
               width="500px">
      <el-form :model="editrow"
               label-width="120px">
        <el-form-item label="计划开始时间">
          <el-date-picker v-model="editrow.planStartTime"
                          type="date"
                          placeholder="请选择"
                          value-format="YYYY-MM-DD"
                          style="width: 300px" />
        </el-form-item>
        <el-form-item label="计划结束时间">
          <el-date-picker v-model="editrow.planEndTime"
                          type="date"
                          placeholder="请选择"
                          value-format="YYYY-MM-DD"
                          style="width: 300px" />
        </el-form-item>
        <el-form-item label="实际开始时间">
          <el-date-picker v-model="editrow.actualStartTime"
                          type="date"
                          placeholder="请选择"
                          value-format="YYYY-MM-DD"
                          style="width: 300px" />
        </el-form-item>
        <el-form-item label="实际结束时间">
          <el-date-picker v-model="editrow.actualEndTime"
                          type="date"
                          placeholder="请选择"
                          value-format="YYYY-MM-DD"
                          style="width: 300px" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="editDialogVisible = false">取消</el-button>
          <el-button type="primary"
                     @click="handleUpdate">确定</el-button>
        </span>
      </template>
    </el-dialog>
    <el-dialog v-model="transferCardVisible"
               title="流转卡"
               width="1000px">
      <div class="transfer-card-title">工单流转卡</div>
      <div class="transfer-card-container">
        <div class="transfer-card-info">
          <div class="info-group">
            <div class="info-item">
              <span class="info-label">工单编号</span>
              <span class="info-value">{{ transferCardRowData.workOrderNo }}</span>
            </div>
            <!-- <div class="info-item">
              <span class="info-label">产品编号</span>
              <span class="info-value">{{ transferCardRowData.productNo }}</span>
            </div> -->
            <div class="info-item">
              <span class="info-label">产品名称</span>
              <span class="info-value">{{ transferCardRowData.productName }}</span>
            </div>
            <div class="info-item">
              <span class="info-label">产品规格</span>
              <span class="info-value">{{ transferCardRowData.model }}</span>
            </div>
            <div class="info-item">
              <span class="info-label">工单状态</span>
              <span class="info-value">{{
                transferCardRowData.status === 1 ? '待确认' :
                transferCardRowData.status === 2 ? '待生产' :
                transferCardRowData.status === 3 ? '生产中' :
                transferCardRowData.status === 4 ? '已生产' :
                transferCardRowData.status
              }}</span>
            </div>
            <div class="info-item">
              <span class="info-label">计划开始时间</span>
              <span class="info-value">{{ transferCardRowData.planStartTime }}</span>
            </div>
            <div class="info-item">
              <span class="info-label">计划结束时间</span>
              <span class="info-value">{{ transferCardRowData.planEndTime }}</span>
            </div>
            <div class="info-item">
              <span class="info-label">备注</span>
              <span class="info-value">{{ transferCardRowData.remark }}</span>
            </div>
          </div>
          <div class="info-group">
            <div class="info-item">
              <span class="info-label">&nbsp;</span>
              <span class="info-value">&nbsp;</span>
            </div>
            <div class="info-item">
              <span class="info-label">计划数量</span>
              <span class="info-value">{{ transferCardRowData.planQuantity }}</span>
            </div>
            <div class="info-item">
              <span class="info-label">良品数量</span>
              <span class="info-value">0</span>
            </div>
            <div class="info-item">
              <span class="info-label">不良品数</span>
              <span class="info-value">0</span>
            </div>
            <div class="info-item">
              <span class="info-label">实际开始时间</span>
              <span class="info-value">{{ transferCardRowData.actualStartTime }}</span>
            </div>
            <div class="info-item">
              <span class="info-label">实际结束时间</span>
              <span class="info-value">{{ transferCardRowData.actualEndTime }}</span>
            </div>
          </div>
        </div>
        <div class="transfer-card-qr">
          <div class="qr-container">
            <img :src="transferCardQrUrl"
                 alt="流转卡二维码"
                 style="width: 200px; height: 200px;" />
            <!-- <div class="qr-tip"
                 style="margin-top: 10px; text-align: center;">流转卡二维码</div> -->
          </div>
        </div>
      </div>
      <div class="print-button-container"
           style=" text-align: center;
      margin-bottom: 40px;">
        <el-button type="primary"
                   style="margin-top: 20px;"
                   @click="printTransferCard">打印流转卡</el-button>
      </div>
    </el-dialog>
    <el-dialog v-model="reportDialogVisible"
               title="报工"
               width="500px">
      <el-form :model="reportForm"
               label-width="120px">
        <el-form-item label="待生产数量">
          <el-input v-model="reportForm.remainingQuantity"
                    readonly
                    style="width: 300px" />
        </el-form-item>
        <el-form-item label="本次生产数量">
          <el-input v-model.number="reportForm.quantity"
                    type="number"
                    min="1"
                    style="width: 300px"
                    placeholder="请输入本次生产数量" />
        </el-form-item>
        <el-form-item label="班组信息">
          <el-input v-model="reportForm.userName"
                    style="width: 300px"
                    readonly
                    placeholder="请输入班组信息" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="reportDialogVisible = false">取消</el-button>
          <el-button type="primary"
                     @click="handleReport">确定</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
  import { onMounted, ref } from "vue";
  import { ElMessageBox } from "element-plus";
  import dayjs from "dayjs";
  import {
    productWorkOrderPage,
    updateProductWorkOrder,
    addProductMain,
  } from "@/api/productionManagement/workOrder.js";
  import { getUserProfile } from "@/api/system/user.js";
  import QRCode from "qrcode";
  import { getCurrentInstance, reactive, toRefs } from "vue";
  const { proxy } = getCurrentInstance();
  const tableColumn = ref([
    {
      label: "工单编号",
      prop: "workOrderNo",
    },
    {
      label: "产品名称",
      prop: "productName",
    },
    {
      label: "规格",
      prop: "model",
    },
    {
      label: "单位",
      prop: "unit",
    },
    {
      label: "工序名称",
      prop: "processName",
    },
    {
      label: "计划开始时间",
      prop: "planStartTime",
    },
    {
      label: "计划结束时间",
      prop: "planEndTime",
    },
    {
      label: "实际开始时间",
      prop: "actualStartTime",
    },
    {
      label: "实际结束时间",
      prop: "actualEndTime",
    },
    {
      label: "操作",
      width: "200",
      align: "center",
      dataType: "action",
      fixed: "right",
      operation: [
        {
          name: "编辑",
          clickFun: row => {
            handleEdit(row);
          },
        },
        {
          name: "流转卡",
          clickFun: row => {
            showTransferCard(row);
          },
        },
        {
          name: "报工",
          clickFun: row => {
            showReportDialog(row);
          },
        },
      ],
    },
  ]);
  const tableData = ref([]);
  const tableLoading = ref(false);
  const qrCodeUrl = ref("");
  const qrRowData = ref(null);
  const editDialogVisible = ref(false);
  const transferCardVisible = ref(false);
  const transferCardData = ref([]);
  const transferCardQrUrl = ref("");
  const transferCardRowData = ref(null);
  const reportDialogVisible = ref(false);
  const reportForm = reactive({
    remainingQuantity: 0,
    quantity: 0,
    userName: "",
    workOrderId: "",
    reportWork: "",
    productProcessRouteItemId: "",
    userId: "",
    productMainId: null,
  });
  const currentReportRowData = ref(null);
  const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
  const data = reactive({
    searchForm: {
      workOrderNo: "",
    },
  });
  const { searchForm } = toRefs(data);
  let editrow = ref(null);
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const pagination = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const getList = () => {
    tableLoading.value = true;
    const params = { ...searchForm.value, ...page };
    productWorkOrderPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
  const showTransferCard = async row => {
    transferCardRowData.value = row;
    const qrContent =
      proxy.javaApi + "/work-order?orderRow=" + JSON.stringify(row);
    console.log(qrContent, "qrContent");
    transferCardQrUrl.value = await QRCode.toDataURL(qrContent);
    transferCardVisible.value = true;
  };
  const printTransferCard = () => {
    window.print();
  };
  const handleEdit = row => {
    editrow.value = JSON.parse(JSON.stringify(row));
    editDialogVisible.value = true;
  };
  const handleUpdate = () => {
    updateProductWorkOrder(editrow.value)
      .then(res => {
        proxy.$modal.msgSuccess("提交成功");
        editDialogVisible.value = false;
        getList();
      })
      .catch(() => {
        ElMessageBox.alert("修改失败", "提示", {
          confirmButtonText: "确定",
        });
      });
  };
  const showReportDialog = row => {
    currentReportRowData.value = row;
    reportForm.remainingQuantity = 1;
    reportForm.quantity = row.quantity;
    reportForm.productProcessRouteItemId = row.productProcessRouteItemId;
    reportForm.workOrderId = row.id;
    reportForm.reportWork = row.reportWork;
    reportForm.productMainId = row.productMainId;
    // èŽ·å–å½“å‰ç™»å½•ç”¨æˆ·ä¿¡æ¯
    reportDialogVisible.value = true;
  };
  const handleReport = () => {
    if (!reportForm.quantity || reportForm.quantity <= 0) {
      ElMessageBox.alert("请输入有效的本次生产数量", "提示", {
        confirmButtonText: "确定",
      });
      return;
    }
    if (reportForm.quantity > reportForm.remainingQuantity) {
      ElMessageBox.alert("本次生产数量不能超过待生产数量", "提示", {
        confirmButtonText: "确定",
      });
      return;
    }
    // console.log(reportForm);
    addProductMain(reportForm).then(res => {
      if (res.code === 200) {
        proxy.$modal.msgSuccess("报工成功");
        reportDialogVisible.value = false;
        getList();
      } else {
        ElMessageBox.alert(res.msg || "报工失败", "提示", {
          confirmButtonText: "确定",
        });
      }
    });
  };
  onMounted(() => {
    getList();
    getUserProfile()
      .then(res => {
        if (res.code === 200) {
          reportForm.userName = res.data.userName;
          reportForm.userId = res.data.userId;
        }
      })
      .catch(err => {
        console.error("获取用户信息失败", err);
      });
  });
</script>
<style scoped lang="scss">
  .search_form {
    margin-bottom: 20px;
    .search-row {
      display: flex;
      gap: 20px;
      align-items: center;
      .search-item {
        display: flex;
        align-items: center;
        gap: 10px;
      }
    }
  }
  .transfer-card-title {
    font-size: 24px;
    font-weight: bold;
    text-align: center;
    margin-bottom: 20px;
  }
  .transfer-card-container {
    display: flex;
    gap: 20px;
    height: 350px;
    .transfer-card-info {
      flex: 1;
      overflow: auto;
      .info-group {
        width: 50%;
        float: left;
      }
      .info-item {
        display: flex;
        margin-bottom: 15px;
        .info-label {
          width: 120px;
          font-weight: bold;
          margin-right: 20px;
        }
        .info-value {
          flex: 1;
        }
      }
    }
    .transfer-card-qr {
      width: 240px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
    }
  }
</style>
<style  lang="scss">
  @media print {
    @page {
      size: landscape;
    }
    body * {
      visibility: hidden;
    }
    .el-dialog__wrapper,
    .el-dialog,
    .el-dialog__body,
    .transfer-card-title,
    .transfer-card-container,
    .transfer-card-container *,
    .info-item,
    .info-label,
    .info-value {
      visibility: visible;
    }
    .print-button-container {
      visibility: hidden;
    }
    .el-dialog__wrapper {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      margin: 0;
    }
    .el-dialog {
      width: 100% !important;
      max-width: 800px;
      margin: 0 auto !important;
    }
    .el-dialog__header,
    .el-dialog__footer {
      display: none;
    }
    .el-dialog__body {
      padding: 20px;
    }
    .transfer-card-container {
      height: auto;
      display: flex;
      gap: 20px;
    }
    .transfer-card-info {
      flex: 1;
      .info-group {
        width: 100%;
        float: none;
        margin-bottom: 20px;
      }
      .info-item {
        display: flex;
        margin-bottom: 10px;
        .info-label {
          width: 100px;
          font-weight: bold;
          margin-right: 15px;
          white-space: nowrap;
        }
        .info-value {
          flex: 1;
          word-break: break-word;
        }
      }
    }
    .transfer-card-qr {
      width: 160px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
    }
    .qr-container img {
      width: 140px !important;
      height: 140px !important;
    }
  }
</style>
src/views/qualityManagement/finalInspection/components/filesDia.vue
@@ -30,16 +30,10 @@
          :isSelection="true"
          @selection-change="handleSelectionChange"
          height="500"
                    :page="page"
                    @pagination="pagination"
      >
      </PIMTable>
            <pagination
                style="margin: 10px 0"
                v-show="total > 0"
                @pagination="paginationSearch"
                :total="total"
                :page="page.current"
                :limit="page.size"
            />
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
@@ -95,6 +89,7 @@
const page = reactive({
    current: 1,
    size: 100,
    total: 0
});
const total = ref(0);
const tableData = ref([]);
@@ -111,7 +106,7 @@
  currentId.value = row.id;
  getList()
}
const paginationSearch = (obj) => {
const pagination = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
@@ -119,7 +114,7 @@
const getList = () => {
  qualityInspectFileListPage({inspectId: currentId.value}).then(res => {
    tableData.value = res.data.records;
        total.value = res.data.total;
        page.total = res.data.total;
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
src/views/qualityManagement/finalInspection/components/formDia.vue
@@ -58,7 +58,9 @@
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
                            <el-select v-model="form.checkName" placeholder="请选择" clearable>
                            <el-select v-model="form.checkName"               filterable
                         default-first-option
                         :reserve-keyword="false" placeholder="请选择" clearable>
                                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                                                     :value="item.nickName"/>
                            </el-select>
src/views/qualityManagement/finalInspection/index.vue
@@ -150,7 +150,7 @@
    label: "操作",
    align: "center",
    fixed: "right",
    width: 280,
    width: 300,
    operation: [
      {
        name: "编辑",
src/views/qualityManagement/processInspection/components/filesDia.vue
@@ -30,16 +30,10 @@
          :isSelection="true"
          @selection-change="handleSelectionChange"
          height="500"
                    :page="page"
                    @pagination="pagination"
      >
      </PIMTable>
            <pagination
                style="margin: 10px 0"
                v-show="total > 0"
                @pagination="paginationSearch"
                :total="total"
                :page="page.current"
                :limit="page.size"
            />
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
@@ -60,7 +54,6 @@
  qualityInspectFileDel,
  qualityInspectFileListPage
} from "@/api/qualityManagement/qualityInspectFile.js";
import Pagination from "@/components/PIMTable/Pagination.vue";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
@@ -98,6 +91,7 @@
const page = reactive({
    current: 1,
    size: 100,
    total: 0
});
const total = ref(0);
const tableData = ref([]);
@@ -114,17 +108,18 @@
  currentId.value = row.id;
  getList()
}
const paginationSearch = (obj) => {
const getList = () => {
  qualityInspectFileListPage({inspectId: currentId.value}).then(res => {
    tableData.value = res.data.records;
        page.total = res.data.total;
  })
}
const pagination = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList = () => {
  qualityInspectFileListPage({inspectId: currentId.value}).then(res => {
    tableData.value = res.data.records;
        total.value = res.data.total;
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
src/views/qualityManagement/processInspection/components/formDia.vue
@@ -65,7 +65,9 @@
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
                            <el-select v-model="form.checkName" placeholder="请选择" clearable>
                            <el-select v-model="form.checkName"               filterable
                         default-first-option
                         :reserve-keyword="false" placeholder="请选择" clearable>
                                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                                                     :value="item.nickName"/>
                            </el-select>
src/views/qualityManagement/processInspection/index.vue
@@ -155,7 +155,7 @@
    label: "操作",
    align: "center",
    fixed: "right",
    width: 280,
    width: 300,
    operation: [
      {
        name: "编辑",
src/views/qualityManagement/rawMaterialInspection/components/filesDia.vue
@@ -30,16 +30,10 @@
          :isSelection="true"
          @selection-change="handleSelectionChange"
          height="500"
                    :page="page"
                    @pagination="pagination"
      >
      </PIMTable>
            <pagination
                style="margin: 10px 0"
                v-show="total > 0"
                @pagination="paginationSearch"
                :total="total"
                :page="page.current"
                :limit="page.size"
            />
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="closeDia">取消</el-button>
@@ -60,7 +54,6 @@
  qualityInspectFileDel,
  qualityInspectFileListPage
} from "@/api/qualityManagement/qualityInspectFile.js";
import Pagination from "@/components/PIMTable/Pagination.vue";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
@@ -97,6 +90,7 @@
const page = reactive({
    current: 1,
    size: 100,
    total: 0
});
const total = ref(0);
const tableData = ref([]);
@@ -114,15 +108,16 @@
  currentId.value = row.id;
  getList()
}
const paginationSearch = (obj) => {
const pagination = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList = () => {
  qualityInspectFileListPage({inspectId: currentId.value, ...page}).then(res => {
    tableData.value = res.data.records;
        total.value = res.data.total;
        page.total = res.data.total;
  })
}
// è¡¨æ ¼é€‰æ‹©æ•°æ®
src/views/qualityManagement/rawMaterialInspection/index.vue
@@ -157,7 +157,7 @@
    label: "操作",
    align: "center",
    fixed: "right",
    width: 280,
    width: 300,
    operation: [
      {
        name: "编辑",
src/views/reportAnalysis/dataDashboard/index.vue
@@ -54,7 +54,7 @@
        <!-- è´¨é‡ç»Ÿè®¡ -->
                <div class="panel-header">
                    <span class="panel-title">质量统计</span>
                    <span class="panel-title">近4月质量统计</span>
                </div>
                <div class="main-panel">
                    <div class="panel-item-customers">
@@ -174,7 +174,7 @@
        </div>
                
                <div class="financial-header">
                    <span class="financial-title">财务分析</span>
                    <span class="financial-title">近4月财务分析</span>
                </div>
                <div class="main-panel">
                    <div class="panel-item-customers">
@@ -205,11 +205,11 @@
                <div class="panel-item-customers">
                    <div style="display: flex;justify-content: space-between;margin-bottom: 20px;">
                        <div class="section-title">应收应付统计</div>
                        <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable" class="custom-radio-group">
                            <el-radio-button label="按周" :value="1" />
                            <el-radio-button label="按月" :value="2" />
                            <el-radio-button label="按季度" :value="3" />
                        </el-radio-group>
<!--                        <el-radio-group v-model="radio1" size="large" @change="statisticsReceivable" class="custom-radio-group">-->
<!--                            <el-radio-button label="按周" :value="1" />-->
<!--                            <el-radio-button label="按月" :value="2" />-->
<!--                            <el-radio-button label="按季度" :value="3" />-->
<!--                        </el-radio-group>-->
                    </div>
                    <Echarts ref="chart"
                                     :color="barColors2"
@@ -226,7 +226,7 @@
        <!-- å›žæ¬¾ä¸Žå¼€ç¥¨åˆ†æž -->
         <div class="panel-header">
                    <span class="panel-title">回款与开票分析</span>
                    <span class="panel-title">近一月回款与开票分析</span>
                </div>
        <div class="panel-item-customers" style="padding-top: 60px;">
                    <Echarts ref="chart" :chartStyle="chartStyle" :grid="grid" :legend="lineLegend" :series="lineSeries"
@@ -412,7 +412,7 @@
const barLegend = {
    show: true,
    textStyle: { color: '#B8C8E0' },
    data: ['原材料不合格数', '过程不合格数', '出厂不合格数']
    data: ['原材料合格数', '过程合格数', '出不合格数']
}
const barLegend1 = {
    show: true,
@@ -487,7 +487,7 @@
])
const barSeries1 = ref([
    {
        name: '原材料不合格数',
        name: '原材料合格数',
        type: 'bar',
        barGap: 0,
        emphasis: {
@@ -509,7 +509,7 @@
        data: []
    },
    {
        name: '过程不合格数',
        name: '过程合格数',
        type: 'bar',
        emphasis: {
            focus: 'series'
@@ -530,7 +530,7 @@
        data: []
    },
    {
        name: '出厂不合格数',
        name: '出厂合格数',
        type: 'bar',
        emphasis: {
            focus: 'series'
@@ -672,10 +672,10 @@
    getLedgerPage(params).then((res) => {
        equipmentNum.value = res.data.total
    });
    getRepairPage(params).then((res) => {
    getRepairPage({...params, status:0}).then((res) => {
        equipmentRepair.value = res.data.total
    });
    getUpkeepPage(params).then((res) => {
    getUpkeepPage({...params, status:0}).then((res) => {
        equipmentMaintain.value = res.data.total
    });
    measuringInstrumentListPage(params).then((res) => {
@@ -920,7 +920,7 @@
  // ä½¿ç”¨nextTick确保DOM完全渲染后再初始化图表
  nextTick(() => {
    // åˆå§‹åŒ–autofit自适应
    autofit.init({ dh: 1080, dw: 1920, el: '.data-dashboard', resize: true }, false)
    autofit.init({ dh: 800, dw: 1280, el: '.data-dashboard', resize: true }, false)
    
    // æ·»åŠ è‡ªåŠ¨æ»šåŠ¨åŠ¨ç”»æ•ˆæžœ - å®¢æˆ·ä¿¡æ¯åˆ—表
    const contractList = refContractList.value
src/views/reportAnalysis/projectProfit/index.vue
@@ -64,11 +64,11 @@
      align: "center",
      prop: "customerName",
    },
    {
      label: "项目名称",
      align: "center",
      prop: "projectName",
    },
    // {
    //   label: "项目名称",
    //   align: "center",
    //   prop: "projectName",
    // },
    {
      label: "合同金额",
      align: "center",
src/views/salesManagement/indicatorStats/index.vue
@@ -224,6 +224,10 @@
}
onMounted(() => {
  // è®¾ç½®æŒ‡æ ‡ç­›é€‰æ—¥æœŸèŒƒå›´é»˜è®¤å€¼ä¸ºå½“天
  const today = dayjs().format('YYYY-MM-DD')
  indicatorFilter.dateRange = [today, today]
  nextTick(() => initIndicatorChart())
})
</script>
src/views/salesManagement/invoiceLedger/fileList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
<template>
  <el-dialog v-model="dialogVisible" title="附件" width="40%" :before-close="handleClose">
    <el-table :data="tableData" border height="40vh">
      <el-table-column label="附件名称" prop="name" min-width="400" show-overflow-tooltip />
      <el-table-column fixed="right" label="操作" width="150" align="center">
        <template #default="scope">
          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">下载</el-button>
          <el-button link type="primary" size="small" @click="lookFile(scope.row)">预览</el-button>
          <el-button link type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </el-dialog>
  <filePreview ref="filePreviewRef" />
</template>
<script setup>
import { ref } from 'vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import filePreview from '@/components/filePreview/index.vue'
import { delCommonFileInvoiceLedger} from '@/api/publicApi/commonFile.js'
const dialogVisible = ref(false)
const tableData = ref([])
const { proxy } = getCurrentInstance();
const filePreviewRef = ref()
const handleClose = () => {
  dialogVisible.value = false
}
const open = (list) => {
  dialogVisible.value = true
  tableData.value = list
}
const downLoadFile = (row) => {
  proxy.$download.name(row.url);
}
const lookFile = (row) => {
  filePreviewRef.value.open(row.url)
}
// åˆ é™¤é™„ä»¶
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除附件"${row.name}"吗?`, '删除确认', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }).then(() => {
        delCommonFileInvoiceLedger([row.id]).then(() => {
      ElMessage.success('删除成功')
      // ä»Žåˆ—表中移除已删除的附件
      const index = tableData.value.findIndex(item => item.id === row.id)
      if (index !== -1) {
        tableData.value.splice(index, 1)
      }
    }).catch(() => {
      ElMessage.error('删除失败')
    })
  }).catch(() => {
    proxy.$modal.msg('已取消删除')
  })
}
defineExpose({
  open
})
</script>
<style></style>
src/views/salesManagement/invoiceLedger/index.vue
@@ -12,7 +12,7 @@
            @change="changeDateRange" @clear="clearRange" />
        </el-form-item>
        <el-form-item label="录入日期">
          <el-date-picker style="width: 100%" v-model="searchForm.createTimeStart" value-format="YYYY-MM-DD"
          <el-date-picker style="width: 100%" v-model="searchForm.createTimeStart" value-format="YYYY-MM-DD HH:mm:ss"
            format="YYYY-MM-DD" type="date" placeholder="请选择" clearable @change="handleQuery" />
        </el-form-item>
        <el-form-item label="不显示有发票行">
@@ -31,9 +31,8 @@
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="销售合同号" prop="salesContractNo" show-overflow-tooltip width="180" />
        <el-table-column label="客户合同号" prop="customerContractNo" show-overflow-tooltip width="180" />
        <el-table-column label="客户名称" prop="customerName" show-overflow-tooltip width="240" />
        <el-table-column label="项目" prop="projectName" width="320" />
<!--        <el-table-column label="项目" prop="projectName" width="320" />-->
        <el-table-column label="产品大类" prop="productCategory" width="200" />
        <el-table-column label="规格型号" prop="specificationModel" width="160" show-overflow-tooltip />
        <el-table-column label="发票号" prop="invoiceNo" width="200" show-overflow-tooltip />
@@ -43,21 +42,22 @@
        <el-table-column label="录入人" prop="invoicePerson" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="createTime" show-overflow-tooltip :formatter="formatDate" width="180" />
        <el-table-column label="开票日期" prop="invoiceDate" show-overflow-tooltip width="120" />
        <el-table-column label="发票" prop="invoiceFileName" width="120" align="center" show-overflow-tooltip fixed="right">
        <!-- <el-table-column label="发票" prop="invoiceFileName" width="120" align="center" show-overflow-tooltip fixed="right">
          <template #default="scope">
            <el-button v-if="scope.row.invoiceFileName" text bg type="primary"
              @click="handleFile(scope.row.commonFiles)">
              æŸ¥çœ‹é™„ä»¶
            </el-button>
            <el-button v-else link type="primary" @click="handleDownload(scope.row)" :disabled="scope.row.invoicePerson !== userStore.nickName">
            <el-button v-else link type="primary" @click="handleDownload(scope.row)">
              ä¸Šä¼ 
            </el-button>
          </template>
        </el-table-column>
        </el-table-column> -->
        <el-table-column fixed="right" label="操作" width="150" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm(scope.row)" :disabled="scope.row.invoicePerson !== userStore.nickName">编辑</el-button>
            <el-button link type="primary" size="small" @click="delInvoiceLedger(scope.row)" :disabled="scope.row.invoicePerson !== userStore.nickName">删除</el-button>
            <el-button link type="primary" size="small" @click="openForm(scope.row)">编辑</el-button>
            <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button>
            <el-button link type="primary" size="small" @click="delInvoiceLedger(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
@@ -108,7 +108,7 @@
            <el-form-item label="附件材料:" prop="remark">
              <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                :headers="upload.headers" accept=".pdf" :limit="10" :before-upload="handleBeforeUpload"
                :on-error="handleUploadError" :on-success="handleUploadSuccess" :on-remove="handleRemove">
                :on-error="handleUploadError" :on-success="handleUploadSuccess">
                <el-button type="primary">上传</el-button>
                <template #tip>
                  <!--                  æ–‡ä»¶æ ¼å¼æ”¯æŒ doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z-->
@@ -149,6 +149,7 @@
        </div>
      </template>
    </el-dialog>
    <FileList ref="fileListRef" />
  </div>
</template>
@@ -164,10 +165,11 @@
  commitFile,
  registrationProductPage,
  delInvoiceLedgerByRegProductId,
} from "../../../api/salesManagement/invoiceLedger.js";
} from "@/api/salesManagement/invoiceLedger.js";
import useUserStore from "@/store/modules/user.js";
import useFormData from "@/hooks/useFormData";
import dayjs from "dayjs";
import FileList from "./fileList.vue";
const { proxy } = getCurrentInstance();
const tableData = ref([]);
@@ -229,7 +231,7 @@
  return parseFloat(cellValue).toFixed(2);
};
const formatDate = (row, column, cellValue) => {
  return dayjs(cellValue).format("YYYY-MM-DD");
  return dayjs(cellValue).format("YYYY-MM-DD HH:mm:ss");
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
@@ -245,7 +247,11 @@
const getList = () => {
  tableLoading.value = true;
  const { invoiceDate, ...rest } = searchForm;
  registrationProductPage({ ...rest, ...page }).then((res) => {
  // å°†èŒƒå›´æ—¥æœŸå­—段传递给后端
  const params = { ...rest, ...page };
  // ç§»é™¤å¼€ç¥¨æ—¥æœŸçš„默认值设置,只保留范围日期字段
  delete params.invoiceDate;
  registrationProductPage(params).then((res) => {
    tableLoading.value = false;
    tableData.value = res.data.records;
    total.value = res.data.total;
@@ -272,13 +278,15 @@
  invoiceLedgerProductInfo({ id: row.id }).then((res) => {
    form.value = { ...res.data };
    fileList.value = res.data.fileList;
    // ä¿å­˜ticketRegistrationId到表单数据中
    if (row.ticketRegistrationId) {
      form.value.ticketRegistrationId = row.ticketRegistrationId;
    }
    if (!form.value.invoicePerson) {
      form.value.invoicePerson = userStore.nickName;
      form.value.entryDate = getCurrentDate();
      // ç§»é™¤å½•入日期默认值设置,只处理范围日期字段
    }
    if (!form.value.invoiceDate) {
      form.value.invoiceDate = getCurrentDate();
    }
    // ç§»é™¤å¼€ç¥¨æ—¥æœŸé»˜è®¤å€¼è®¾ç½®ï¼Œåªå¤„理范围日期字段
  });
  dialogFormVisible.value = true;
};
@@ -291,7 +299,6 @@
};
// ä¸Šä¼ å‰æ ¡æ£€
function handleBeforeUpload(file) {
  console.log("file", file);
  // æ ¡æ£€æ–‡ä»¶å¤§å°
  if (file.size > 1024 * 1024 * 10) {
    proxy.$modal.msgError("上传文件大小不能超过10MB!");
@@ -303,6 +310,7 @@
    proxy.$modal.msgError("文件格式不匹配");
    return false;
  }
  console.log('handleBeforeUpload');
  proxy.$modal.loading("正在上传文件,请稍候...");
  return true;
}
@@ -314,20 +322,27 @@
// ä¸Šä¼ æˆåŠŸå›žè°ƒ
function handleUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
  console.log('handleUploadSuccess');
  if (res.code === 200) {
    proxy.$refs["fileUpload"].handleRemove(file);
    fileList.value.push(res.data);
    proxy.$modal.msgSuccess("上传成功");
    // å°†ä¸Šä¼ æˆåŠŸçš„æ–‡ä»¶ä¿¡æ¯æ·»åŠ åˆ°fileList中
    const fileInfo = {
      name: file.name,
      url: res.data.url || file.response?.data?.url || file.url,
      response: file.response
    };
    // æ£€æŸ¥æ˜¯å¦å·²å­˜åœ¨ç›¸åŒæ–‡ä»¶ï¼Œé¿å…é‡å¤æ·»åŠ 
    const existingFileIndex = fileList.value.findIndex(f => f.name === fileInfo.name);
    if (existingFileIndex === -1) {
      fileList.value.push(fileInfo);
    } else {
      fileList.value[existingFileIndex] = fileInfo;
    }
    // ç¡®ä¿è¡¨å•数据中的fileList也更新
    form.value.fileList = fileList.value;
  } else {
    proxy.$modal.msgError(res.msg);
    proxy.$refs.fileUpload.handleRemove(file);
  }
}
// ç§»é™¤æ–‡ä»¶
function handleRemove(file) {
  let index = fileList.value.findIndex((item) => item.url === file.url);
  if (index > -1) {
    fileList.value.splice(index, 1);
  }
}
// æäº¤è¡¨å•
@@ -361,13 +376,6 @@
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
// æ‰“开附件上传弹窗
const handleDownload = (val) => {
  fileList.value = [];
  uploadModal.value = true;
  currentId.value = val.id;
};
// ç¡®è®¤æ–‡ä»¶ä¸Šä¼ 
@@ -433,7 +441,22 @@
  getList();
};
//附件相关
const fileListRef = ref(null)
//查看附件
const downLoadFile = (row) => {
    invoiceLedgerProductInfo({ id: row.id }).then((res) => {
        fileListRef.value.open(res.data.fileList)
    });
}
onMounted(() => {
  // è®¾ç½®å¼€ç¥¨æ—¥æœŸèŒƒå›´é»˜è®¤å€¼ä¸ºå½“天
  const today = dayjs().format('YYYY-MM-DD');
  searchForm.invoiceDate = [today, today];
  // è®¾ç½®èŒƒå›´æ—¥æœŸå­—段的起始和结束时间
  searchForm.invoiceDateStart = today;
  searchForm.invoiceDateEnd = today;
  getList();
});
</script>
src/views/salesManagement/invoiceRegistration/index.vue
@@ -1,406 +1,378 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :inline="true" :model="searchForm">
        <el-form-item label="客户名称">
          <el-input
            v-model="searchForm.customerName"
            style="width: 240px"
            placeholder="请输入名称搜索"
            clearable
            :prefix-icon="Search"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="客户合同号">
          <el-input
            v-model="searchForm.customerContractNo"
            placeholder="请输入客户合同号"
            clearable
          />
        </el-form-item>
        <el-form-item label="项目名称">
          <el-input
            v-model="searchForm.projectName"
            placeholder="请输入项目名称"
            clearable
          />
        </el-form-item>
        <el-form-item>
          <el-checkbox
            v-model="searchForm.status"
            label="不显示未开票金额为0"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery"> æœç´¢ </el-button>
          <el-button @click="resetForm"> é‡ç½® </el-button>
          <el-button @click="handleExport" style="margin-right: 10px">导出</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="table_list">
      <div class="flex justify-between">
        <div></div>
        <div>
        <el-button type="primary" @click="openForm" style="margin-bottom: 8px">
          æ–°å¢žç™»è®°
        </el-button>
        </div>
      </div>
      <el-table
        :data="tableData"
        :border="true"
        height="calc(100vh - 21em)"
        v-loading="tableLoading"
        :expand-row-keys="expandedRowKeys"
        :row-key="(row) => row.id"
        show-summary
        :summary-method="summarizeMainTable"
        @expand-change="expandChange"
        @selection-change="handleSelectionChange"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column type="expand">
          <template #default="props">
            <el-table
              :data="props.row.children"
              border
              show-summary
              :summary-method="summarizeChildrenTable"
            >
              <el-table-column
                align="center"
                label="序号"
                type="index"
                width="60"
              />
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column
                label="规格型号"
                prop="specificationModel"
                width="150"
              />
              <el-table-column label="单位" prop="unit" width="70" />
              <el-table-column label="数量" prop="quantity" width="70" />
              <el-table-column label="税率(%)" prop="taxRate" width="80" />
              <el-table-column
                label="含税单价(元)"
                prop="taxInclusiveUnitPrice"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="含税总价(元)"
                prop="taxInclusiveTotalPrice"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="不含税总价(元)"
                prop="taxExclusiveTotalPrice"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="开票数"
                prop="invoiceNum"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="开票金额(元)"
                prop="invoiceAmount"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="未开票数"
                prop="noInvoiceNum"
                :formatter="formattedNumber"
              />
              <el-table-column
                label="未开票金额(元)"
                prop="noInvoiceAmount"
                :formatter="formattedNumber"
              />
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column
          label="销售合同号"
          prop="salesContractNo"
          show-overflow-tooltip
          width="200"
        />
        <el-table-column
          label="客户合同号"
          prop="customerContractNo"
          width="200"
          show-overflow-tooltip
        />
        <el-table-column
          label="客户名称"
          prop="customerName"
          show-overflow-tooltip
          width="240"
        />
        <el-table-column label="业务员" prop="salesman" show-overflow-tooltip width="90"/>
        <el-table-column
          label="项目名称"
          prop="projectName"
          show-overflow-tooltip
          width="200"
        />
        <el-table-column
          label="合同金额(元)"
          prop="contractAmount"
          show-overflow-tooltip
          :formatter="formattedNumber"
                    width="220"
        />
        <el-table-column
          label="已开票金额(元)"
          prop="invoiceTotal"
          show-overflow-tooltip
          :formatter="formattedNumber"
          width="120"
        />
        <el-table-column
          label="未开票金额(元)"
          prop="noInvoiceAmountTotal"
          show-overflow-tooltip
          width="120"
        >
          <template #default="{ row, column }">
            <el-text type="danger">
              {{ formattedNumber(row, column, row.noInvoiceAmountTotal) }}
            </el-text>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
        :total="total"
        layout="total, sizes, prev, pager, next, jumper"
        :page="page.current"
        :limit="page.size"
        @pagination="paginationChange"
      />
    </div>
    <el-dialog
      v-model="dialogFormVisible"
      title="新增开票登记页面"
      width="85%"
      @close="closeDia"
    >
      <el-form
        :model="form"
        label-width="140px"
        label-position="top"
        :rules="rules"
        ref="formRef"
      >
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="销售合同号:" prop="salesContractNo">
              <el-input v-model="form.salesContractNo" disabled></el-input>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户名称:" prop="customerName">
              <el-input
                v-model="form.customerName"
                placeholder="自动填充"
                disabled
              ></el-input>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="业务员:" prop="salesman">
              <el-input
                v-model="form.salesman"
                placeholder="自动填充"
                disabled
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="项目名称:" prop="projectName">
              <el-input
                v-model="form.projectName"
                placeholder="自动填充"
                disabled
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="录入人" prop="createUer">
              <el-input v-model="form.createUer" placeholder="请输入录入人" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="开票日期" prop="issueDate">
              <el-date-picker
                style="width: 100%"
                v-model="form.issueDate"
                type="date"
                placeholder="请选择"
                clearable
                format="YYYY-MM-DD"
                value-format="YYYY-MM-DD"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="录入日期:" prop="createTime">
              <el-date-picker
                style="width: 100%"
                v-model="form.createTime"
                type="date"
                placeholder="请选择"
                clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="发票号码:" prop="invoiceNo">
              <el-input
                v-model="form.invoiceNo"
                placeholder="请输入"
                clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-form-item label="产品信息:" prop="entryDate"> </el-form-item>
        </el-row>
        <el-table
          :data="productData"
          border
          show-summary
          :summary-method="summarizeChildrenTable"
        >
          <el-table-column
            align="center"
            label="序号"
            type="index"
            width="60"
          />
          <el-table-column label="产品大类" prop="productCategory" />
          <el-table-column
            label="规格型号"
            prop="specificationModel"
            width="150"
          />
          <el-table-column label="单位" prop="unit" />
          <el-table-column label="数量" prop="quantity" width="70" />
          <el-table-column label="税率(%)" prop="taxRate" width="80" />
          <el-table-column
            label="含税单价(元)"
            prop="taxInclusiveUnitPrice"
            :formatter="formattedNumber"
    <div class="app-container">
        <div class="search_form">
            <el-form :inline="true" :model="searchForm">
                <el-form-item label="客户名称">
                    <el-input
                        v-model="searchForm.customerName"
                        style="width: 240px"
                        placeholder="请输入名称搜索"
                        clearable
                        :prefix-icon="Search"
                        @change="handleQuery"
                    />
                </el-form-item>
                <el-form-item>
                    <el-checkbox
                        v-model="searchForm.status"
                        label="不显示未开票金额为0"
                        @change="handleQuery"
                    />
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="handleQuery"> æœç´¢ </el-button>
                    <el-button @click="resetForm"> é‡ç½® </el-button>
                    <el-button @click="handleExport" style="margin-right: 10px">导出</el-button>
                </el-form-item>
            </el-form>
        </div>
        <div class="table_list">
            <div class="flex justify-between">
                <div></div>
                <div>
                    <el-button type="primary" @click="openForm" style="margin-bottom: 8px">
                        æ–°å¢žç™»è®°
                    </el-button>
                </div>
            </div>
            <el-table
                :data="tableData"
                :border="true"
                height="calc(100vh - 21em)"
                v-loading="tableLoading"
                :expand-row-keys="expandedRowKeys"
                :row-key="(row) => row.id"
                show-summary
                :summary-method="summarizeMainTable"
                @expand-change="expandChange"
                @selection-change="handleSelectionChange"
            >
                <el-table-column align="center" type="selection" width="55" />
                <el-table-column type="expand">
                    <template #default="props">
                        <el-table
                            :data="props.row.children"
                            border
                            show-summary
                            :summary-method="summarizeChildrenTable"
                        >
                            <el-table-column
                                align="center"
                                label="序号"
                                type="index"
                                width="60"
                            />
                            <el-table-column label="产品大类" prop="productCategory" />
                            <el-table-column
                                label="规格型号"
                                prop="specificationModel"
                                width="150"
                            />
                            <el-table-column label="单位" prop="unit" width="70" />
                            <el-table-column label="数量" prop="quantity" width="70" />
                            <el-table-column label="税率(%)" prop="taxRate" width="80" />
                            <el-table-column
                                label="含税单价(元)"
                                prop="taxInclusiveUnitPrice"
                                :formatter="formattedNumber"
                            />
                            <el-table-column
                                label="含税总价(元)"
                                prop="taxInclusiveTotalPrice"
                                :formatter="formattedNumber"
                            />
                            <el-table-column
                                label="不含税总价(元)"
                                prop="taxExclusiveTotalPrice"
                                :formatter="formattedNumber"
                            />
                            <el-table-column
                                label="开票数"
                                prop="invoiceNum"
                                :formatter="formattedNumber"
                            />
                            <el-table-column
                                label="开票金额(元)"
                                prop="invoiceAmount"
                                :formatter="formattedNumber"
                            />
                            <el-table-column
                                label="未开票数"
                                prop="noInvoiceNum"
                                :formatter="formattedNumber"
                            />
                            <el-table-column
                                label="未开票金额(元)"
                                prop="noInvoiceAmount"
                                :formatter="formattedNumber"
                            />
                        </el-table>
                    </template>
                </el-table-column>
                <el-table-column align="center" label="序号" type="index" width="60" />
                <el-table-column
                    label="销售合同号"
                    prop="salesContractNo"
                    show-overflow-tooltip
                />
<!--                <el-table-column-->
<!--                    label="客户合同号"-->
<!--                    prop="customerContractNo"-->
<!--                    width="200"-->
<!--                    show-overflow-tooltip-->
<!--                />-->
                <el-table-column
                    label="客户名称"
                    prop="customerName"
                    show-overflow-tooltip
                />
                <el-table-column label="业务员" prop="salesman" show-overflow-tooltip/>
                <el-table-column
                    label="合同金额(元)"
                    prop="contractAmount"
                    show-overflow-tooltip
                    :formatter="formattedNumber"
                />
                <el-table-column
                    label="已开票金额(元)"
                    prop="invoiceTotal"
                    show-overflow-tooltip
                    :formatter="formattedNumber"
                />
                <el-table-column
                    label="未开票金额(元)"
                    prop="noInvoiceAmountTotal"
                    show-overflow-tooltip
                    width="120"
                >
                    <template #default="{ row, column }">
                        <el-text type="danger">
                            {{ formattedNumber(row, column, row.noInvoiceAmountTotal) }}
                        </el-text>
                    </template>
                </el-table-column>
            </el-table>
            <pagination
                v-show="total > 0"
                :total="total"
                layout="total, sizes, prev, pager, next, jumper"
                :page="page.current"
                :limit="page.size"
                @pagination="paginationChange"
            />
        </div>
        <el-dialog
            v-model="dialogFormVisible"
            title="新增开票登记页面"
            width="85%"
            @close="closeDia"
        >
            <el-form
                :model="form"
                label-width="140px"
                label-position="top"
                :rules="rules"
                ref="formRef"
            >
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="销售合同号:" prop="salesContractNo">
                            <el-input v-model="form.salesContractNo" disabled placeholder="多合同批量处理(具体合同号见产品列表)"></el-input>
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="客户名称:" prop="customerName">
                            <el-input
                                v-model="form.customerName"
                                placeholder="自动填充"
                                disabled
                            ></el-input>
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="业务员:" prop="salesman">
                            <el-input
                                v-model="form.salesman"
                                placeholder="自动填充"
                                disabled
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="录入人" prop="createUer">
                            <el-input v-model="form.createUer" placeholder="请输入录入人" />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="开票日期" prop="issueDate">
                            <el-date-picker
                                style="width: 100%"
                                v-model="form.issueDate"
                                type="date"
                                placeholder="请选择"
                                clearable
                                format="YYYY-MM-DD"
                                value-format="YYYY-MM-DD"
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="录入日期:" prop="createTime">
                            <el-date-picker
                                style="width: 100%"
                                v-model="form.createTime"
                                type="date"
                                placeholder="请选择"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                    <el-col :span="12">
                        <el-form-item label="发票号码:" prop="invoiceNo">
                            <el-input
                                v-model="form.invoiceNo"
                                placeholder="请输入"
                                clearable
                            />
                        </el-form-item>
                    </el-col>
                </el-row>
                <el-row>
                    <el-form-item label="产品信息:" prop="entryDate"> </el-form-item>
                </el-row>
                <el-table
                    :data="productData"
                    border
                    show-summary
                    :summary-method="summarizeChildrenTable"
                >
                    <el-table-column
                        align="center"
                        label="序号"
                        type="index"
                        width="60"
                    />
                    <el-table-column label="所属合同" prop="salesContractNo" width="200">
                        <template #default="{ row }">
                            <el-tag type="primary">{{ row.salesContractNo }}</el-tag>
                        </template>
                    </el-table-column>
                    <el-table-column label="产品大类" prop="productCategory" />
                    <el-table-column
                        label="规格型号"
                        prop="specificationModel"
                        width="150"
                    />
                    <el-table-column label="单位" prop="unit" />
                    <el-table-column label="数量" prop="quantity" width="70" />
                    <el-table-column label="税率(%)" prop="taxRate" width="80" />
                    <el-table-column
                        label="含税单价(元)"
                        prop="taxInclusiveUnitPrice"
                        :formatter="formattedNumber"
                        width="200"
          />
          <el-table-column
            label="含税总价(元)"
            prop="taxInclusiveTotalPrice"
            :formatter="formattedNumber"
                    />
                    <el-table-column
                        label="含税总价(元)"
                        prop="taxInclusiveTotalPrice"
                        :formatter="formattedNumber"
                        width="200"
          />
          <el-table-column
            label="不含税总价(元)"
            prop="taxExclusiveTotalPrice"
            :formatter="formattedNumber"
            width="150"
          />
          <el-table-column label="本次开票数" prop="currentInvoiceNum" width="180">
            <template #default="scope">
              <el-input-number :step="0.1" :min="0" style="width: 100%"
                    />
                    <el-table-column
                        label="不含税总价(元)"
                        prop="taxExclusiveTotalPrice"
                        :formatter="formattedNumber"
                        width="150"
                    />
                    <el-table-column label="本次开票数" prop="currentInvoiceNum" width="180">
                        <template #default="scope">
                            <el-input-number :step="0.1" :min="0" style="width: 100%"
                                                             :precision="2"
                v-model="scope.row.currentInvoiceNum"
                @change="invoiceNumBlur(scope.row)"
              ></el-input-number>
            </template>
          </el-table-column>
          <el-table-column
            label="本次开票金额(元)"
            prop="currentInvoiceAmount"
            width="180"
          >
            <template #default="scope">
              <el-input-number :step="0.01" :min="0" style="width: 100%"
                                                             v-model="scope.row.currentInvoiceNum"
                                                             @change="invoiceNumBlur(scope.row)"
                            ></el-input-number>
                        </template>
                    </el-table-column>
                    <el-table-column
                        label="本次开票金额(元)"
                        prop="currentInvoiceAmount"
                        width="180"
                    >
                        <template #default="scope">
                            <el-input-number :step="0.01" :min="0" style="width: 100%"
                                                             :precision="2"
                v-model="scope.row.currentInvoiceAmount"
                @change="invoiceAmountBlur(scope.row)"
              ></el-input-number>
            </template>
          </el-table-column>
          <el-table-column label="未开票数" prop="noInvoiceNum" width="120">
            <template #default="scope">
              <el-input
                type="number"
                min="0"
                disabled
                v-model="scope.row.noInvoiceNum"
              ></el-input>
            </template>
          </el-table-column>
          <el-table-column
            label="未开票金额(元)"
            prop="noInvoiceAmount"
            width="200"
          >
            <template #default="scope">
              <el-input
                type="number"
                min="0"
                disabled
                v-model="scope.row.noInvoiceAmount"
                :formatter="formattedInputNumber"
                :precision="2"
                :step="0.01"
              ></el-input>
            </template>
          </el-table-column>
          <el-table-column label="登记人" prop="register" width="100">
            <!-- <template #default="{ row }">
              <el-input
                v-model="row.register"
                placeholder="请输入登记人"
                disabled
              />
            </template> -->
          </el-table-column>
          <el-table-column label="登记日期" prop="registerDate" width="150">
            <!-- <template #default="{ row }">
              <el-date-picker
                style="width: 100%"
                v-model="row.registerDate"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                type="date"
                placeholder="请选择"
                clearable
                disabled
              />
            </template> -->
          </el-table-column>
        </el-table>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
                                                             v-model="scope.row.currentInvoiceAmount"
                                                             @change="invoiceAmountBlur(scope.row)"
                            ></el-input-number>
                        </template>
                    </el-table-column>
                    <el-table-column label="未开票数" prop="noInvoiceNum" width="120">
                        <template #default="scope">
                            <el-input
                                type="number"
                                min="0"
                                disabled
                                v-model="scope.row.noInvoiceNum"
                            ></el-input>
                        </template>
                    </el-table-column>
                    <el-table-column
                        label="未开票金额(元)"
                        prop="noInvoiceAmount"
                        width="200"
                    >
                        <template #default="scope">
                            <el-input
                                type="number"
                                min="0"
                                disabled
                                v-model="scope.row.noInvoiceAmount"
                                :formatter="formattedInputNumber"
                                :precision="2"
                                :step="0.01"
                            ></el-input>
                        </template>
                    </el-table-column>
                    <el-table-column label="登记人" prop="register" width="100">
                        <!-- <template #default="{ row }">
                            <el-input
                                v-model="row.register"
                                placeholder="请输入登记人"
                                disabled
                            />
                        </template> -->
                    </el-table-column>
                    <el-table-column label="登记日期" prop="registerDate" width="150">
                        <!-- <template #default="{ row }">
                            <el-date-picker
                                style="width: 100%"
                                v-model="row.registerDate"
                                value-format="YYYY-MM-DD"
                                format="YYYY-MM-DD"
                                type="date"
                                placeholder="请选择"
                                clearable
                                disabled
                            />
                        </template> -->
                    </el-table-column>
                </el-table>
            </el-form>
            <template #footer>
                <div class="dialog-footer">
                    <el-button type="primary" @click="submitForm">确认</el-button>
                    <el-button @click="closeDia">取消</el-button>
                </div>
            </template>
        </el-dialog>
    </div>
</template>
<script setup>
@@ -410,9 +382,9 @@
import { ElMessageBox } from "element-plus";
// import {userListNoPage} from "@/api/system/user.js";
import {
  getSalesLedgerWithProducts,
  ledgerListPage,
  productList,
    getSalesLedgerWithProducts,
    ledgerListPage,
    productList,
} from "@/api/salesManagement/salesLedger.js";
import { invoiceRegistrationSave } from "@/api/salesManagement/invoiceRegistration.js";
import useFormData from "@/hooks/useFormData";
@@ -426,261 +398,377 @@
const selectedRows = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
    current: 1,
    size: 100,
});
const total = ref(0);
// ç”¨æˆ·ä¿¡æ¯è¡¨å•弹框数据
const operationType = ref("");
const dialogFormVisible = ref(false);
const data = reactive({
  searchForm: {
    customerName: "",
    status: false,
    customerContractNo: undefined, // å®¢æˆ·åˆåŒå·
    projectName: undefined, // é¡¹ç›®åç§°
    createUer: undefined, // ç™»è®°äºº
    issueDate: undefined, // å¼€ç¥¨æ—¥æœŸ
    createTime: undefined, // å½•入日期:
  },
  form: {
    salesLedgerId: "",
    customerName: "",
    salesman: "",
    projectName: "",
    productData: [],
    invoiceNo: "",
    createUer: userStore.nickName,
    issueDate: dayjs().format("YYYY-MM-DD"),
  },
  rules: {
    salesLedgerId: [{ required: true, message: "请选择", trigger: "change" }],
    createUer: [{ required: true, message: "请选择", trigger: "blur" }],
    issueDate: [{ required: true, message: "请选择", trigger: "change" }],
    invoiceNo: [{ required: true, message: "请输入", trigger: "change" }],
    createTime: [{ required: true, message: "请选择", trigger: "change" }],
  },
    searchForm: {
        customerName: "",
        status: false,
        customerContractNo: undefined, // å®¢æˆ·åˆåŒå·
        projectName: undefined, // é¡¹ç›®åç§°
        createUer: undefined, // ç™»è®°äºº
        issueDate: undefined, // å¼€ç¥¨æ—¥æœŸ
        createTime: undefined, // å½•入日期:
        productCategory: "",
        isInvoice: 1
    },
    form: {
        salesLedgerId: "",
        customerName: "",
        salesman: "",
        projectName: "",
        productData: [],
        invoiceNo: "",
        createUer: userStore.nickName,
        issueDate: dayjs().format("YYYY-MM-DD"),
        selectedContractIds: [], // æ–°å¢žï¼šå­˜å‚¨æ‰€æœ‰é€‰ä¸­çš„合同ID
        isBatch: false // æ–°å¢žï¼šæ ‡è¯†æ˜¯å¦ä¸ºæ‰¹é‡æ“ä½œ
    },
    rules: {
        createUer: [{ required: true, message: "请选择", trigger: "blur" }],
        issueDate: [{ required: true, message: "请选择", trigger: "change" }],
        invoiceNo: [{ required: true, message: "请输入", trigger: "change" }],
        createTime: [{ required: true, message: "请选择", trigger: "change" }],
    },
});
const { form, rules } = toRefs(data);
const { form: searchForm, resetForm } = useFormData(data.searchForm);
const formattedNumber = (row, column, cellValue) => {
  if (cellValue == 0) {
    return parseFloat(cellValue).toFixed(2);
  }
  if (cellValue) {
    return parseFloat(cellValue).toFixed(2);
  } else {
    return cellValue;
  }
    if (cellValue == 0) {
        return parseFloat(cellValue).toFixed(2);
    }
    if (cellValue) {
        return parseFloat(cellValue).toFixed(2);
    } else {
        return cellValue;
    }
};
const formattedInputNumber = (value) => {
  return value ? parseFloat(value).toFixed(2) : 0;
    return value ? parseFloat(value).toFixed(2) : 0;
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
    page.current = 1;
    getList();
};
const paginationChange = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList = () => {
  tableLoading.value = true;
  ledgerListPage({ ...searchForm, ...page }).then((res) => {
    tableLoading.value = false;
    tableData.value = res.records;
    total.value = res.total;
    expandedRowKeys.value = [];
  });
    tableLoading.value = true;
    ledgerListPage({ ...searchForm, ...page }).then((res) => {
        tableLoading.value = false;
        tableData.value = res.records;
        total.value = res.total;
        expandedRowKeys.value = [];
    });
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  console.log("selection", selection);
  selectedRows.value = selection.filter(
    (item) => item.salesContractNo !== undefined
  );
    console.log("selection", selection);
    selectedRows.value = selection.filter(
        (item) => item.salesContractNo !== undefined
    );
};
const expandedRowKeys = ref([]);
// å±•开行
const expandChange = (row, expandedRows) => {
  if (expandedRows.length > 0) {
    expandedRowKeys.value = [];
    try {
      productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
        const index = tableData.value.findIndex((item) => item.id === row.id);
        if (index > -1) {
          tableData.value[index].children = res.data;
        }
        expandedRowKeys.value.push(row.id);
      });
    } catch (error) {
      console.log(error);
    }
  } else {
    expandedRowKeys.value = [];
  }
    if (expandedRows.length > 0) {
        expandedRowKeys.value = [];
        try {
            productList({ salesLedgerId: row.id, type: 1 }).then((res) => {
                const index = tableData.value.findIndex((item) => item.id === row.id);
                if (index > -1) {
                    tableData.value[index].children = res.data;
                }
                expandedRowKeys.value.push(row.id);
            });
        } catch (error) {
            console.log(error);
        }
    } else {
        expandedRowKeys.value = [];
    }
};
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
  return proxy.summarizeTable(param, [
    "contractAmount",
    "invoiceTotal",
    "noInvoiceAmountTotal",
  ]);
    return proxy.summarizeTable(param, [
        "contractAmount",
        "invoiceTotal",
        "noInvoiceAmountTotal",
    ]);
};
// å­è¡¨åˆè®¡æ–¹æ³•
const summarizeChildrenTable = (param) => {
  return proxy.summarizeTable(param, [
    "taxInclusiveUnitPrice",
    "taxInclusiveTotalPrice",
    "taxExclusiveTotalPrice",
    "invoiceNum",
    "invoiceAmount",
    "currentInvoiceAmount",
    "noInvoiceNum",
    "noInvoiceAmount",
    "currentInvoiceNum",
  ]);
    return proxy.summarizeTable(param, [
        "taxInclusiveUnitPrice",
        "taxInclusiveTotalPrice",
        "taxExclusiveTotalPrice",
        "invoiceNum",
        "invoiceAmount",
        "currentInvoiceAmount",
        "noInvoiceNum",
        "noInvoiceAmount",
        "currentInvoiceNum",
    ]);
};
// æ‰“开弹框
const openForm = () => {
  // åˆ¤æ–­æ˜¯å¦å¤šé€‰
  if (selectedRows.value.length != 1) {
    proxy.$modal.msgError("请选择一条合同");
    return;
  }
  form.value = {};
  productData.value = [];
  getSalesLedgerWithProducts({ id: selectedRows.value[0].id }).then((res) => {
    form.value = { ...res };
    form.value.createTime = dayjs().format("YYYY-MM-DD");
    form.value.issueDate = dayjs().format("YYYY-MM-DD");
    form.value.createUer = userStore.nickName;
    productData.value = form.value.productData.map((item) => {
      return item;
    });
    dialogFormVisible.value = true;
    console.log("productData.value ", productData.value);
  });
    // åˆ¤æ–­æ˜¯å¦é€‰æ‹©äº†åˆåŒ
    if (selectedRows.value.length === 0) {
        proxy.$modal.msgError("请至少选择一条合同");
        return;
    }
    // æ£€æŸ¥æ‰€æœ‰é€‰æ‹©çš„合同是否具有相同的客户名称
    const firstRow = selectedRows.value[0];
    const isSameCustomer = selectedRows.value.every(row =>
        row.customerName === firstRow.customerName
    );
    if (!isSameCustomer) {
        proxy.$modal.msgError("请选择相同客户名称的合同");
        return;
    }
    // å…è®¸ä¸åŒçš„销售合同号批量处理,无需检查重复
    form.value = {};
    productData.value = [];
    // åŠ è½½æ‰€æœ‰é€‰ä¸­åˆåŒçš„äº§å“æ•°æ®
    const promises = selectedRows.value.map(row =>
        getSalesLedgerWithProducts({ id: row.id })
    );
    Promise.all(promises).then(results => {
        // åˆå¹¶æ‰€æœ‰åˆåŒçš„产品数据,并为每个产品添加对应的合同信息
        const allProductData = [];
        results.forEach((result, index) => {
            const contract = selectedRows.value[index];
            const contractId = contract.id;
            if (result.productData) {
                result.productData.forEach(item => {
                    allProductData.push({
                        ...item,
                        id: contractId, // æ˜Žç¡®è®¾ç½®åˆåŒID
                        salesContractNo: contract.salesContractNo, // æ·»åŠ é”€å”®åˆåŒå·
                        customerName: contract.customerName, // æ·»åŠ å®¢æˆ·åç§°
                        customerContractNo: contract.customerContractNo // æ·»åŠ å®¢æˆ·åˆåŒå·
                    });
                });
            }
        });
        // è®¾ç½®è¡¨å•数据(使用第一个合同的基本信息,销售合同号留空)
        form.value = { ...results[0] };
        form.value.createTime = dayjs().format("YYYY-MM-DD");
        form.value.issueDate = dayjs().format("YYYY-MM-DD");
        form.value.createUer = userStore.nickName;
        form.value.selectedContractIds = selectedRows.value.map(row => row.id); // å­˜å‚¨æ‰€æœ‰é€‰ä¸­çš„合同ID
        form.value.salesContractNo = ""; // é”€å”®åˆåŒå·ç•™ç©ºï¼Œå› ä¸ºä¼šåœ¨äº§å“è¡¨æ ¼ä¸­åˆ†åˆ«æ˜¾ç¤º
        productData.value = allProductData;
        dialogFormVisible.value = true;
        console.log("productData.value ", productData.value);
    });
};
// æäº¤è¡¨å•
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      form.value.productData = proxy.HaveJson(productData.value);
      invoiceRegistrationSave(form.value).then((res) => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
        getList();
      });
    }
  });
    proxy.$refs["formRef"].validate((valid) => {
        if (valid) {
            // å¦‚果是批量操作,将所有合同的数据放在一个数组里,只调用一次接口
            if (selectedRows.value.length > 1) {
                // åˆ›å»ºåŒ…含所有合同数据的数组
                const batchData = selectedRows.value.map(contract => {
                    // ç­›é€‰å‡ºå±žäºŽå½“前合同的产品数据
                    const contractProductData = productData.value.filter(item =>
                        item.salesLedgerId === contract.id
                    );
                    // ä¸ºæ¯ä¸ªé”€å”®åˆåŒå·åˆ›å»ºç‹¬ç«‹çš„对象
                    return {
                        // åŸºç¡€è¡¨å•数据
                        issueDate: form.value.issueDate,
                        createTime: form.value.createTime,
                        createUer: form.value.createUer,
                        invoiceNo: form.value.invoiceNo,
                        // åˆåŒå®žé™…信息
                        id: contract.id, // ä½¿ç”¨id作为字段名,值为salesLedgerId
                        salesContractNo: contract.salesContractNo, // ä½¿ç”¨å®žé™…的销售合同号
                        customerName: contract.customerName, // ä½¿ç”¨å®žé™…的客户名称
                        customerId: contract.customerId, // æ·»åŠ å®¢æˆ·ID
                        customerContractNo: contract.customerContractNo, // ä½¿ç”¨å®žé™…的客户合同号
                        projectName: contract.projectName, // ä½¿ç”¨å®žé™…的项目名称
                        salesman: contract.salesman, // ä½¿ç”¨å®žé™…的业务员
                        // äº§å“æ•°æ®
                        productData: proxy.HaveJson(contractProductData),
                        // æ‰¹é‡æ ‡è¯†
                        isBatch: true
                    };
                });
                // åªè°ƒç”¨ä¸€æ¬¡æŽ¥å£ï¼Œä¼ é€’包含所有合同数据的数组
                invoiceRegistrationSave(batchData).then(() => {
                    proxy.$modal.msgSuccess("批量新增成功");
                    closeDia();
                    getList();
                });
            } else {
                // å•个合同提交逻辑 - ä¹Ÿä»¥æ•°ç»„形式传递
                const singleContract = selectedRows.value[0];
                const singleFormArray = [
                    {
                        // åŸºç¡€è¡¨å•数据
                        issueDate: form.value.issueDate,
                        createTime: form.value.createTime,
                        createUer: form.value.createUer,
                        invoiceNo: form.value.invoiceNo,
                        // åˆåŒå®žé™…信息
                        id: singleContract.id, // ä½¿ç”¨id作为字段名,值为salesLedgerId
                        salesContractNo: singleContract.salesContractNo, // ä½¿ç”¨å®žé™…的销售合同号
                        customerName: singleContract.customerName, // ä½¿ç”¨å®žé™…的客户名称
                        customerId: singleContract.customerId, // æ·»åŠ å®¢æˆ·ID
                        customerContractNo: singleContract.customerContractNo, // ä½¿ç”¨å®žé™…的客户合同号
                        projectName: singleContract.projectName, // ä½¿ç”¨å®žé™…的项目名称
                        salesman: singleContract.salesman, // ä½¿ç”¨å®žé™…的业务员
                        // äº§å“æ•°æ®
                        productData: proxy.HaveJson(productData.value),
                        // æ‰¹é‡æ ‡è¯†
                        isBatch: false
                    }
                ];
                invoiceRegistrationSave(singleFormArray).then((res) => {
                    proxy.$modal.msgSuccess("提交成功");
                    closeDia();
                    getList();
                });
            }
        }
    });
};
// å…³é—­å¼¹æ¡†
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/invoiceRegistration/export", {}, "开票登记信息.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download("/invoiceRegistration/export", {}, "开票登记信息.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
// å¯¼å‡ºé”€å”®å°è´¦
const handleExport = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/sales/ledger/exportOne", { ...searchForm, ...page }, "开票登记.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
        .then(() => {
            proxy.download("/sales/ledger/exportOne", { ...searchForm, ...page }, "开票登记.xlsx");
        })
        .catch(() => {
            proxy.$modal.msg("已取消");
        });
};
//本次开票失焦操作
const invoiceNumBlur = (row) => {
  if (!row.currentInvoiceNum) {
    row.currentInvoiceNum = 0;
  }
  if (row.currentInvoiceNum > row.tempNoInvoiceNum) {
    proxy.$modal.msgWarning("本次开票数不得大于未开票数");
    row.currentInvoiceNum = 0;
  }
  // è®¡ç®—本次开票金额
  row.currentInvoiceAmount = (
    row.currentInvoiceNum * row.taxInclusiveUnitPrice
  ).toFixed(2);
  // è®¡ç®—未开票数
  row.noInvoiceNum = (row.originalNoInvoiceNum - row.currentInvoiceNum).toFixed(
    2
  );
  // è®¡ç®—未开票金额
  row.noInvoiceAmount = (
    row.tempnoInvoiceAmount - row.currentInvoiceAmount
  ).toFixed(2);
    if (!row.currentInvoiceNum) {
        row.currentInvoiceNum = 0;
    }
    if (row.currentInvoiceNum > row.tempNoInvoiceNum) {
        proxy.$modal.msgWarning("本次开票数不得大于未开票数");
        row.currentInvoiceNum = 0;
    }
    // è®¡ç®—本次开票金额
    row.currentInvoiceAmount = (
        row.currentInvoiceNum * row.taxInclusiveUnitPrice
    ).toFixed(2);
    // è®¡ç®—未开票数
    row.noInvoiceNum = (row.originalNoInvoiceNum - row.currentInvoiceNum).toFixed(
        2
    );
    // è®¡ç®—未开票金额
    row.noInvoiceAmount = (
        row.tempnoInvoiceAmount - row.currentInvoiceAmount
    ).toFixed(2);
};
// æœ¬æ¬¡å¼€ç¥¨é‡‘额失焦操作
const invoiceAmountBlur = (row) => {
  if (!row.currentInvoiceAmount) {
    row.currentInvoiceAmount = 0;
  }
  // è®¡ç®—是否超过开票总金额
  if (row.currentInvoiceAmount > row.tempnoInvoiceAmount) {
    proxy.$modal.msgWarning("本次开票金额不得大于未开票金额");
    row.currentInvoiceAmount = 0;
  }
  // è®¡ç®—本次开票数
  row.currentInvoiceNum = (
    row.currentInvoiceAmount / row.taxInclusiveUnitPrice
  ).toFixed(2);
  console.log("row.currentInvoiceNum ", row.currentInvoiceNum);
  console.log(" row.originalNoInvoiceNum  ", row.originalNoInvoiceNum);
  // è®¡ç®—未开票数
  row.noInvoiceNum = (row.originalNoInvoiceNum - row.currentInvoiceNum).toFixed(
    2
  );
  // è®¡ç®—未开票金额
  row.noInvoiceAmount = (
    row.tempnoInvoiceAmount - row.currentInvoiceAmount
  ).toFixed(2);
    if (!row.currentInvoiceAmount) {
        row.currentInvoiceAmount = 0;
    }
    // è®¡ç®—是否超过开票总金额
    if (row.currentInvoiceAmount > row.tempnoInvoiceAmount) {
        proxy.$modal.msgWarning("本次开票金额不得大于未开票金额");
        row.currentInvoiceAmount = 0;
    }
    // è®¡ç®—本次开票数
    row.currentInvoiceNum = (
        row.currentInvoiceAmount / row.taxInclusiveUnitPrice
    ).toFixed(2);
    console.log("row.currentInvoiceNum ", row.currentInvoiceNum);
    console.log(" row.originalNoInvoiceNum  ", row.originalNoInvoiceNum);
    // è®¡ç®—未开票数
    row.noInvoiceNum = (row.originalNoInvoiceNum - row.currentInvoiceNum).toFixed(
        2
    );
    // è®¡ç®—未开票金额
    row.noInvoiceAmount = (
        row.tempnoInvoiceAmount - row.currentInvoiceAmount
    ).toFixed(2);
};
onMounted(() => {
  getList();
    getList();
});
</script>
<style scoped lang="scss">
.table_list {
  margin-top: unset;
    margin-top: unset;
}
.flex {
  display: flex;
    display: flex;
}
.justify-between {
  justify-content: space-between;
    justify-content: space-between;
}
::v-deep(.el-checkbox__label) {
  font-weight: bold;
    font-weight: bold;
}
</style>
src/views/salesManagement/orderManagement/index.vue
@@ -59,7 +59,6 @@
            Â¥{{ scope.row.amount.toFixed(2) }}
          </template>
        </el-table-column>
        <el-table-column prop="paymentMethod" label="付款方式" width="120" />
        <el-table-column prop="status" label="订单状态" width="100">
          <template #default="scope">
            <el-tag :type="getStatusType(scope.row.status)">
@@ -131,15 +130,6 @@
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="付款方式" prop="paymentMethod">
              <el-select v-model="form.paymentMethod" placeholder="请选择付款方式" style="width: 100%">
                <el-option label="全款到付" value="全款到付"></el-option>
                <el-option label="分期付款" value="分期付款"></el-option>
                <el-option label="月结" value="月结"></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="订单状态" prop="status">
              <el-select v-model="form.status" placeholder="请选择状态" style="width: 100%">
@@ -246,11 +236,10 @@
  {
    id: 1,
    orderNo: 'ORD202312001',
    customer: '上海科技有限公司',
    salesperson: '陈志强',
    customer: '广州科技公司',
    salesperson: '张三',
    orderDate: '2023-12-01',
    amount: 50000.00,
    paymentMethod: '全款到付',
    status: '待审核',
    remark: '重要客户订单'
  },
@@ -261,7 +250,6 @@
    salesperson: '刘雅婷',
    orderDate: '2023-12-02',
    amount: 35000.00,
    paymentMethod: '分期付款',
    status: '已审核',
    remark: '常规订单'
  },
@@ -272,7 +260,6 @@
    salesperson: '王建国',
    orderDate: '2023-12-03',
    amount: 28000.00,
    paymentMethod: '月结',
    status: '已发货',
    remark: '新客户订单'
  }
@@ -291,7 +278,6 @@
  salesperson: '',
  orderDate: '',
  amount: 0,
  paymentMethod: '',
  status: '待审核',
  remark: ''
})
@@ -301,7 +287,6 @@
  salesperson: [{ required: true, message: '请选择业务员', trigger: 'change' }],
  orderDate: [{ required: true, message: '请选择订单日期', trigger: 'change' }],
  amount: [{ required: true, message: '请输入订单金额', trigger: 'blur' }],
  paymentMethod: [{ required: true, message: '请选择付款方式', trigger: 'change' }],
  status: [{ required: true, message: '请选择状态', trigger: 'change' }]
}
@@ -360,7 +345,6 @@
  form.salesperson = ''
  form.orderDate = ''
  form.amount = 0
  form.paymentMethod = ''
  form.status = '待审核'
  form.remark = ''
  dialogVisible.value = true
src/views/salesManagement/paymentShipping/index.vue
@@ -60,7 +60,6 @@
            Â¥{{ scope.row.paidAmount }}
          </template>
        </el-table-column>
        <el-table-column prop="paymentMethod" label="付款方式" width="120" />
        <el-table-column prop="paymentStatus" label="付款状态" width="100">
          <template #default="scope">
            <el-tag :type="getPaymentStatusType(scope.row.paymentStatus)">
@@ -120,15 +119,6 @@
          <el-col :span="12">
            <el-form-item label="订单金额" prop="orderAmount">
              <el-input-number v-model="form.orderAmount" :precision="2" :min="0" style="width: 100%"></el-input-number>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="付款方式" prop="paymentMethod">
              <el-select v-model="form.paymentMethod" placeholder="请选择付款方式" style="width: 100%">
                <el-option label="全款到付" value="全款到付"></el-option>
                <el-option label="分期付款" value="分期付款"></el-option>
                <el-option label="月结" value="月结"></el-option>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
@@ -201,14 +191,6 @@
        </el-form-item>
        <el-form-item label="付款金额" prop="paymentAmount">
          <el-input-number v-model="paymentAmount" :precision="2" :min="0" :max="currentRecord.orderAmount" style="width: 100%"></el-input-number>
        </el-form-item>
        <el-form-item label="付款方式" prop="paymentMethod">
          <el-select v-model="paymentMethod" placeholder="请选择付款方式" style="width: 100%">
            <el-option label="现金" value="现金"></el-option>
            <el-option label="银行转账" value="银行转账"></el-option>
            <el-option label="支付宝" value="支付宝"></el-option>
            <el-option label="微信支付" value="微信支付"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="付款备注" prop="paymentRemark">
          <el-input type="textarea" v-model="paymentRemark" rows="3" placeholder="请输入付款备注"></el-input>
@@ -312,7 +294,6 @@
  orderNo: '',
  customer: '',
  orderAmount: 0,
  paymentMethod: '',
  paymentStatus: '未付款',
  shippingStatus: '待发货',
  shippingDate: '',
@@ -324,7 +305,6 @@
  // orderNo: [{ required: true, message: '请输入订单号', trigger: 'blur' }],
  customer: [{ required: true, message: '请选择客户', trigger: 'change' }],
  orderAmount: [{ required: true, message: '请输入订单金额', trigger: 'blur' }],
  paymentMethod: [{ required: true, message: '请选择付款方式', trigger: 'change' }],
  paymentStatus: [{ required: true, message: '请选择付款状态', trigger: 'change' }],
  shippingStatus: [{ required: true, message: '请选择发货状态', trigger: 'change' }]
}
@@ -335,7 +315,6 @@
const shippingDialogVisible = ref(false)
const currentRecord = ref({})
const paymentAmount = ref(0)
const paymentMethod = ref('')
const paymentRemark = ref('')
const shippingDate = ref('')
const logisticsCompany = ref('')
@@ -379,7 +358,6 @@
  form.orderNo = ''
  form.customer = ''
  form.orderAmount = 0
  form.paymentMethod = ''
  form.paymentStatus = '未付款'
  form.shippingStatus = '待发货'
  form.shippingDate = ''
@@ -404,7 +382,6 @@
const handlePayment = (row) => {
  currentRecord.value = row
  paymentAmount.value = row.orderAmount - row.paidAmount
  paymentMethod.value = ''
  paymentRemark.value = ''
  paymentDialogVisible.value = true
}
@@ -435,10 +412,6 @@
}
const savePayment = () => {
  if (!paymentMethod.value) {
    ElMessage.warning('请选择付款方式')
    return
  }
  currentRecord.value.paidAmount = Number(currentRecord.value.paidAmount) + paymentAmount.value
  if(currentRecord.value.paidAmount == currentRecord.value.orderAmount){
    currentRecord.value.paymentStatus = '已付款'
src/views/salesManagement/receiptPayment/index.vue
@@ -13,24 +13,6 @@
                prefix-icon="Search"
              />
            </el-form-item>
            <el-form-item label="客户合同号">
              <el-input
                v-model="searchForm.customerContractNo"
                placeholder="请输入"
                @change="handleQuery"
                clearable
                prefix-icon="Search"
              />
            </el-form-item>
            <el-form-item label="项目名称">
              <el-input
                v-model="searchForm.projectName"
                placeholder="请输入"
                @change="handleQuery"
                clearable
                prefix-icon="Search"
              />
            </el-form-item>
            <el-form-item>
              <el-checkbox
                v-model="searchForm.status"
@@ -122,7 +104,6 @@
                    size="small"
                    @click="changeEditType(scope.row)"
                    v-if="!scope.row.editType"
                                        :disabled="scope.row.registrant !== userStore.nickName"
                    >编辑</el-button
                  >
                  <el-button
@@ -131,7 +112,6 @@
                    size="small"
                    @click="saveReceiptPayment(scope.row)"
                    v-if="scope.row.editType"
                                        :disabled="scope.row.registrant !== userStore.nickName"
                    >保存</el-button
                  >
                  <el-button
@@ -139,7 +119,6 @@
                    type="primary"
                    size="small"
                    @click="delReceiptRecord(scope.row)"
                                        :disabled="scope.row.registrant !== userStore.nickName"
                    >删除</el-button
                  >
                </template>
@@ -155,23 +134,10 @@
          width="240"
        />
        <el-table-column
          label="客户合同号"
          prop="customerContractNo"
          show-overflow-tooltip
          width="240"
        />
        <el-table-column
          label="客户名称"
          prop="customerName"
          show-overflow-tooltip
          width="240"
        />
        <el-table-column
          label="项目名称"
          prop="projectName"
          show-overflow-tooltip
          width="340"
        />
        <el-table-column
          label="回款状态"
@@ -184,12 +150,12 @@
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column
          label="产品大类"
          prop="productCategory"
          show-overflow-tooltip
          width="100"
        />
<!--        <el-table-column-->
<!--          label="产品大类"-->
<!--          prop="productCategory"-->
<!--          show-overflow-tooltip-->
<!--          width="100"-->
<!--        />-->
        <el-table-column
          label="发票号"
          prop="invoiceNo"
@@ -349,7 +315,6 @@
                                v-model="form.registrant"
                                placeholder="请输入"
                                clearable
                                disabled
                            />
                        </el-form-item>
                    </el-col>
@@ -504,9 +469,7 @@
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  console.log("selection", selection);
  selectedRows.value = selection.filter(
    (item) => item.customerContractNo !== null
  );
  selectedRows.value = selection
};
// ä¸»è¡¨åˆè®¡æ–¹æ³•
const summarizeMainTable = (param) => {
src/views/salesManagement/receiptPaymentHistory/index.vue
@@ -10,24 +10,6 @@
          :prefix-icon="Search"
        />
      </el-form-item>
      <el-form-item label="客户合同号">
        <el-input
          v-model="searchForm.customerContractNo"
          placeholder="输入客户合同号"
          @change="handleQuery"
          clearable
          :prefix-icon="Search"
        />
      </el-form-item>
      <el-form-item label="项目名称">
        <el-input
          v-model="searchForm.projectName"
          placeholder="输入项目名称"
          @change="handleQuery"
          clearable
          :prefix-icon="Search"
        />
      </el-form-item>
      <el-form-item label="回款日期">
        <el-date-picker
          v-model="searchForm.receiptPaymentDate"
@@ -80,11 +62,6 @@
    width:240
  },
  {
    label: "客户合同号",
    prop: "customerContractNo",
    width:240
  },
  {
    label: "回款日期",
    prop: "receiptPaymentDate",
    width:100
@@ -93,11 +70,6 @@
    label: "客户名称",
    prop: "customerName",
    width:240
  },
  {
    label: "项目名称",
    prop: "projectName",
    width:200
  },
  {
    label: "回款金额(元)",
@@ -149,8 +121,6 @@
  receiptPaymentDate: [],
  receiptPaymentDateStart: undefined,
  receiptPaymentDateEnd: undefined,
  customerContractNo: undefined,
  projectName: undefined,
});
const { receipt_payment_type } = proxy.useDict("receipt_payment_type");
const isShowSummarySon = ref(true);
@@ -168,7 +138,11 @@
const getList = () => {
  tableLoading.value = true;
  const { receiptPaymentDate, ...rest } = searchForm;
  receiptPaymentHistoryListPage({ ...rest, ...page }).then((res) => {
  // å°†èŒƒå›´æ—¥æœŸå­—段传递给后端
  const params = { ...rest, ...page };
  // ç§»é™¤å›žæ¬¾æ—¥æœŸçš„默认值设置,只保留范围日期字段
  delete params.receiptPaymentDate;
  receiptPaymentHistoryListPage(params).then((res) => {
    tableLoading.value = false;
    tableData.value = res.records;
        page.total = res.total;
@@ -212,6 +186,12 @@
};
onMounted(() => {
  // è®¾ç½®å›žæ¬¾æ—¥æœŸèŒƒå›´é»˜è®¤å€¼ä¸ºå½“天
  const today = dayjs().format('YYYY-MM-DD');
  searchForm.receiptPaymentDate = [today, today];
  // è®¾ç½®èŒƒå›´æ—¥æœŸå­—段的起始和结束时间
  searchForm.receiptPaymentDateStart = dayjs(today).format('YYYY-MM-DD 00:00:00');
  searchForm.receiptPaymentDateEnd = dayjs(today).format('YYYY-MM-DD 23:59:59');
  getList();
});
</script>
src/views/salesManagement/salesLedger/fileList.vue
@@ -2,10 +2,11 @@
  <el-dialog v-model="dialogVisible" title="附件" width="40%" :before-close="handleClose">
    <el-table :data="tableData" border height="40vh">
      <el-table-column label="附件名称" prop="name" min-width="400" show-overflow-tooltip />
      <el-table-column fixed="right" label="操作" width="100" align="center">
      <el-table-column fixed="right" label="操作" width="150" align="center">
        <template #default="scope">
          <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">下载</el-button>
          <el-button link type="primary" size="small" @click="lookFile(scope.row)">预览</el-button>
          <el-button link type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
@@ -15,7 +16,9 @@
<script setup>
import { ref } from 'vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import filePreview from '@/components/filePreview/index.vue'
import { delCommonFile } from '@/api/publicApi/commonFile.js'
const dialogVisible = ref(false)
const tableData = ref([])
@@ -35,6 +38,27 @@
const lookFile = (row) => {
  filePreviewRef.value.open(row.url)
}
// åˆ é™¤é™„ä»¶
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除附件"${row.name}"吗?`, '删除确认', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  }).then(() => {
    delCommonFile([row.id]).then(() => {
      ElMessage.success('删除成功')
      // ä»Žåˆ—表中移除已删除的附件
      const index = tableData.value.findIndex(item => item.id === row.id)
      if (index !== -1) {
        tableData.value.splice(index, 1)
      }
    }).catch(() => {
      ElMessage.error('删除失败')
    })
  }).catch(() => {
    proxy.$modal.msg('已取消删除')
  })
}
defineExpose({
  open
})
src/views/salesManagement/salesLedger/index.vue
@@ -6,16 +6,8 @@
          <el-input v-model="searchForm.customerName" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="客户合同号:">
          <el-input v-model="searchForm.customerContractNo" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="销售合同号:">
          <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="项目名称:">
          <el-input v-model="searchForm.projectName" placeholder="请输入" clearable prefix-icon="Search"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="录入日期:">
@@ -41,45 +33,47 @@
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 21em)">
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column type="expand">
          <template #default="props">
            <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
              <el-table-column align="center" label="序号" type="index" width="60" />
              <el-table-column label="产品名称" prop="productCategory"/>
              <el-table-column label="产品高度" prop="specificationModel" width="100">
                <template #default="scope">
                  <div>{{ scope.row.specificationModel }}{{ scope.row.unit }}</div>
                </template>
              </el-table-column>
              <el-table-column label="数量" prop="quantity"/>
              <el-table-column label="单价(美元)/ä»¶" prop="taxInclusiveUnitPrice" :formatter="formattedNumber"></el-table-column>
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="单位" prop="unit" />
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column label="含税总价(美元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
              <el-table-column label="不含税总价(美元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
              <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
            </el-table>
          </template>
        </el-table-column>
        <el-table-column align="center" label="序号" type="index" width="60" />
        <el-table-column label="销售合同号" prop="salesContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户合同号" prop="customerContractNo" width="180" show-overflow-tooltip />
        <el-table-column label="客户名称" prop="customerName" width="300" show-overflow-tooltip />
        <el-table-column label="业务员" prop="salesman" width="100" show-overflow-tooltip />
        <el-table-column label="项目名称" prop="projectName" width="180" show-overflow-tooltip />
        <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip />
        <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip
          :formatter="formattedNumber" />
        <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
        <el-table-column label="发货车牌" prop="shippingCarNumber" width="120" show-overflow-tooltip>
          <template #default="scope">
            <div>
              <div v-if="scope.row.shippingCarNumber">{{ scope.row.shippingCarNumber }}</div>
              <el-tag v-else type="warning">未发货</el-tag>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="发货日期" prop="shippingDate" width="120" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip />
        <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="200" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" :disabled="scope.row.invoiceTotal>0 || scope.row.entryPersonName !== userStore.nickName" @click="openForm('edit', scope.row)">编辑</el-button>
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button>
<!--            <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详情</el-button>-->
            <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button>
            <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>
            <el-button v-if="!scope.row.shippingCarNumber" link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>
          </template>
        </el-table-column>
      </el-table>
@@ -97,7 +91,9 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="业务员:" prop="salesman">
              <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'">
              <el-select v-model="form.salesman"                 filterable
                         default-first-option
                         :reserve-keyword="false" placeholder="请选择" clearable :disabled="operationType === 'view'">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                  :value="item.nickName" />
              </el-select>
@@ -106,26 +102,14 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户合同号:" prop="customerContractNo">
              <el-input v-model="form.customerContractNo" placeholder="请输入" clearable :disabled="operationType === 'view'"/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="客户名称:" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'">
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable>
                <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
                  {{
                    item.customerName + "——" + item.taxpayerIdentificationNumber
                  }}
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="项目名称:" prop="projectName">
              <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
                    <el-col :span="12">
@@ -138,7 +122,9 @@
        <el-row :gutter="30">
                    <el-col :span="12">
                        <el-form-item label="录入人:" prop="entryPerson">
                            <el-select v-model="form.entryPerson" placeholder="请选择" clearable @change="changs" disabled>
                            <el-select v-model="form.entryPerson"                 filterable
                         default-first-option
                         :reserve-keyword="false" placeholder="请选择" clearable @change="changs">
                                <el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
                            </el-select>
                        </el-form-item>
@@ -150,13 +136,7 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="付款方式">
              <el-input v-model="form.paymentMethod" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-form-item label="产品信息:" prop="entryDate">
            <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button>
@@ -167,18 +147,15 @@
          :summary-method="summarizeMainTable">
          <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" />
          <el-table-column align="center" label="序号" type="index" width="60" />
          <el-table-column label="产品名称" prop="productCategory" />
          <el-table-column label="产品高度" prop="specificationModel">
            <template #default="scope">
              <div>{{ scope.row.specificationModel }}{{ scope.row.unit }}</div>
            </template>
          </el-table-column>
          <!-- <el-table-column label="单位" prop="unit" /> -->
          <el-table-column label="产品大类" prop="productCategory" />
          <el-table-column label="规格型号" prop="specificationModel" />
          <el-table-column label="绑定机器" prop="speculativeTradingName" />
          <el-table-column label="单位" prop="unit" />
          <el-table-column label="数量" prop="quantity" />
          <!-- <el-table-column label="税率(%)" prop="taxRate" /> -->
          <el-table-column label="单价(美元)/ä»¶" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
          <!-- <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
          <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" /> -->
          <el-table-column label="税率(%)" prop="taxRate" />
          <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
          <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
          <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
          <el-table-column fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'">
            <template #default="scope">
              <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
@@ -222,7 +199,7 @@
      <el-form :model="productForm" label-width="140px" label-position="top" :rules="productRules" ref="productFormRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品名称:" prop="productCategory">
            <el-form-item label="产品大类:" prop="productCategory">
              <!-- <el-select v-model="productForm.productCategory" placeholder="请选择" clearable>
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
              </el-select> -->
@@ -233,7 +210,7 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品高度:" prop="productModelId">
            <el-form-item label="规格型号:" prop="productModelId">
              <el-select v-model="productForm.productModelId" placeholder="请选择" clearable @change="getProductModel">
                <el-option v-for="item in modelOptions" :key="item.id" :label="item.model" :value="item.id" />
              </el-select>
@@ -241,8 +218,15 @@
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="绑定机器:">
              <el-input v-model="productForm.speculativeTradingName" placeholder="请先选择规格型号" clearable disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="高度单位:" prop="unit">
            <el-form-item label="单位:" prop="unit">
              <el-input v-model="productForm.unit" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
@@ -258,7 +242,7 @@
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="单价(美元)/件:" prop="taxInclusiveUnitPrice">
            <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
              <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%"
                                                             :precision="2"
                                                             placeholder="请输入" clearable @change="calculateFromUnitPrice" />
@@ -266,19 +250,20 @@
          </el-col>
                    <el-col :span="12">
                        <el-form-item label="数量:" prop="quantity">
                            <el-input-number  :min="0" v-model="productForm.quantity" placeholder="请输入" clearable
                            <el-input-number  :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请输入" clearable
                                                                :precision="2"
                                                                @change="calculateFromQuantity" style="width: 100%" />
                        </el-form-item>
                    </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="含税总价(美元):" prop="taxInclusiveTotalPrice">
            <el-form-item label="含税总价(元):" prop="taxInclusiveTotalPrice">
              <el-input v-model="productForm.taxInclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromTotalPrice" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不含税总价(美元):" prop="taxExclusiveTotalPrice">
            <el-form-item label="不含税总价(元):" prop="taxExclusiveTotalPrice">
              <el-input v-model="productForm.taxExclusiveTotalPrice" placeholder="请输入" clearable @change="calculateFromExclusiveTotalPrice" />
            </el-form-item>
          </el-col>
@@ -324,7 +309,7 @@
                    <div v-for="(item, index) in printData" :key="index" class="print-page">
                        <div class="delivery-note">
                            <div class="header">
                                <div class="company-name">双奇点有限责任公司</div>
                                <div class="company-name">海川开心食品有限公司</div>
                                <div class="document-title">零售发货单</div>
                            </div>
                            
@@ -335,12 +320,15 @@
                                        <span class="value">{{ formatDate(item.createTime) }}</span>
                                    </div>
                                    <div>
                                        <span class="label">客户名称:</span>
                                        <span class="value">{{ item.customerName || '张爱有' }}</span>
                                        <span class="label">发货车牌号:</span>
                                        <span class="value">{{ item.shippingCarNumber }}</span>
                                    </div>
                                </div>
                                <div class="info-row">
                                    <div>
                                        <span class="label">客户名称:</span>
                                        <span class="value">{{ item.customerName || '张爱有' }}</span>
                                    </div>
                                    <span class="label">单号:</span>
                                    <span class="value">{{ item.salesContractNo }}</span>
                                </div>
@@ -441,11 +429,19 @@
                <el-row :gutter="30">
                    <el-col :span="24">
                        <el-form-item label="发货车牌号:" prop="shippingCarNumber">
                            <el-input
              <el-select v-model="deliveryForm.shippingCarNumber" filterable allow-create placeholder="请选择发货车牌号">
                <el-option key="1" label="新A H5153" value="新A H5153"/>
                <el-option key="2" label="新A H4232" value="新A H4232"/>
                <el-option key="3" label="新A H4001" value="新A H4001"/>
                <el-option key="4" label="新A H6409" value="新A H6409"/>
                <el-option key="5" label="新A G7446" value="新A G7446"/>
                <el-option key="6" label="新H 80369" value="新H 80369"/>
              </el-select>
                            <!-- <el-input
                                v-model="deliveryForm.shippingCarNumber"
                                placeholder="请输入发货车牌号"
                                clearable
                            />
                            /> -->
                        </el-form-item>
                    </el-col>
                </el-row>
@@ -509,9 +505,7 @@
const data = reactive({
  searchForm: {
    customerName: "", // å®¢æˆ·åç§°
    customerContractNo: "", // å®¢æˆ·åˆåŒç¼–号
    salesContractNo: "", // é”€å”®åˆåŒç¼–号
    projectName: "", // é¡¹ç›®åç§°
    entryDate: null, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
@@ -519,23 +513,16 @@
  form: {
    salesContractNo: "",
    salesman: "",
    customerContractNo: "",
    customerId: "",
    projectName: "",
    entryPerson: "",
    entryDate: "",
    maintenanceTime: "",
    productData: [],
    executionDate: "",
    paymentMethod: "",
  },
  rules: {
    salesman: [{ required: true, message: "请选择", trigger: "change" }],
    customerContractNo: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    customerId: [{ required: true, message: "请选择", trigger: "change" }],
    projectName: [{ required: true, message: "请输入", trigger: "blur" }],
    entryPerson: [{ required: true, message: "请选择", trigger: "change" }],
    entryDate: [{ required: true, message: "请选择", trigger: "change" }],
    executionDate: [{ required: true, message: "请选择", trigger: "change" }],
@@ -558,7 +545,7 @@
    taxInclusiveTotalPrice: "",
    taxExclusiveTotalPrice: "",
    invoiceType: "",
    cartonSpecifications:'',
    speculativeTradingName: "",
  },
  productRules: {
    productCategory: [{ required: true, message: "请选择", trigger: "change" }],
@@ -571,7 +558,6 @@
    taxInclusiveUnitPrice: [
      { required: true, message: "请输入", trigger: "blur" },
    ],
    cartonSpecifications: [{ required: true, message: "请输入", trigger: "blur" }],
    taxRate: [{ required: true, message: "请选择", trigger: "change" }],
    taxInclusiveTotalPrice: [
      { required: true, message: "请输入", trigger: "blur" },
@@ -628,7 +614,11 @@
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  // åªæœ‰åœ¨ç‚¹å‡»æœç´¢æŒ‰é’®æ—¶æ‰é‡ç½®é¡µç åˆ°ç¬¬ä¸€é¡µ
  // é¿å…è¡¨å•字段change事件干扰分页
  if (arguments.length === 0) {
    page.current = 1;
  }
    expandedRowKeys.value = [];
  getList();
};
@@ -640,7 +630,11 @@
const getList = () => {
  tableLoading.value = true;
  const { entryDate, ...rest } = searchForm;
  ledgerListPage({ ...rest, ...page })
  // å°†èŒƒå›´æ—¥æœŸå­—段传递给后端
  const params = { ...rest, ...page };
  // ç§»é™¤å½•入日期的默认值设置,只保留范围日期字段
  delete params.entryDate;
  ledgerListPage(params)
    .then((res) => {
      tableLoading.value = false;
      tableData.value = res.records;
@@ -653,7 +647,7 @@
      tableLoading.value = false;
    });
};
// èŽ·å–äº§å“åç§°tree数据
// èŽ·å–äº§å“å¤§ç±»tree数据
const getProductOptions = () => {
  productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
@@ -665,7 +659,6 @@
// èŽ·å–tree子数据
const getModels = (value) => {
  productForm.value.productCategory = findNodeById(productOptions.value, value);
  productForm.value.productId = value;
  modelList({ id: value }).then((res) => {
    modelOptions.value = res;
  });
@@ -676,9 +669,11 @@
  if (index !== -1) {
    productForm.value.specificationModel = modelOptions.value[index].model;
    productForm.value.unit = modelOptions.value[index].unit;
    productForm.value.speculativeTradingName = modelOptions.value[index].speculativeTradingName || "";
  } else {
    productForm.value.specificationModel = null;
    productForm.value.unit = null;
    productForm.value.speculativeTradingName = "";
  }
};
const findNodeById = (nodes, productId) => {
@@ -780,7 +775,7 @@
  //     form.value.entryPerson = userAll.user.userId // è®¾ç½®é»˜è®¤ä¸šåŠ¡å‘˜ä¸ºå½“å‰ç”¨æˆ·
  //   }
  // });
  form.value.entryDate = getCurrentDate(); // è®¾ç½®é»˜è®¤å½•入日期为当前日期
  // ç§»é™¤å½•入日期默认值设置,只处理范围日期字段
  dialogFormVisible.value = true;
};
function changs(val) {
@@ -878,7 +873,6 @@
        }else{
          productData.value[productIndex.value] = { ...productForm.value }
        }
        console.log('productData',productData)
        closeProductDia();
      }
    }
@@ -1177,7 +1171,7 @@
      <div class="print-page">
        <div class="delivery-note">
          <div class="header">
            <div class="company-name">双奇点有限责任公司</div>
            <div class="company-name">海川开心食品有限公司</div>
            <div class="document-title">零售发货单</div>
          </div>
          
@@ -1404,10 +1398,10 @@
// æ ¹æ®ä¸å«ç¨Žæ€»ä»·è®¡ç®—含税单价和数量
const calculateFromExclusiveTotalPrice = () => {
    // if (!productForm.value.taxRate) {
    //     proxy.$modal.msgWarning("请先选择税率");
    //     return;
    // }
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  
  const exclusiveTotalPrice = parseFloat(productForm.value.taxExclusiveTotalPrice);
@@ -1433,10 +1427,10 @@
// æ ¹æ®æ•°é‡å˜åŒ–计算总价
const calculateFromQuantity = () => {
    // if (!productForm.value.taxRate) {
    //     proxy.$modal.msgWarning("请先选择税率");
    //     return;
    // }
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  
  const quantity = parseFloat(productForm.value.quantity);
@@ -1465,10 +1459,10 @@
// æ ¹æ®å«ç¨Žå•价变化计算总价
const calculateFromUnitPrice = () => {
    // if (!productForm.value.taxRate) {
    //     proxy.$modal.msgWarning("请先选择税率");
    //     return;
    // }
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  
  const quantity = parseFloat(productForm.value.quantity);
@@ -1497,10 +1491,10 @@
// æ ¹æ®ç¨ŽçŽ‡å˜åŒ–è®¡ç®—ä¸å«ç¨Žæ€»ä»·
const calculateFromTaxRate = () => {
    // if (!productForm.value.taxRate) {
    //     proxy.$modal.msgWarning("请先选择税率");
    //     return;
    // }
    if (!productForm.value.taxRate) {
        proxy.$modal.msgWarning("请先选择税率");
        return;
    }
  if (isCalculating.value) return;
  
  const inclusiveTotalPrice = parseFloat(productForm.value.taxInclusiveTotalPrice);
@@ -1537,7 +1531,7 @@
const openDeliveryForm = (row) => {
  currentDeliveryRow.value = row;
  deliveryForm.value = {
    shippingDate: getCurrentDate(),
    shippingDate: "", // ç§»é™¤é»˜è®¤å€¼è®¾ç½®
    shippingCarNumber: "",
  };
  deliveryFormVisible.value = true;
@@ -1572,7 +1566,13 @@
};
onMounted(() => {
    getList();
  // è®¾ç½®å½•入日期范围默认值为当天
  const today = dayjs().format('YYYY-MM-DD');
  searchForm.entryDate = [today, today];
  // è®¾ç½®èŒƒå›´æ—¥æœŸå­—段的起始和结束时间
  searchForm.entryDateStart = today;
  searchForm.entryDateEnd = today;
  getList();
});
</script>
src/views/salesManagement/salesQuotation/index.vue
@@ -144,15 +144,6 @@
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="付款方式" prop="paymentMethod">
                <el-select v-model="form.paymentMethod" placeholder="请选择付款方式" style="width: 100%">
                  <el-option label="全款到付" value="全款到付"></el-option>
                  <el-option label="分期付款" value="分期付款"></el-option>
                  <el-option label="月结" value="月结"></el-option>
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="交货期" prop="deliveryPeriod">
                <el-date-picker
                  v-model="form.deliveryPeriod"
@@ -302,7 +293,6 @@
        <el-descriptions-item label="业务员">{{ currentQuotation.salesperson }}</el-descriptions-item>
        <el-descriptions-item label="报价日期">{{ currentQuotation.quotationDate }}</el-descriptions-item>
        <el-descriptions-item label="有效期至">{{ currentQuotation.validDate }}</el-descriptions-item>
        <el-descriptions-item label="付款方式">{{ currentQuotation.paymentMethod }}</el-descriptions-item>
        <el-descriptions-item label="交货期">{{ currentQuotation.deliveryPeriod }}</el-descriptions-item>
<!--        <el-descriptions-item label="报价状态">-->
<!--          <el-tag :type="getStatusType(currentQuotation.status)">{{ currentQuotation.status }}</el-tag>-->
@@ -394,7 +384,6 @@
  salesperson: [{ required: true, message: '请选择业务员', trigger: 'change' }],
  quotationDate: [{ required: true, message: '请选择报价日期', trigger: 'change' }],
  validDate: [{ required: true, message: '请选择有效期', trigger: 'change' }],
  paymentMethod: [{ required: true, message: '请选择付款方式', trigger: 'change' }],
  deliveryPeriod: [{ required: true, message: '请选择交货期', trigger: 'change' }]
}
const userList = ref([]);
vite.config.js
@@ -6,17 +6,11 @@
export default defineConfig(({ mode, command }) => {
  const env = loadEnv(mode, process.cwd());
  const { VITE_APP_ENV } = env;
  const baseUrl =
      env.VITE_APP_ENV === "development"
          ? "http://114.132.189.42:9036"
          : env.VITE_BASE_API;
  const javaUrl =
      env.VITE_APP_ENV === "development"
          ? "http://114.132.189.42:9037"
          : env.VITE_JAVA_API;
  const baseUrl = env.VITE_APP_ENV === "development" ? "http://192.168.1.35:7003" : env.VITE_BASE_API;
  const javaUrl = env.VITE_APP_ENV === "development" ? "http://114.132.189.42:9037" : env.VITE_JAVA_API;
  return {
    define:{
      __BASE_API__: JSON.stringify(javaUrl)
    define: {
      __BASE_API__: JSON.stringify(javaUrl),
    },
    // éƒ¨ç½²ç”Ÿäº§çŽ¯å¢ƒå’Œå¼€å‘çŽ¯å¢ƒä¸‹çš„URL。
    // é»˜è®¤æƒ…况下,vite ä¼šå‡è®¾ä½ çš„应用是被部署在一个域名的根路径上
@@ -59,7 +53,7 @@
        "/dev-api": {
          target: baseUrl,
          changeOrigin: true,
          rewrite: (p) => p.replace(/^\/dev-api/, ""),
          rewrite: p => p.replace(/^\/dev-api/, ""),
        },
        // springdoc proxy
        "^/v3/api-docs/(.*)": {
@@ -74,7 +68,7 @@
          {
            postcssPlugin: "internal:charset-removal",
            AtRule: {
              charset: (atRule) => {
              charset: atRule => {
                if (atRule.name === "charset") {
                  atRule.remove();
                }