diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizClientController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizClientController.java index af182a83..a003db09 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizClientController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizClientController.java @@ -1,14 +1,17 @@ package com.ruoyi.web.controller.bid; import java.util.List; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.system.domain.bid.BizClient; import com.ruoyi.system.service.bid.IBizClientService; @@ -60,6 +63,35 @@ public class BizClientController extends BaseController { return toAjax(service.deleteBizClientByIds(clientIds)); } + // ========== Excel 导入导出 ========== + + @PreAuthorize("@ss.hasPermi('bid:client:export')") + @Log(title = "甲方客户", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, BizClient query) { + List list = service.selectBizClientList(query); + ExcelUtil util = new ExcelUtil(BizClient.class); + util.exportExcel(response, list, "客户数据"); + } + + @PreAuthorize("@ss.hasPermi('bid:client:import')") + @Log(title = "甲方客户", businessType = BusinessType.IMPORT) + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { + ExcelUtil util = new ExcelUtil(BizClient.class); + List clientList = util.importExcel(file.getInputStream()); + Long tenantId = getDeptId(); + if (tenantId == null) { tenantId = 1L; } + String message = service.importClient(clientList, updateSupport, getUsername(), tenantId); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + ExcelUtil util = new ExcelUtil(BizClient.class); + util.importTemplateExcel(response, "客户数据"); + } + /** * 查询客户的关联历史发货单 * 链路: client → client_quote → rfq → delivery_order diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizDeliveryOrderController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizDeliveryOrderController.java index 2a3746e7..42ef5c60 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizDeliveryOrderController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizDeliveryOrderController.java @@ -1,6 +1,7 @@ package com.ruoyi.web.controller.bid; import java.util.List; +import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -83,13 +84,27 @@ public class BizDeliveryOrderController extends BaseController { return toAjax(service.recall(id)); } - @PreAuthorize("@ss.hasPermi('bid:order:closeDate:edit')") + @PreAuthorize("@ss.hasPermi('bid:order:closeDate:edit') or @ss.hasPermi('bid:clientdelivery:closeDate:edit')") @Log(title = "结单时间", businessType = BusinessType.UPDATE) @PutMapping("/{id}/closeDate") public AjaxResult setCloseDate(@PathVariable Long id, @RequestParam String closeDate) { return toAjax(service.setCloseDate(id, closeDate, getUsername())); } + @PreAuthorize("@ss.hasPermi('bid:order:closeDate:edit') or @ss.hasPermi('bid:clientdelivery:closeDate:edit')") + @Log(title = "结单时间", businessType = BusinessType.UPDATE) + @PutMapping("/batchCloseDate") + public AjaxResult batchSetCloseDate(@RequestBody Map params) { + @SuppressWarnings("unchecked") + List rawIds = (List) params.get("ids"); + String closeDate = (String) params.get("closeDate"); + if (rawIds == null || rawIds.isEmpty()) { + return AjaxResult.error("请选择要结单的订单"); + } + List ids = rawIds.stream().map(Integer::longValue).collect(java.util.stream.Collectors.toList()); + return toAjax(service.batchSetCloseDate(ids, closeDate, getUsername())); + } + // ════════════════════════════════════════ // 物料发货记录 // ════════════════════════════════════════════ @@ -107,18 +122,66 @@ public class BizDeliveryOrderController extends BaseController { @PreAuthorize("@ss.hasPermi('bid:order:transit')") @GetMapping("/transit/stats") public AjaxResult transitStats() { - return success(service.selectTransitStats()); + Long tenantId = getDeptId(); + return success(service.selectTransitStats(tenantId)); } - @PreAuthorize("@ss.hasPermi('bid:order:history')") + @PreAuthorize("@ss.hasPermi('bid:order:history') or @ss.hasPermi('bid:clientdelivery:closeDate')") @GetMapping("/history/stats") - public AjaxResult historyStats() { - return success(service.selectHistoryStats()); + public AjaxResult historyStats(@RequestParam(required = false) String type) { + Long tenantId = getDeptId(); + return success(service.selectHistoryStats(tenantId, type)); } @PreAuthorize("@ss.hasPermi('bid:order:closeDate:edit')") @GetMapping("/closeDate/stats") public AjaxResult closeDateStats() { - return success(service.selectCloseDateStats()); + Long tenantId = getDeptId(); + return success(service.selectCloseDateStats(tenantId)); + } + + /** 履约时效可视化 - 时间线数据(供应商履约 + 甲方履约通用) */ + @PreAuthorize("@ss.hasPermi('bid:order:timeline') or @ss.hasPermi('bid:clientdelivery:timeline') or @ss.hasPermi('bid:order:transit') or @ss.hasPermi('bid:order:history')") + @GetMapping("/timeline") + public AjaxResult timeline(@RequestParam(required = false) String type, + @RequestParam(required = false) String status, + @RequestParam(required = false) String dateFrom, + @RequestParam(required = false) String dateTo) { + Long tenantId = getDeptId(); + List> orders = service.selectTimelineData(tenantId, type, status, dateFrom, dateTo); + // 计算准时/延期统计数据 + long onTime = orders.stream().filter(o -> { + Object cd = o.get("actualCloseDate"); + Object dd = o.get("delayDate"); + if (cd == null) return false; + // 有延期日期时以延期日期为准,否则以约定交货日为准 + Object base = dd != null ? dd : o.get("deliveryDate"); + if (base == null) return false; + try { + return java.sql.Date.valueOf(cd.toString()).compareTo(java.sql.Date.valueOf(base.toString())) <= 0; + } catch (Exception e) { return false; } + }).count(); + + long delayed = orders.stream().filter(o -> { + Object cd = o.get("actualCloseDate"); + Object dd = o.get("delayDate"); + if (cd == null) return false; + Object base = dd != null ? dd : o.get("deliveryDate"); + if (base == null) return false; + try { + return java.sql.Date.valueOf(cd.toString()).compareTo(java.sql.Date.valueOf(base.toString())) > 0; + } catch (Exception e) { return false; } + }).count(); + + long pending = orders.stream().filter(o -> o.get("actualCloseDate") == null).count(); + + Map result = new java.util.HashMap<>(); + result.put("orders", orders); + Map stats = new java.util.HashMap<>(); + stats.put("onTime", onTime); + stats.put("delayed", delayed); + stats.put("pending", pending); + result.put("stats", stats); + return success(result); } } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizMaterialController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizMaterialController.java index 7e87479d..d2cf9f5a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizMaterialController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizMaterialController.java @@ -1,14 +1,17 @@ package com.ruoyi.web.controller.bid; import java.util.List; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.system.domain.bid.BizMaterial; import com.ruoyi.system.service.bid.IBizMaterialService; @@ -61,6 +64,35 @@ public class BizMaterialController extends BaseController { return toAjax(service.deleteBizMaterialByIds(materialIds)); } + // ========== Excel 导入导出 ========== + + @PreAuthorize("@ss.hasPermi('bid:material:export')") + @Log(title = "物料管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, BizMaterial query) { + List list = service.selectBizMaterialList(query); + ExcelUtil util = new ExcelUtil(BizMaterial.class); + util.exportExcel(response, list, "物料数据"); + } + + @PreAuthorize("@ss.hasPermi('bid:material:import')") + @Log(title = "物料管理", businessType = BusinessType.IMPORT) + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { + ExcelUtil util = new ExcelUtil(BizMaterial.class); + List materialList = util.importExcel(file.getInputStream()); + Long tenantId = getDeptId(); + if (tenantId == null) { tenantId = 1L; } + String message = service.importMaterial(materialList, updateSupport, getUsername(), tenantId); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + ExcelUtil util = new ExcelUtil(BizMaterial.class); + util.importTemplateExcel(response, "物料数据"); + } + // ========== 物料详情页接口 ========== @PreAuthorize("@ss.hasPermi('bid:material:detail')") diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizSupplierController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizSupplierController.java index 597d9f8e..e31d4caa 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizSupplierController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/bid/BizSupplierController.java @@ -1,14 +1,17 @@ package com.ruoyi.web.controller.bid; import java.util.List; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.system.domain.bid.BizSupplier; import com.ruoyi.system.service.bid.IBizSupplierService; @@ -54,4 +57,33 @@ public class BizSupplierController extends BaseController { public AjaxResult remove(@PathVariable Long[] supplierIds) { return toAjax(service.deleteBizSupplierByIds(supplierIds)); } + + // ========== Excel 导入导出 ========== + + @PreAuthorize("@ss.hasPermi('bid:supplier:export')") + @Log(title = "供应商管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, BizSupplier query) { + List list = service.selectBizSupplierList(query); + ExcelUtil util = new ExcelUtil(BizSupplier.class); + util.exportExcel(response, list, "供应商数据"); + } + + @PreAuthorize("@ss.hasPermi('bid:supplier:import')") + @Log(title = "供应商管理", businessType = BusinessType.IMPORT) + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { + ExcelUtil util = new ExcelUtil(BizSupplier.class); + List supplierList = util.importExcel(file.getInputStream()); + Long tenantId = getDeptId(); + if (tenantId == null) { tenantId = 1L; } + String message = service.importSupplier(supplierList, updateSupport, getUsername(), tenantId); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + ExcelUtil util = new ExcelUtil(BizSupplier.class); + util.importTemplateExcel(response, "供应商数据"); + } } diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml index b5253d47..506c2b75 100644 --- a/ruoyi-admin/src/main/resources/logback.xml +++ b/ruoyi-admin/src/main/resources/logback.xml @@ -1,6 +1,7 @@ + @@ -8,14 +9,38 @@ - + + ${log.path}/ruoyi.log + + ${log.path}/ruoyi.%d{yyyy-MM-dd}.log + 7 + + + ${log.pattern} + + + + + ${log.path}/error.log + + ${log.path}/error.%d{yyyy-MM-dd}.log + 30 + + + ${log.pattern} + + + ERROR + + + + + + + - - - - diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizClient.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizClient.java index e04161c7..6f3841d9 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizClient.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizClient.java @@ -1,19 +1,40 @@ package com.ruoyi.system.domain.bid; import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.annotation.Excel; public class BizClient extends BaseEntity { private Long clientId; private Long tenantId; + + @Excel(name = "客户编号", type = Excel.Type.IMPORT) private String clientNo; + + @Excel(name = "客户名称", type = Excel.Type.IMPORT) private String clientName; + + @Excel(name = "联系人", type = Excel.Type.IMPORT) private String contact; + + @Excel(name = "电话", type = Excel.Type.IMPORT) private String phone; + + @Excel(name = "邮箱", type = Excel.Type.IMPORT) private String email; + + @Excel(name = "城市", type = Excel.Type.IMPORT) private String city; + + @Excel(name = "地址", type = Excel.Type.IMPORT) private String address; + + @Excel(name = "等级", type = Excel.Type.IMPORT, combo = {"A", "B", "C"}, defaultValue = "B") private String grade; + + @Excel(name = "来源", type = Excel.Type.IMPORT) private String source; + + @Excel(name = "状态", type = Excel.Type.IMPORT, readConverterExp = "0=正常,1=停用", defaultValue = "0") private String status; public Long getClientId() { return clientId; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizMaterial.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizMaterial.java index 8f48f510..fddd827b 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizMaterial.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizMaterial.java @@ -1,25 +1,46 @@ package com.ruoyi.system.domain.bid; import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.annotation.Excel; public class BizMaterial extends BaseEntity { private Long materialId; private Long tenantId; private Long categoryId; + + @Excel(name = "物料编码", type = Excel.Type.IMPORT) private String materialCode; + + @Excel(name = "物料名称", type = Excel.Type.IMPORT) private String materialName; + + @Excel(name = "规格型号", type = Excel.Type.IMPORT) private String spec; + + @Excel(name = "单位", type = Excel.Type.IMPORT) private String unit; + + @Excel(name = "品牌", type = Excel.Type.IMPORT) private String brand; + + @Excel(name = "描述", type = Excel.Type.IMPORT) private String description; + + @Excel(name = "状态", type = Excel.Type.IMPORT, readConverterExp = "0=正常,1=停用", defaultValue = "0") private String status; // search helper private String categoryName; // 新增字段 + @Excel(name = "性能参数", type = Excel.Type.IMPORT) private String performanceParams; + + @Excel(name = "材质", type = Excel.Type.IMPORT) private String material; + + @Excel(name = "用途", type = Excel.Type.IMPORT) private String purpose; + private String imageUrl; public Long getMaterialId() { return materialId; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizSupplier.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizSupplier.java index 920488aa..25b5660f 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizSupplier.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/bid/BizSupplier.java @@ -1,16 +1,30 @@ package com.ruoyi.system.domain.bid; import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.annotation.Excel; public class BizSupplier extends BaseEntity { private Long supplierId; private Long tenantId; + + @Excel(name = "供应商名称", type = Excel.Type.IMPORT) private String supplierName; + + @Excel(name = "联系人", type = Excel.Type.IMPORT) private String contact; + + @Excel(name = "电话", type = Excel.Type.IMPORT) private String phone; + + @Excel(name = "邮箱", type = Excel.Type.IMPORT) private String email; + + @Excel(name = "地址", type = Excel.Type.IMPORT) private String address; + private Long userId; + + @Excel(name = "状态", type = Excel.Type.IMPORT, readConverterExp = "0=正常,1=停用", defaultValue = "0") private String status; public Long getSupplierId() { return supplierId; } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizClientMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizClientMapper.java index a39c313e..8a7507f4 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizClientMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizClientMapper.java @@ -1,6 +1,7 @@ package com.ruoyi.system.mapper.bid; import com.ruoyi.system.domain.bid.BizClient; +import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; @@ -12,4 +13,7 @@ public interface BizClientMapper { int deleteBizClientById(Long id); int deleteBizClientByIds(Long[] ids); List> selectClientDeliveryOrders(Long clientId); + + // 按客户编号查询(导入判重用) + BizClient selectBizClientByNo(@Param("tenantId") Long tenantId, @Param("clientNo") String clientNo); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizDeliveryOrderMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizDeliveryOrderMapper.java index ec360822..7891cd18 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizDeliveryOrderMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizDeliveryOrderMapper.java @@ -7,10 +7,10 @@ import java.util.List; import java.util.Map; public interface BizDeliveryOrderMapper { - List selectBizDeliveryOrderList(BizDeliveryOrder query); + List selectBizDeliveryOrderList(@Param("query") BizDeliveryOrder query); BizDeliveryOrder selectBizDeliveryOrderById(Long id); - int insertBizDeliveryOrder(BizDeliveryOrder record); - int updateBizDeliveryOrder(BizDeliveryOrder record); + int insertBizDeliveryOrder(@Param("record") BizDeliveryOrder record); + int updateBizDeliveryOrder(@Param("record") BizDeliveryOrder record); int deleteBizDeliveryOrderById(Long id); int deleteBizDeliveryOrderByIds(Long[] ids); @@ -25,9 +25,15 @@ public interface BizDeliveryOrderMapper { List> selectMaterialRecords(@Param("materialId") Long materialId); // 在途统计 - Map selectTransitStats(); + Map selectTransitStats(@Param("tenantId") Long tenantId); + // 履约时间线 + List> selectTimelineData(@Param("tenantId") Long tenantId, + @Param("type") String type, + @Param("status") String status, + @Param("dateFrom") String dateFrom, + @Param("dateTo") String dateTo); // 历史统计 - Map selectHistoryStats(); + Map selectHistoryStats(@Param("tenantId") Long tenantId, @Param("type") String type); // 结单统计 - Map selectCloseDateStats(); + Map selectCloseDateStats(@Param("tenantId") Long tenantId); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizMaterialMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizMaterialMapper.java index f4ad37a5..31308206 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizMaterialMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizMaterialMapper.java @@ -32,4 +32,7 @@ public interface BizMaterialMapper { // 根据物料名称精确匹配(同名称不同规格/品牌对比) List selectMaterialsByExactName(@Param("materialName") String materialName, @Param("excludeId") Long excludeId); + + // 按物料编码查询(导入判重用) + BizMaterial selectBizMaterialByCode(@Param("tenantId") Long tenantId, @Param("materialCode") String materialCode); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizSupplierMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizSupplierMapper.java index 73ab2a00..0da062f9 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizSupplierMapper.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/bid/BizSupplierMapper.java @@ -12,4 +12,7 @@ public interface BizSupplierMapper { int updateBizSupplier(BizSupplier record); int deleteBizSupplierById(Long id); int deleteBizSupplierByIds(Long[] ids); + + // 按供应商名称查询(导入判重用) + BizSupplier selectBizSupplierByName(@Param("tenantId") Long tenantId, @Param("supplierName") String supplierName); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizClientService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizClientService.java index beb9fa33..d72fbafe 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizClientService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizClientService.java @@ -12,4 +12,7 @@ public interface IBizClientService { int deleteBizClientById(Long id); int deleteBizClientByIds(Long[] ids); List> selectClientDeliveryOrders(Long clientId); + + // Excel批量导入 + String importClient(List clientList, Boolean updateSupport, String operName, Long tenantId); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizDeliveryOrderService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizDeliveryOrderService.java index 803a074c..eda687d8 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizDeliveryOrderService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizDeliveryOrderService.java @@ -17,14 +17,17 @@ public interface IBizDeliveryOrderService { int complete(Long id, String username); int recall(Long id); int setCloseDate(Long id, String closeDate, String username); + int batchSetCloseDate(List ids, String closeDate, String username); // 物料发货记录 List> selectMaterialRecords(Long materialId); // 在途统计 - Map selectTransitStats(); + Map selectTransitStats(Long tenantId); // 历史统计 - Map selectHistoryStats(); + Map selectHistoryStats(Long tenantId, String type); // 结单统计 - Map selectCloseDateStats(); + Map selectCloseDateStats(Long tenantId); + // 履约时间线 + List> selectTimelineData(Long tenantId, String type, String status, String dateFrom, String dateTo); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizMaterialService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizMaterialService.java index 139b9ef0..2c1b531c 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizMaterialService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizMaterialService.java @@ -26,4 +26,7 @@ public interface IBizMaterialService { // 根据物料名称精确匹配(同名称不同规格/品牌对比) List selectMaterialsByExactName(String materialName, Long excludeId); + + // Excel批量导入 + String importMaterial(List materialList, Boolean updateSupport, String operName, Long tenantId); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizSupplierService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizSupplierService.java index 6cabc3b6..a0b98ed3 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizSupplierService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/IBizSupplierService.java @@ -11,4 +11,7 @@ public interface IBizSupplierService { int updateBizSupplier(BizSupplier record); int deleteBizSupplierById(Long id); int deleteBizSupplierByIds(Long[] ids); + + // Excel批量导入 + String importSupplier(List supplierList, Boolean updateSupport, String operName, Long tenantId); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizClientServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizClientServiceImpl.java index a1513acf..329abc26 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizClientServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizClientServiceImpl.java @@ -3,6 +3,8 @@ package com.ruoyi.system.service.bid.impl; import com.ruoyi.system.domain.bid.BizClient; import com.ruoyi.system.mapper.bid.BizClientMapper; import com.ruoyi.system.service.bid.IBizClientService; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @@ -47,4 +49,64 @@ public class BizClientServiceImpl implements IBizClientService { public List> selectClientDeliveryOrders(Long clientId) { return mapper.selectClientDeliveryOrders(clientId); } + + // ═══════════════════════════════════════════════ + // Excel批量导入 + // ═══════════════════════════════════════════════ + @Override + public String importClient(List clientList, Boolean updateSupport, String operName, Long tenantId) { + if (StringUtils.isNull(clientList) || clientList.isEmpty()) { + throw new ServiceException("导入数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + for (BizClient row : clientList) { + try { + if (StringUtils.isEmpty(row.getClientName())) { + throw new ServiceException("客户名称不能为空"); + } + // 按客户编号判重(编号为空则跳过判重直接新增) + BizClient existing = null; + if (StringUtils.isNotEmpty(row.getClientNo())) { + existing = mapper.selectBizClientByNo(tenantId, row.getClientNo()); + } + if (existing == null) { + row.setTenantId(tenantId); + row.setCreateBy(operName); + if (StringUtils.isEmpty(row.getStatus())) { + row.setStatus("0"); + } + if (StringUtils.isEmpty(row.getGrade())) { + row.setGrade("B"); + } + mapper.insertBizClient(row); + successNum++; + successMsg.append("
" + successNum + "、客户 " + row.getClientName() + " 导入成功"); + } else if (updateSupport) { + row.setClientId(existing.getClientId()); + row.setTenantId(tenantId); + row.setUpdateBy(operName); + mapper.updateBizClient(row); + successNum++; + successMsg.append("
" + successNum + "、客户 " + row.getClientName() + " 更新成功"); + } else { + failureNum++; + failureMsg.append("
" + failureNum + "、客户编号 " + row.getClientNo() + " 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、客户 " + (row.getClientName() == null ? "空" : row.getClientName()) + " 导入失败:" + e.getMessage(); + failureMsg.append(msg); + } + } + if (failureNum > 0) { + failureMsg.insert(0, "导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizDeliveryOrderServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizDeliveryOrderServiceImpl.java index fb4d5a4d..1879392d 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizDeliveryOrderServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizDeliveryOrderServiceImpl.java @@ -14,6 +14,8 @@ import java.util.Date; import java.util.List; import java.util.Map; +import com.ruoyi.common.exception.ServiceException; + @Service public class BizDeliveryOrderServiceImpl implements IBizDeliveryOrderService { @Autowired @@ -126,11 +128,36 @@ public class BizDeliveryOrderServiceImpl implements IBizDeliveryOrderService { @Override @Transactional public int setCloseDate(Long id, String closeDate, String username) { - if (closeDate == null || closeDate.isEmpty()) { - return mapper.updateDeliveryStatus(id, null, null, null, ""); - } else { - return mapper.updateDeliveryStatus(id, null, null, java.sql.Date.valueOf(closeDate), username); + BizDeliveryOrder d = mapper.selectBizDeliveryOrderById(id); + if (d == null) throw new RuntimeException("发货单不存在"); + String status = d.getDeliveryStatus(); + // 仅允许已签收(history)的订单设置结单日期 + if (!"history".equals(status)) { + throw new RuntimeException("当前状态(" + status + ")不允许设置结单日期,请先完成收货签收"); } + if (closeDate == null || closeDate.isEmpty()) { + throw new RuntimeException("结单日期不能为空"); + } + // 设置结单日期,同时将状态从 history(已签收) 置为 closed(已结单) + java.sql.Date parsedDate; + try { + parsedDate = java.sql.Date.valueOf(closeDate); + } catch (IllegalArgumentException e) { + throw new RuntimeException("日期格式错误,请使用 yyyy-MM-dd 格式"); + } + return mapper.updateDeliveryStatus(id, "closed", null, parsedDate, username); + } + + @Override + @Transactional + public int batchSetCloseDate(List ids, String closeDate, String username) { + if (ids == null || ids.isEmpty()) throw new ServiceException("请选择要结单的订单"); + if (closeDate == null || closeDate.isEmpty()) throw new ServiceException("请设置结单日期"); + int total = 0; + for (Long id : ids) { + total += setCloseDate(id, closeDate, username); + } + return total; } // ═══════════════════════════════════════════════ @@ -143,17 +170,22 @@ public class BizDeliveryOrderServiceImpl implements IBizDeliveryOrderService { } @Override - public Map selectTransitStats() { - return mapper.selectTransitStats(); + public Map selectTransitStats(Long tenantId) { + return mapper.selectTransitStats(tenantId); } @Override - public Map selectHistoryStats() { - return mapper.selectHistoryStats(); + public Map selectHistoryStats(Long tenantId, String type) { + return mapper.selectHistoryStats(tenantId, type); } @Override - public Map selectCloseDateStats() { - return mapper.selectCloseDateStats(); + public Map selectCloseDateStats(Long tenantId) { + return mapper.selectCloseDateStats(tenantId); + } + + @Override + public List> selectTimelineData(Long tenantId, String type, String status, String dateFrom, String dateTo) { + return mapper.selectTimelineData(tenantId, type, status, dateFrom, dateTo); } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizMaterialServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizMaterialServiceImpl.java index fc54f9cc..422a436b 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizMaterialServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizMaterialServiceImpl.java @@ -3,6 +3,8 @@ package com.ruoyi.system.service.bid.impl; import com.ruoyi.system.domain.bid.BizMaterial; import com.ruoyi.system.mapper.bid.BizMaterialMapper; import com.ruoyi.system.service.bid.IBizMaterialService; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; @@ -113,4 +115,62 @@ public class BizMaterialServiceImpl implements IBizMaterialService { public List selectMaterialsByExactName(String materialName, Long excludeId) { return mapper.selectMaterialsByExactName(materialName, excludeId); } + + // ═══════════════════════════════════════════════ + // Excel批量导入 + // ═══════════════════════════════════════════════ + @Override + public String importMaterial(List materialList, Boolean updateSupport, String operName, Long tenantId) { + if (StringUtils.isNull(materialList) || materialList.isEmpty()) { + throw new ServiceException("导入数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + for (BizMaterial row : materialList) { + try { + // 校验必填字段 + if (StringUtils.isEmpty(row.getMaterialCode())) { + throw new ServiceException("物料编码不能为空"); + } + if (StringUtils.isEmpty(row.getMaterialName())) { + throw new ServiceException("物料名称不能为空"); + } + // 查询是否已存在 + BizMaterial existing = mapper.selectBizMaterialByCode(tenantId, row.getMaterialCode()); + if (existing == null) { + row.setTenantId(tenantId); + row.setCreateBy(operName); + if (StringUtils.isEmpty(row.getStatus())) { + row.setStatus("0"); + } + mapper.insertBizMaterial(row); + successNum++; + successMsg.append("
" + successNum + "、物料编码 " + row.getMaterialCode() + " 导入成功"); + } else if (updateSupport) { + row.setMaterialId(existing.getMaterialId()); + row.setTenantId(tenantId); + row.setUpdateBy(operName); + mapper.updateBizMaterial(row); + successNum++; + successMsg.append("
" + successNum + "、物料编码 " + row.getMaterialCode() + " 更新成功"); + } else { + failureNum++; + failureMsg.append("
" + failureNum + "、物料编码 " + row.getMaterialCode() + " 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、物料编码 " + (row.getMaterialCode() == null ? "空" : row.getMaterialCode()) + " 导入失败:" + e.getMessage(); + failureMsg.append(msg); + } + } + if (failureNum > 0) { + failureMsg.insert(0, "导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizSupplierServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizSupplierServiceImpl.java index c3df0e8a..946c4e02 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizSupplierServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/bid/impl/BizSupplierServiceImpl.java @@ -3,6 +3,8 @@ package com.ruoyi.system.service.bid.impl; import com.ruoyi.system.domain.bid.BizSupplier; import com.ruoyi.system.mapper.bid.BizSupplierMapper; import com.ruoyi.system.service.bid.IBizSupplierService; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @@ -46,4 +48,58 @@ public class BizSupplierServiceImpl implements IBizSupplierService { public int deleteBizSupplierByIds(Long[] ids) { return mapper.deleteBizSupplierByIds(ids); } + + // ═══════════════════════════════════════════════ + // Excel批量导入 + // ═══════════════════════════════════════════════ + @Override + public String importSupplier(List supplierList, Boolean updateSupport, String operName, Long tenantId) { + if (StringUtils.isNull(supplierList) || supplierList.isEmpty()) { + throw new ServiceException("导入数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + for (BizSupplier row : supplierList) { + try { + if (StringUtils.isEmpty(row.getSupplierName())) { + throw new ServiceException("供应商名称不能为空"); + } + // 按供应商名称判重 + BizSupplier existing = mapper.selectBizSupplierByName(tenantId, row.getSupplierName()); + if (existing == null) { + row.setTenantId(tenantId); + row.setCreateBy(operName); + if (StringUtils.isEmpty(row.getStatus())) { + row.setStatus("0"); + } + mapper.insertBizSupplier(row); + successNum++; + successMsg.append("
" + successNum + "、供应商 " + row.getSupplierName() + " 导入成功"); + } else if (updateSupport) { + row.setSupplierId(existing.getSupplierId()); + row.setTenantId(tenantId); + row.setUpdateBy(operName); + mapper.updateBizSupplier(row); + successNum++; + successMsg.append("
" + successNum + "、供应商 " + row.getSupplierName() + " 更新成功"); + } else { + failureNum++; + failureMsg.append("
" + failureNum + "、供应商 " + row.getSupplierName() + " 已存在"); + } + } catch (Exception e) { + failureNum++; + String msg = "
" + failureNum + "、供应商 " + (row.getSupplierName() == null ? "空" : row.getSupplierName()) + " 导入失败:" + e.getMessage(); + failureMsg.append(msg); + } + } + if (failureNum > 0) { + failureMsg.insert(0, "导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } } diff --git a/ruoyi-system/src/main/resources/mapper/bid/BizClientMapper.xml b/ruoyi-system/src/main/resources/mapper/bid/BizClientMapper.xml index f86ac8d9..41bce2da 100644 --- a/ruoyi-system/src/main/resources/mapper/bid/BizClientMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/bid/BizClientMapper.xml @@ -40,6 +40,11 @@ SELECT * FROM biz_client WHERE client_id=#{id} + + + INSERT INTO biz_client(tenant_id,client_no,client_name,contact,phone,email,city,address,grade,source,status,create_by,create_time) VALUES(#{tenantId},#{clientNo},#{clientName},#{contact},#{phone},#{email},#{city},#{address},#{grade},#{source},#{status},#{createBy},NOW()) diff --git a/ruoyi-system/src/main/resources/mapper/bid/BizDeliveryOrderMapper.xml b/ruoyi-system/src/main/resources/mapper/bid/BizDeliveryOrderMapper.xml index ec52700d..16853147 100644 --- a/ruoyi-system/src/main/resources/mapper/bid/BizDeliveryOrderMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/bid/BizDeliveryOrderMapper.xml @@ -37,13 +37,13 @@ LEFT JOIN biz_client_quote cq ON d.client_quote_id = cq.quote_id LEFT JOIN biz_client cl ON cq.client_id = cl.client_id - AND d.tenant_id=#{tenantId} - AND d.type=#{type} - AND d.do_no LIKE CONCAT('%',#{doNo},'%') - AND d.supplier_id=#{supplierId} - AND d.delivery_status=#{deliveryStatus} - AND s.supplier_name LIKE CONCAT('%',#{supplierName},'%') - AND cl.client_name LIKE CONCAT('%',#{clientName},'%') + AND d.tenant_id=#{query.tenantId} + AND d.type=#{query.type} + AND d.do_no LIKE CONCAT('%',#{query.doNo},'%') + AND d.supplier_id=#{query.supplierId} + AND d.delivery_status=#{query.deliveryStatus} + AND s.supplier_name LIKE CONCAT('%',#{query.supplierName},'%') + AND cl.client_name LIKE CONCAT('%',#{query.clientName},'%') ORDER BY d.create_time DESC @@ -55,26 +55,26 @@ WHERE d.do_id=#{id} - + INSERT INTO biz_delivery_order(tenant_id,do_no,type,rfq_id,quotation_id,client_quote_id,supplier_id,total_amount,currency,delivery_date,delay_date,actual_close_date,close_date_set_by,delivery_status,remark,create_by,create_time) - VALUES(#{tenantId},#{doNo},#{type},#{rfqId},#{quotationId},#{clientQuoteId},#{supplierId},#{totalAmount},#{currency},#{deliveryDate},#{delayDate},#{actualCloseDate},#{closeDateSetBy},#{deliveryStatus},#{remark},#{createBy},NOW()) + VALUES(#{record.tenantId},#{record.doNo},#{record.type},#{record.rfqId},#{record.quotationId},#{record.clientQuoteId},#{record.supplierId},#{record.totalAmount},#{record.currency},#{record.deliveryDate},#{record.delayDate},#{record.actualCloseDate},#{record.closeDateSetBy},#{record.deliveryStatus},#{record.remark},#{record.createBy},NOW()) UPDATE biz_delivery_order - do_no=#{doNo}, - supplier_id=#{supplierId}, - total_amount=#{totalAmount}, - delivery_date=#{deliveryDate}, - delay_date=#{delayDate}, - actual_close_date=#{actualCloseDate}, - close_date_set_by=#{closeDateSetBy}, - delivery_status=#{deliveryStatus}, - remark=#{remark}, - update_by=#{updateBy}, update_time=NOW() + do_no=#{record.doNo}, + supplier_id=#{record.supplierId}, + total_amount=#{record.totalAmount}, + delivery_date=#{record.deliveryDate}, + delay_date=#{record.delayDate}, + actual_close_date=#{record.actualCloseDate}, + close_date_set_by=#{record.closeDateSetBy}, + delivery_status=#{record.deliveryStatus}, + remark=#{record.remark}, + update_by=#{record.updateBy}, update_time=NOW() - WHERE do_id=#{doId} + WHERE do_id=#{record.doId} DELETE FROM biz_delivery_order WHERE do_id=#{id} @@ -148,4 +154,34 @@ WHERE di.material_id = #{materialId} ORDER BY d.create_time DESC + + + diff --git a/ruoyi-system/src/main/resources/mapper/bid/BizMaterialMapper.xml b/ruoyi-system/src/main/resources/mapper/bid/BizMaterialMapper.xml index 78da41a5..078fb0d8 100644 --- a/ruoyi-system/src/main/resources/mapper/bid/BizMaterialMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/bid/BizMaterialMapper.xml @@ -6,6 +6,7 @@ + @@ -33,10 +34,10 @@ m.category_id = #{categoryId} OR m.category_id IN ( SELECT category_id FROM biz_material_category - WHERE ancestors LIKE CONCAT( - (SELECT ancestors FROM biz_material_category WHERE category_id = #{categoryId}), - ',%' - ) + WHERE ancestors LIKE CONCAT('%,', #{categoryId}, ',%') + OR ancestors LIKE CONCAT(#{categoryId}, ',%') + OR ancestors LIKE CONCAT('%,', #{categoryId}) + OR ancestors = #{categoryId} ) ) AND m.material_code LIKE CONCAT('%',#{materialCode},'%') @@ -55,6 +56,11 @@ WHERE m.material_id=#{id} + + + INSERT INTO biz_material(tenant_id,category_id,material_code,material_name,spec,unit,brand, description,status,performance_params,material,purpose,image_url,create_by,create_time) diff --git a/ruoyi-system/src/main/resources/mapper/bid/BizSupplierMapper.xml b/ruoyi-system/src/main/resources/mapper/bid/BizSupplierMapper.xml index bedb3e73..7c405602 100644 --- a/ruoyi-system/src/main/resources/mapper/bid/BizSupplierMapper.xml +++ b/ruoyi-system/src/main/resources/mapper/bid/BizSupplierMapper.xml @@ -30,6 +30,11 @@ + + + diff --git a/ruoyi-ui/src/api/bid/delivery.js b/ruoyi-ui/src/api/bid/delivery.js index 04f337ec..38ed0e21 100644 --- a/ruoyi-ui/src/api/bid/delivery.js +++ b/ruoyi-ui/src/api/bid/delivery.js @@ -9,3 +9,4 @@ export const shipDelivery = (id) => request({ url: baseUrl + '/' + id + '/ship', export const completeDelivery = (id) => request({ url: baseUrl + '/' + id + '/complete', method: 'put' }) export const recallDelivery = (id) => request({ url: baseUrl + '/' + id + '/recall', method: 'put' }) export const setCloseDate = (id, closeDate) => request({ url: baseUrl + '/' + id + '/closeDate', method: 'put', params: { closeDate } }) +export const batchSetCloseDate = (ids, closeDate) => request({ url: baseUrl + '/batchCloseDate', method: 'put', data: { ids, closeDate } }) diff --git a/ruoyi-ui/src/api/bid/timeline.js b/ruoyi-ui/src/api/bid/timeline.js new file mode 100644 index 00000000..b56c8215 --- /dev/null +++ b/ruoyi-ui/src/api/bid/timeline.js @@ -0,0 +1,5 @@ +import request from '@/utils/request' +const baseUrl = '/bid/delivery' + +/** 履约时间线数据 */ +export const getTimeline = (params) => request({ url: baseUrl + '/timeline', method: 'get', params }) diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js index df1a4134..fde63a35 100644 --- a/ruoyi-ui/src/router/index.js +++ b/ruoyi-ui/src/router/index.js @@ -232,7 +232,7 @@ export const dynamicRoutes = [ name: 'OrderObjection', permissions: ['bid:objection:list'], meta: { title: '订单异议', activeMenu: '/bid/order' } - } + }, ] }, @@ -268,6 +268,18 @@ export const dynamicRoutes = [ permissions: ['bid:clientdelivery:signed'], children: [{ path: '', component: () => import('@/views/bid/clientDelivery/signed'), name: 'ClientDeliverySigned', meta: { title: '甲方签收', activeMenu: '/bid/clientDelivery' } }] }, + { + path: '/bid/clientDelivery/closeDate', + component: Layout, + permissions: ['bid:clientdelivery:closeDate'], + children: [{ path: '', component: () => import('@/views/bid/clientDelivery/closeDate'), name: 'ClientCloseDate', meta: { title: '甲方结单管理', activeMenu: '/bid/clientDelivery' } }] + }, + { + path: '/bid/clientDelivery/timeline', + component: Layout, + permissions: ['bid:clientdelivery:timeline'], + children: [{ path: '', component: () => import('@/views/bid/clientDelivery/timeline'), name: 'ClientDeliveryTimeline', meta: { title: '甲方履约时效', activeMenu: '/bid/clientDelivery' } }] + }, { path: '/bid/comparison/detail', @@ -281,6 +293,20 @@ export const dynamicRoutes = [ meta: { title: '智慧比价分析', activeMenu: '/bid/comparison' } }] }, + + // ── 履约时效总览 ── + { + path: '/bid/timeline', + component: Layout, + permissions: ['bid:order:timeline'], + children: [{ + path: '', + component: () => import('@/views/bid/order/timeline'), + name: 'OrderTimeline', + meta: { title: '履约时效总览', activeMenu: '/bid/timeline' } + }] + }, + { path: '/system/user-auth', component: Layout, diff --git a/ruoyi-ui/src/views/bid/client/index.vue b/ruoyi-ui/src/views/bid/client/index.vue index e88ea789..d8c5d0a6 100644 --- a/ruoyi-ui/src/views/bid/client/index.vue +++ b/ruoyi-ui/src/views/bid/client/index.vue @@ -16,6 +16,8 @@ 搜索
新增客户 + 导入 + 导出
@@ -197,15 +199,27 @@ 关闭 + + + + + diff --git a/ruoyi-ui/src/views/bid/clientDelivery/timeline.vue b/ruoyi-ui/src/views/bid/clientDelivery/timeline.vue new file mode 100644 index 00000000..3df10f76 --- /dev/null +++ b/ruoyi-ui/src/views/bid/clientDelivery/timeline.vue @@ -0,0 +1,369 @@ + + + + + diff --git a/ruoyi-ui/src/views/bid/material/index.vue b/ruoyi-ui/src/views/bid/material/index.vue index e35bd6d8..c5874770 100644 --- a/ruoyi-ui/src/views/bid/material/index.vue +++ b/ruoyi-ui/src/views/bid/material/index.vue @@ -60,6 +60,12 @@ 删除 + + 导入 + + + 导出 + 当前分类: {{ currentCategoryName }} @@ -303,6 +309,16 @@ 确定 + + + @@ -310,9 +326,11 @@ import { listMaterial, getMaterial, addMaterial, updateMaterial, delMaterial, listManufacturer } from "@/api/bid/material"; import { getCategoryList, addCategory, updateCategory, delCategory } from "@/api/bid/category"; import request from '@/utils/request' +import ExcelImportDialog from "@/components/ExcelImportDialog" export default { name: "Material", + components: { ExcelImportDialog }, data() { return { loading: false, multiple: true, total: 0, materialList: [], @@ -409,6 +427,10 @@ export default { handleAdd() { this.reset(); this.perfParams = []; + // 自动带入当前选中的分类 + if (this.queryParams.categoryId) { + this.form.categoryId = this.queryParams.categoryId; + } this.open = true; this.title = "新增物料"; }, @@ -428,6 +450,13 @@ export default { const ids = row.materialId || (this.ids || []).join(","); this.$modal.confirm("确认删除?").then(() => delMaterial(ids)).then(() => { this.getList(); this.$modal.msgSuccess("删除成功"); }); }, + // ═══ Excel 导入导出 ═══ + handleImport() { + this.$refs.importRef.open(); + }, + handleExport() { + this.download('/bid/material/export', { ...this.queryParams }, `material_${new Date().getTime()}.xlsx`); + }, handleStatusChange(row) { updateMaterial(row); }, // 性能参数 addPerfRow() { diff --git a/ruoyi-ui/src/views/bid/order/closeDate.vue b/ruoyi-ui/src/views/bid/order/closeDate.vue index cc0464d2..00556f28 100644 --- a/ruoyi-ui/src/views/bid/order/closeDate.vue +++ b/ruoyi-ui/src/views/bid/order/closeDate.vue @@ -15,9 +15,8 @@
- - - + + 搜索 重置 @@ -31,7 +30,7 @@
- 订单列表 + 已签收待结单
+ + diff --git a/ruoyi-ui/src/views/bid/supplier/index.vue b/ruoyi-ui/src/views/bid/supplier/index.vue index bfd91e08..772d519a 100644 --- a/ruoyi-ui/src/views/bid/supplier/index.vue +++ b/ruoyi-ui/src/views/bid/supplier/index.vue @@ -16,6 +16,8 @@ /> + +
@@ -297,6 +299,16 @@ 确定 + + + @@ -304,9 +316,11 @@ import { listSupplier, getSupplier, addSupplier, updateSupplier, delSupplier } from "@/api/bid/supplier"; import { listObjection } from "@/api/bid/objection"; import { getSupplierQuoteItems } from "@/api/bid/quotation"; +import ExcelImportDialog from "@/components/ExcelImportDialog" export default { name: "SupplierManage", + components: { ExcelImportDialog }, data() { return { // ---- 左侧列表 ---- @@ -389,6 +403,10 @@ export default { this.getList(); }, + // ═══ Excel 导入导出 ═══ + handleImport() { this.$refs.importRef.open(); }, + handleExport() { this.download('/bid/supplier/export', { ...this.queryParams }, `supplier_${new Date().getTime()}.xlsx`); }, + handleSizeChange(size) { this.queryParams.pageSize = size; this.queryParams.pageNum = 1; diff --git a/sql/fix_menu_client_close_date.sql b/sql/fix_menu_client_close_date.sql new file mode 100644 index 00000000..4c302d1d --- /dev/null +++ b/sql/fix_menu_client_close_date.sql @@ -0,0 +1,17 @@ +-- ═══════════════════════════════════════════════════════════ +-- 甲方履约 - 结单时间管理 菜单与权限 +-- ═══════════════════════════════════════════════════════════ + +SET NAMES utf8mb4; + +-- 1. 创建菜单 +INSERT IGNORE INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +VALUES(2044, '甲方结单管理', 2040, 4, 'closeDate', 'bid/clientDelivery/closeDate', 1, 0, 'C', '0', '0', 'bid:clientdelivery:closeDate', 'date', 'admin', NOW()); + +-- 2. 给 admin 角色授权 +INSERT IGNORE INTO sys_role_menu(role_id, menu_id) VALUES(1, 2044); + +-- 3. 按钮权限 +INSERT IGNORE INTO sys_menu(menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +SELECT '结单确认', menu_id, 1, '#', NULL, 1, 0, 'F', '0', '0', 'bid:clientdelivery:closeDate:edit', '#', 'admin', NOW() +FROM sys_menu WHERE perms = 'bid:clientdelivery:closeDate' AND menu_id = 2044; diff --git a/sql/fix_menu_import.sql b/sql/fix_menu_import.sql new file mode 100644 index 00000000..92ed683f --- /dev/null +++ b/sql/fix_menu_import.sql @@ -0,0 +1,42 @@ +-- ═══════════════════════════════════════════════════════════ +-- Excel 批量导入功能 - 权限菜单初始化 +-- 物料/客户/供应商 三模块的导入导出按钮权限 +-- ═══════════════════════════════════════════════════════════ + +SET NAMES utf8mb4; + +-- 清理可能存在的旧数据 +DELETE FROM sys_role_menu WHERE menu_id IN (2140, 2141, 2142, 2143, 2144, 2145); +DELETE FROM sys_menu WHERE menu_id IN (2140, 2141, 2142, 2143, 2144, 2145); + +-- 1. 物料导入按钮 (parent=2001 物料管理) +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +VALUES(2140, UNHEX('E789A9E69699E5AFBCE585A5'), 2001, 7, '', NULL, 1, 0, 'F', '0', '0', 'bid:material:import', '#', 'admin', NOW()); + +-- 2. 物料导出按钮 +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +VALUES(2141, UNHEX('E789A9E69699E5AFBCE587BA'), 2001, 8, '', NULL, 1, 0, 'F', '0', '0', 'bid:material:export', '#', 'admin', NOW()); + +-- 3. 客户导入按钮 (parent=2028 甲方客户管理) +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +VALUES(2142, UNHEX('E5AEA2E688B7E5AFBCE585A5'), 2028, 10, '', NULL, 1, 0, 'F', '0', '0', 'bid:client:import', '#', 'admin', NOW()); + +-- 4. 客户导出按钮 +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +VALUES(2143, UNHEX('E5AEA2E688B7E5AFBCE587BA'), 2028, 11, '', NULL, 1, 0, 'F', '0', '0', 'bid:client:export', '#', 'admin', NOW()); + +-- 5. 供应商导入按钮 (parent=2002 供应商管理) +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +VALUES(2144, UNHEX('E4BE9BE5BA94E59586E5AFBCE585A5'), 2002, 7, '', NULL, 1, 0, 'F', '0', '0', 'bid:supplier:import', '#', 'admin', NOW()); + +-- 6. 供应商导出按钮 +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +VALUES(2145, UNHEX('E4BE9BE5BA94E59586E5AFBCE587BA'), 2002, 8, '', NULL, 1, 0, 'F', '0', '0', 'bid:supplier:export', '#', 'admin', NOW()); + +-- 7. 给 admin 角色授权 +INSERT INTO sys_role_menu(role_id, menu_id) VALUES(1, 2140); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES(1, 2141); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES(1, 2142); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES(1, 2143); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES(1, 2144); +INSERT INTO sys_role_menu(role_id, menu_id) VALUES(1, 2145); diff --git a/sql/fix_menu_timeline.sql b/sql/fix_menu_timeline.sql new file mode 100644 index 00000000..e6fb26ac --- /dev/null +++ b/sql/fix_menu_timeline.sql @@ -0,0 +1,17 @@ +-- ═══════════════════════════════════════════════════════════ +-- 履约时效总览 菜单(同时覆盖供应商履约和甲方履约的甘特图) +-- 挂在订单履约(2120)下,与供应商履约、甲方履约同级 +-- ═══════════════════════════════════════════════════════════ + +SET NAMES utf8mb4; + +-- 1. 删除旧的子菜单(2122挂在2121下,2123挂在2040下) +DELETE FROM sys_role_menu WHERE menu_id IN (2122, 2123); +DELETE FROM sys_menu WHERE menu_id IN (2122, 2123); + +-- 2. 新增统一菜单,挂在订单履约(2120)下,排序3 +INSERT IGNORE INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time) +VALUES(2124, '履约时效总览', 2120, 3, 'timeline', 'bid/order/timeline', 1, 0, 'C', '0', '0', 'bid:order:timeline', 'chart', 'admin', NOW()); + +-- 3. 给 admin 角色授权 +INSERT IGNORE INTO sys_role_menu(role_id, menu_id) VALUES(1, 2124);