销售发货,产品调整
This commit is contained in:
2
.dbg/product-import-image.env
Normal file
2
.dbg/product-import-image.env
Normal file
@@ -0,0 +1,2 @@
|
||||
DEBUG_SERVER_URL=http://127.0.0.1:7777/event
|
||||
DEBUG_SESSION_ID=product-import-image
|
||||
36
.dbg/trae-debug-log-default.ndjson
Normal file
36
.dbg/trae-debug-log-default.ndjson
Normal file
@@ -0,0 +1,36 @@
|
||||
{"ts": 1779294580277, "event": "import_open_workbook", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "fileSize": 204246016, "workbookClass": "org.apache.poi.hssf.usermodel.HSSFWorkbook"}}
|
||||
{"ts": 1779294580300, "event": "import_header", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "sheetClass": "org.apache.poi.hssf.usermodel.HSSFSheet", "lastRowNum": 333, "headerIndex": {"序号": 0, "产品名称": 1, "产品图片": 2, "产品规格": 3, "价格": 4, "产品型号": 5}, "colProductName": 1, "colSpec": 3, "colModel": 5, "colUnitPrice": -1, "colRemark": -1, "colImages": 2, "xssfCellImagesPart": null}}
|
||||
{"ts": 1779294580378, "event": "import_pictures_map", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "rowsWithPictures": 56, "totalPictures": 65}}
|
||||
{"ts": 1779294580412, "event": "import_image_miss_row", "payload": {"rowIndex": 1, "productName": "床头柜", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_DE28F1DE70D7472299A0EB95E12ABDE8\",1)"}}
|
||||
{"ts": 1779294580415, "event": "import_image_miss_row", "payload": {"rowIndex": 2, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_D1DA8BA74D034AC0892AF25D577A3DFC\",1)"}}
|
||||
{"ts": 1779294580417, "event": "import_image_miss_row", "payload": {"rowIndex": 3, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_C5A9E5DB6F484CD4B0ED9E78B40B850B\",1)"}}
|
||||
{"ts": 1779294580420, "event": "import_image_miss_row", "payload": {"rowIndex": 4, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_E9A371D0A3D74FE8AFF9840431B05F3E\",1)"}}
|
||||
{"ts": 1779294580423, "event": "import_image_miss_row", "payload": {"rowIndex": 5, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_690D368FCF9F4BFAB19C8654A6C61FFD\",1)"}}
|
||||
{"ts": 1779294642453, "event": "import_done_parse", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "resultSize": 236, "rowsWithProductImages": 55}}
|
||||
{"ts": 1779294704871, "event": "import_open_workbook", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "fileSize": 204246016, "workbookClass": "org.apache.poi.hssf.usermodel.HSSFWorkbook"}}
|
||||
{"ts": 1779294704873, "event": "import_header", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "sheetClass": "org.apache.poi.hssf.usermodel.HSSFSheet", "lastRowNum": 333, "headerIndex": {"序号": 0, "产品名称": 1, "产品图片": 2, "产品规格": 3, "价格": 4, "产品型号": 5}, "colProductName": 1, "colSpec": 3, "colModel": 5, "colUnitPrice": -1, "colRemark": -1, "colImages": 2, "xssfCellImagesPart": null}}
|
||||
{"ts": 1779294704910, "event": "import_pictures_map", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "rowsWithPictures": 56, "totalPictures": 65}}
|
||||
{"ts": 1779294704913, "event": "import_image_miss_row", "payload": {"rowIndex": 1, "productName": "床头柜", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_DE28F1DE70D7472299A0EB95E12ABDE8\",1)"}}
|
||||
{"ts": 1779294704916, "event": "import_image_miss_row", "payload": {"rowIndex": 2, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_D1DA8BA74D034AC0892AF25D577A3DFC\",1)"}}
|
||||
{"ts": 1779294704918, "event": "import_image_miss_row", "payload": {"rowIndex": 3, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_C5A9E5DB6F484CD4B0ED9E78B40B850B\",1)"}}
|
||||
{"ts": 1779294704921, "event": "import_image_miss_row", "payload": {"rowIndex": 4, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_E9A371D0A3D74FE8AFF9840431B05F3E\",1)"}}
|
||||
{"ts": 1779294704925, "event": "import_image_miss_row", "payload": {"rowIndex": 5, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_690D368FCF9F4BFAB19C8654A6C61FFD\",1)"}}
|
||||
{"ts": 1779294767240, "event": "import_done_parse", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "resultSize": 236, "rowsWithProductImages": 55}}
|
||||
{"ts": 1779294901849, "event": "import_open_workbook", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "fileSize": 204246016, "workbookClass": "org.apache.poi.hssf.usermodel.HSSFWorkbook"}}
|
||||
{"ts": 1779294901852, "event": "import_header", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "sheetClass": "org.apache.poi.hssf.usermodel.HSSFSheet", "lastRowNum": 333, "headerIndex": {"序号": 0, "产品名称": 1, "产品图片": 2, "产品规格": 3, "价格": 4, "产品型号": 5}, "colProductName": 1, "colSpec": 3, "colModel": 5, "colUnitPrice": -1, "colRemark": -1, "colImages": 2, "xssfCellImagesPart": null}}
|
||||
{"ts": 1779294901883, "event": "import_pictures_map", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "rowsWithPictures": 56, "totalPictures": 65}}
|
||||
{"ts": 1779294901886, "event": "import_image_miss_row", "payload": {"rowIndex": 1, "productName": "床头柜", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_DE28F1DE70D7472299A0EB95E12ABDE8\",1)"}}
|
||||
{"ts": 1779294901889, "event": "import_image_miss_row", "payload": {"rowIndex": 2, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_D1DA8BA74D034AC0892AF25D577A3DFC\",1)"}}
|
||||
{"ts": 1779294901893, "event": "import_image_miss_row", "payload": {"rowIndex": 3, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_C5A9E5DB6F484CD4B0ED9E78B40B850B\",1)"}}
|
||||
{"ts": 1779294901895, "event": "import_image_miss_row", "payload": {"rowIndex": 4, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_E9A371D0A3D74FE8AFF9840431B05F3E\",1)"}}
|
||||
{"ts": 1779294901897, "event": "import_image_miss_row", "payload": {"rowIndex": 5, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_690D368FCF9F4BFAB19C8654A6C61FFD\",1)"}}
|
||||
{"ts": 1779294980062, "event": "import_done_parse", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "resultSize": 236, "rowsWithProductImages": 55}}
|
||||
{"ts": 1779295230641, "event": "import_open_workbook", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "fileSize": 204246016, "workbookClass": "org.apache.poi.hssf.usermodel.HSSFWorkbook"}}
|
||||
{"ts": 1779295230643, "event": "import_header", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "sheetClass": "org.apache.poi.hssf.usermodel.HSSFSheet", "lastRowNum": 333, "headerIndex": {"序号": 0, "产品名称": 1, "产品图片": 2, "产品规格": 3, "价格": 4, "产品型号": 5}, "colProductName": 1, "colSpec": 3, "colModel": 5, "colUnitPrice": -1, "colRemark": -1, "colImages": 2, "xssfCellImagesPart": null}}
|
||||
{"ts": 1779295230672, "event": "import_pictures_map", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "rowsWithPictures": 56, "totalPictures": 65}}
|
||||
{"ts": 1779295230676, "event": "import_image_miss_row", "payload": {"rowIndex": 1, "productName": "床头柜", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_DE28F1DE70D7472299A0EB95E12ABDE8\",1)"}}
|
||||
{"ts": 1779295230679, "event": "import_image_miss_row", "payload": {"rowIndex": 2, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_D1DA8BA74D034AC0892AF25D577A3DFC\",1)"}}
|
||||
{"ts": 1779295230682, "event": "import_image_miss_row", "payload": {"rowIndex": 3, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_C5A9E5DB6F484CD4B0ED9E78B40B850B\",1)"}}
|
||||
{"ts": 1779295230684, "event": "import_image_miss_row", "payload": {"rowIndex": 4, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_E9A371D0A3D74FE8AFF9840431B05F3E\",1)"}}
|
||||
{"ts": 1779295230686, "event": "import_image_miss_row", "payload": {"rowIndex": 5, "productName": "置物架", "cellType": "FORMULA", "hasDispImgFormula": true, "cellText": "_xlfn.DISPIMG(\"ID_690D368FCF9F4BFAB19C8654A6C61FFD\",1)"}}
|
||||
{"ts": 1779295298724, "event": "import_done_parse", "payload": {"fileName": "询价-产品目录(整理)2604.xls", "resultSize": 236, "rowsWithProductImages": 55}}
|
||||
@@ -76,9 +76,9 @@ spring:
|
||||
servlet:
|
||||
multipart:
|
||||
# 单个文件大小
|
||||
max-file-size: 10MB
|
||||
max-file-size: 512MB
|
||||
# 设置总上传的文件大小
|
||||
max-request-size: 20MB
|
||||
max-request-size: 512MB
|
||||
# 服务模块
|
||||
devtools:
|
||||
restart:
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
<groupId>com.gear</groupId>
|
||||
<artifactId>gear-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.gear</groupId>
|
||||
<artifactId>gear-system</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-collections</groupId>
|
||||
<artifactId>commons-collections</artifactId>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -67,6 +67,11 @@ public class MatMaterialOutBo extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 厂家(配料厂家)
|
||||
*/
|
||||
private String factory;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
|
||||
@@ -27,4 +27,7 @@ public class MatProductImportBo {
|
||||
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("产品图片")
|
||||
private String productImages;
|
||||
}
|
||||
|
||||
@@ -72,6 +72,11 @@ public class MatPurchaseInDetailBo extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 厂家(配料厂家)
|
||||
*/
|
||||
private String factory;
|
||||
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
|
||||
@@ -71,6 +71,7 @@ public class MatMaterialOutServiceImpl implements IMatMaterialOutService {
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getOperator()), "mmo.operator", bo.getOperator());
|
||||
lqw.ge(bo.getBeginTime() != null, "mmo.out_time", bo.getBeginTime());
|
||||
lqw.le(bo.getEndTime() != null, "mmo.out_time", bo.getEndTime());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getFactory()), "mm.factory", bo.getFactory());
|
||||
// 逻辑删除
|
||||
lqw.eq("mmo.del_flag", 0);
|
||||
// 排序
|
||||
|
||||
@@ -255,6 +255,10 @@ public class MatProductServiceImpl implements IMatProductService {
|
||||
data.setModel(model);
|
||||
data.setUnitPrice(row.getUnitPrice());
|
||||
data.setRemark(row.getRemark());
|
||||
String images = StringUtils.trim(row.getProductImages());
|
||||
if (StringUtils.isNotBlank(images)) {
|
||||
data.setProductImages(images);
|
||||
}
|
||||
data.setProductType(normalizeProductType(data.getProductType()));
|
||||
data.setCurrentStock(normalizeCurrentStock(data.getCurrentStock()));
|
||||
if (exist == null) {
|
||||
|
||||
@@ -85,6 +85,7 @@ public class MatPurchaseInDetailServiceImpl implements IMatPurchaseInDetailServi
|
||||
qw.ge(bo.getBeginTime() != null, "mpid.in_time", bo.getBeginTime());
|
||||
qw.le(bo.getEndTime() != null, "mpid.in_time", bo.getEndTime());
|
||||
qw.eq(StringUtils.isNotBlank(bo.getOperator()), "mpid.operator", bo.getOperator());
|
||||
qw.eq(StringUtils.isNotBlank(bo.getFactory()), "mm.factory", bo.getFactory());
|
||||
// 逻辑删除
|
||||
qw.eq("mpid.del_flag", 0);
|
||||
// 排序
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.gear.oa.controller;
|
||||
|
||||
import com.gear.common.annotation.Log;
|
||||
import com.gear.common.annotation.RepeatSubmit;
|
||||
import com.gear.common.core.controller.BaseController;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.domain.R;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import com.gear.common.enums.BusinessType;
|
||||
import com.gear.oa.domain.bo.GearShippingOrderDetailBo;
|
||||
import com.gear.oa.domain.vo.GearShippingOrderDetailVo;
|
||||
import com.gear.oa.service.IGearShippingOrderDetailService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/oa/shippingOrderDetail")
|
||||
public class GearShippingOrderDetailController extends BaseController {
|
||||
|
||||
private final IGearShippingOrderDetailService iGearShippingOrderDetailService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<GearShippingOrderDetailVo> list(GearShippingOrderDetailBo bo, PageQuery pageQuery) {
|
||||
return iGearShippingOrderDetailService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
@GetMapping("/{detailId}")
|
||||
public R<GearShippingOrderDetailVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long detailId) {
|
||||
return R.ok(iGearShippingOrderDetailService.queryById(detailId));
|
||||
}
|
||||
|
||||
@Log(title = "发货单据明细", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody GearShippingOrderDetailBo bo) {
|
||||
return toAjax(iGearShippingOrderDetailService.insertByBo(bo));
|
||||
}
|
||||
|
||||
@Log(title = "发货单据明细", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody GearShippingOrderDetailBo bo) {
|
||||
return toAjax(iGearShippingOrderDetailService.updateByBo(bo));
|
||||
}
|
||||
|
||||
@Log(title = "发货单据明细", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{detailIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] detailIds) {
|
||||
return toAjax(iGearShippingOrderDetailService.deleteWithValidByIds(Arrays.asList(detailIds), true));
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import com.gear.common.enums.BusinessType;
|
||||
import com.gear.common.exception.ServiceException;
|
||||
import com.gear.common.utils.StringUtils;
|
||||
import com.gear.common.utils.poi.ExcelUtil;
|
||||
import com.gear.oa.domain.bo.GearStockIoOrderBo;
|
||||
import com.gear.oa.domain.bo.GearStockIoOrderWithDetailBo;
|
||||
@@ -54,23 +56,39 @@ public class GearStockIoOrderController extends BaseController {
|
||||
}
|
||||
|
||||
@GetMapping("/materialFlow")
|
||||
public R<IGearStockIoOrderService.MaterialFlowResp> materialFlow(@NotNull(message = "物料ID不能为空") @RequestParam Long itemId,
|
||||
public R<IGearStockIoOrderService.MaterialFlowResp> materialFlow(@RequestParam(required = false) Long itemId,
|
||||
@RequestParam(required = false) String factory,
|
||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime,
|
||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime) {
|
||||
return R.ok(stockIoOrderService.queryMaterialFlow(itemId, startTime, endTime));
|
||||
if (itemId != null) {
|
||||
return R.ok(stockIoOrderService.queryMaterialFlow(itemId, startTime, endTime));
|
||||
}
|
||||
if (StringUtils.isBlank(factory)) {
|
||||
return R.fail("请选择物料或填写厂家");
|
||||
}
|
||||
return R.ok(stockIoOrderService.queryMaterialFlowByFactory(factory, startTime, endTime));
|
||||
}
|
||||
|
||||
@Log(title = "物料出入库统计", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/materialFlow/export")
|
||||
public void exportMaterialFlow(@NotNull(message = "物料ID不能为空") @RequestParam Long itemId,
|
||||
public void exportMaterialFlow(@RequestParam(required = false) Long itemId,
|
||||
@RequestParam(required = false) String factory,
|
||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime,
|
||||
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime,
|
||||
HttpServletResponse response) {
|
||||
IGearStockIoOrderService.MaterialFlowResp resp = stockIoOrderService.queryMaterialFlow(itemId, startTime, endTime);
|
||||
IGearStockIoOrderService.MaterialFlowResp resp;
|
||||
if (itemId != null) {
|
||||
resp = stockIoOrderService.queryMaterialFlow(itemId, startTime, endTime);
|
||||
} else if (StringUtils.isNotBlank(factory)) {
|
||||
resp = stockIoOrderService.queryMaterialFlowByFactory(factory, startTime, endTime);
|
||||
} else {
|
||||
throw new ServiceException("请选择物料或填写厂家");
|
||||
}
|
||||
List<MaterialFlowExportRow> exportRows = new ArrayList<>();
|
||||
|
||||
MaterialFlowExportRow summary = new MaterialFlowExportRow();
|
||||
summary.setItemId(resp.getItemId());
|
||||
summary.setFactory(factory);
|
||||
summary.setStartTime(resp.getStartTime());
|
||||
summary.setEndTime(resp.getEndTime());
|
||||
summary.setAction("汇总");
|
||||
@@ -85,6 +103,7 @@ public class GearStockIoOrderController extends BaseController {
|
||||
for (IGearStockIoOrderService.MaterialFlowRow r : resp.getRows()) {
|
||||
MaterialFlowExportRow row = new MaterialFlowExportRow();
|
||||
row.setItemId(resp.getItemId());
|
||||
row.setFactory(factory);
|
||||
row.setTime(r.getTime());
|
||||
row.setAction(r.getAction());
|
||||
row.setOrderCode(r.getOrderCode());
|
||||
@@ -105,6 +124,8 @@ public class GearStockIoOrderController extends BaseController {
|
||||
public static class MaterialFlowExportRow {
|
||||
@ExcelProperty(value = "物料ID")
|
||||
private Long itemId;
|
||||
@ExcelProperty(value = "厂家")
|
||||
private String factory;
|
||||
@ExcelProperty(value = "开始时间")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@@ -144,6 +165,14 @@ public class GearStockIoOrderController extends BaseController {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
public String getFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
||||
public void setFactory(String factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public Date getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@@ -62,4 +62,8 @@ public class GearOrderDetail extends BaseEntity {
|
||||
*/
|
||||
private BigDecimal noTaxPrice;
|
||||
|
||||
private String spec;
|
||||
|
||||
private String model;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.gear.oa.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("gear_shipping_order_detail")
|
||||
public class GearShippingOrderDetail extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "detail_id")
|
||||
private Long detailId;
|
||||
|
||||
private Long shippingId;
|
||||
|
||||
private Long productId;
|
||||
|
||||
private String productCode;
|
||||
|
||||
private String productName;
|
||||
|
||||
private String spec;
|
||||
|
||||
private String model;
|
||||
|
||||
private BigDecimal quantity;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Integer sort;
|
||||
|
||||
@TableLogic(value = "0", delval = "2")
|
||||
private String delFlag;
|
||||
}
|
||||
@@ -63,5 +63,9 @@ public class GearOrderDetailBo extends BaseEntity {
|
||||
*/
|
||||
private BigDecimal noTaxPrice;
|
||||
|
||||
private String spec;
|
||||
|
||||
private String model;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ public class GearReceivableBo extends BaseEntity {
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
private Long salesmanId;
|
||||
|
||||
/**
|
||||
* 到期日
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.gear.oa.domain.bo;
|
||||
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import com.gear.common.core.validate.AddGroup;
|
||||
import com.gear.common.core.validate.EditGroup;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class GearShippingOrderDetailBo extends BaseEntity {
|
||||
|
||||
private Long detailId;
|
||||
|
||||
@NotNull(message = "发货单据ID不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private Long shippingId;
|
||||
|
||||
@NotNull(message = "产品ID不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private Long productId;
|
||||
|
||||
private String productCode;
|
||||
|
||||
private String productName;
|
||||
|
||||
private String spec;
|
||||
|
||||
private String model;
|
||||
|
||||
@NotNull(message = "数量不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private BigDecimal quantity;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Integer sort;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.gear.oa.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.gear.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ExcelIgnoreUnannotated
|
||||
public class GearShippingOrderDetailVo extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long detailId;
|
||||
|
||||
private Long shippingId;
|
||||
|
||||
private Long productId;
|
||||
|
||||
private String productCode;
|
||||
|
||||
private String productName;
|
||||
|
||||
private String spec;
|
||||
|
||||
private String model;
|
||||
|
||||
private BigDecimal quantity;
|
||||
|
||||
private String unit;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Integer sort;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.gear.oa.mapper;
|
||||
|
||||
import com.gear.common.core.mapper.BaseMapperPlus;
|
||||
import com.gear.oa.domain.GearShippingOrderDetail;
|
||||
import com.gear.oa.domain.vo.GearShippingOrderDetailVo;
|
||||
|
||||
public interface GearShippingOrderDetailMapper extends BaseMapperPlus<GearShippingOrderDetailMapper, GearShippingOrderDetail, GearShippingOrderDetailVo> {
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface MatMaterialSimpleMapper {
|
||||
@@ -13,7 +14,9 @@ public interface MatMaterialSimpleMapper {
|
||||
"from mat_material where material_id = #{materialId} and del_flag = 0 limit 1")
|
||||
Map<String, Object> selectSnapshot(@Param("materialId") Long materialId);
|
||||
|
||||
@Select("select material_id from mat_material where del_flag = 0 and factory like concat('%', #{factory}, '%')")
|
||||
List<Long> selectIdsByFactory(@Param("factory") String factory);
|
||||
|
||||
@Update("update mat_material set current_stock = ifnull(current_stock, 0) + #{delta} where material_id = #{materialId} and del_flag = 0")
|
||||
int updateStockDelta(@Param("materialId") Long materialId, @Param("delta") BigDecimal delta);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.gear.oa.service;
|
||||
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.oa.domain.bo.GearShippingOrderDetailBo;
|
||||
import com.gear.oa.domain.vo.GearShippingOrderDetailVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface IGearShippingOrderDetailService {
|
||||
|
||||
GearShippingOrderDetailVo queryById(Long detailId);
|
||||
|
||||
TableDataInfo<GearShippingOrderDetailVo> queryPageList(GearShippingOrderDetailBo bo, PageQuery pageQuery);
|
||||
|
||||
List<GearShippingOrderDetailVo> queryList(GearShippingOrderDetailBo bo);
|
||||
|
||||
Boolean insertByBo(GearShippingOrderDetailBo bo);
|
||||
|
||||
Boolean updateByBo(GearShippingOrderDetailBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
||||
@@ -35,6 +35,8 @@ public interface IGearStockIoOrderService {
|
||||
|
||||
MaterialFlowResp queryMaterialFlow(Long itemId, Date startTime, Date endTime);
|
||||
|
||||
MaterialFlowResp queryMaterialFlowByFactory(String factory, Date startTime, Date endTime);
|
||||
|
||||
@lombok.Data
|
||||
class MaterialFlowResp {
|
||||
private Long itemId;
|
||||
|
||||
@@ -13,9 +13,7 @@ import org.springframework.stereotype.Service;
|
||||
import com.gear.oa.domain.bo.GearOrderBo;
|
||||
import com.gear.oa.domain.vo.GearOrderVo;
|
||||
import com.gear.oa.domain.GearOrder;
|
||||
import com.gear.oa.domain.GearShippingOrder;
|
||||
import com.gear.oa.mapper.GearOrderMapper;
|
||||
import com.gear.oa.mapper.GearShippingOrderMapper;
|
||||
import com.gear.oa.service.IGearOrderService;
|
||||
|
||||
import java.util.List;
|
||||
@@ -33,51 +31,13 @@ import java.util.Collection;
|
||||
public class GearOrderServiceImpl implements IGearOrderService {
|
||||
|
||||
private final GearOrderMapper baseMapper;
|
||||
private final GearShippingOrderMapper shippingOrderMapper;
|
||||
|
||||
/**
|
||||
* 查询订单主
|
||||
*/
|
||||
@Override
|
||||
public GearOrderVo queryById(Long orderId){
|
||||
GearOrderVo vo = baseMapper.selectVoById(orderId);
|
||||
if (vo == null) {
|
||||
return null;
|
||||
}
|
||||
Integer derived = deriveOrderStatusByShipping(orderId, vo.getOrderStatus());
|
||||
if (derived != null && vo.getOrderStatus() != null && !derived.equals(vo.getOrderStatus())) {
|
||||
baseMapper.update(null, Wrappers.<GearOrder>lambdaUpdate()
|
||||
.eq(GearOrder::getOrderId, orderId)
|
||||
.ne(GearOrder::getOrderStatus, 3)
|
||||
.in(GearOrder::getOrderStatus, 0, 1)
|
||||
.set(GearOrder::getOrderStatus, derived));
|
||||
vo.setOrderStatus(derived);
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
private Integer deriveOrderStatusByShipping(Long orderId, Integer currentStatus) {
|
||||
if (orderId == null) return currentStatus;
|
||||
if (currentStatus != null && currentStatus == 3) return 3;
|
||||
|
||||
QueryWrapper<GearShippingOrder> qw = new QueryWrapper<>();
|
||||
qw.select("MAX(CAST(status AS SIGNED))");
|
||||
qw.eq("order_id", orderId);
|
||||
qw.eq("del_flag", "0");
|
||||
List<Object> objs = shippingOrderMapper.selectObjs(qw);
|
||||
Integer maxStatus = null;
|
||||
if (objs != null && !objs.isEmpty() && objs.get(0) != null) {
|
||||
try {
|
||||
maxStatus = Integer.parseInt(String.valueOf(objs.get(0)));
|
||||
} catch (Exception ignored) {
|
||||
maxStatus = null;
|
||||
}
|
||||
}
|
||||
|
||||
int st = currentStatus == null ? 0 : currentStatus;
|
||||
if (maxStatus != null && maxStatus >= 3 && (st == 0 || st == 1)) return 2;
|
||||
if (maxStatus != null && maxStatus >= 2 && st == 0) return 1;
|
||||
return currentStatus;
|
||||
return baseMapper.selectVoById(orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,8 +58,6 @@ public class GearOrderServiceImpl implements IGearOrderService {
|
||||
lqw.eq(bo.getSalesmanId() != null, "o.salesman_id", bo.getSalesmanId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getSalesManager()), "s.name", bo.getSalesManager());
|
||||
lqw.eq(bo.getOrderStatus() != null, "o.order_status", bo.getOrderStatus());
|
||||
lqw.apply(bo.getOrderStatus() != null && bo.getOrderStatus() == 0,
|
||||
"NOT EXISTS (SELECT 1 FROM gear_shipping_order so WHERE so.order_id = o.order_id AND so.del_flag = '0' AND CAST(so.status AS SIGNED) >= 2)");
|
||||
lqw.eq(bo.getTradeType() != null, "o.trade_type", bo.getTradeType());
|
||||
lqw.eq(bo.getTaxAmount() != null, "o.tax_amount", bo.getTaxAmount());
|
||||
lqw.eq(bo.getNoTaxAmount() != null, "o.no_tax_amount", bo.getNoTaxAmount());
|
||||
|
||||
@@ -9,8 +9,10 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.gear.oa.domain.GearJournal;
|
||||
import com.gear.oa.domain.GearOrder;
|
||||
import com.gear.oa.mapper.GearCustomerMapper;
|
||||
import com.gear.oa.mapper.GearJournalMapper;
|
||||
import com.gear.oa.mapper.GearOrderMapper;
|
||||
import com.gear.oa.service.IGearJournalService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -24,9 +26,11 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 应收款管理(宽松版)Service业务层处理
|
||||
@@ -39,6 +43,7 @@ import java.util.Collection;
|
||||
public class GearReceivableServiceImpl implements IGearReceivableService {
|
||||
|
||||
private final GearReceivableMapper baseMapper;
|
||||
private final GearOrderMapper orderMapper;
|
||||
@Resource
|
||||
private GearJournalMapper journalMapper;
|
||||
@Resource
|
||||
@@ -67,7 +72,16 @@ public class GearReceivableServiceImpl implements IGearReceivableService {
|
||||
QueryWrapper<GearReceivable> lqw = Wrappers.query();
|
||||
lqw.eq("r.del_flag", 0);
|
||||
lqw.eq(bo.getCustomerId() != null, "r.customer_id", bo.getCustomerId());
|
||||
lqw.eq(bo.getOrderId() != null, "r.order_id", bo.getOrderId());
|
||||
if (bo.getOrderId() != null) {
|
||||
lqw.eq("r.order_id", bo.getOrderId());
|
||||
} else if (bo.getSalesmanId() != null) {
|
||||
List<Long> orderIds = queryOrderIdsBySalesman(bo.getSalesmanId());
|
||||
if (orderIds.isEmpty()) {
|
||||
lqw.eq("r.receivable_id", -1L);
|
||||
} else {
|
||||
lqw.in("r.order_id", orderIds);
|
||||
}
|
||||
}
|
||||
lqw.eq(bo.getDueDate() != null, "r.due_date", bo.getDueDate());
|
||||
lqw.eq(bo.getAmount() != null, "r.amount", bo.getAmount());
|
||||
lqw.eq(bo.getPaidAmount() != null, "r.paid_amount", bo.getPaidAmount());
|
||||
@@ -77,6 +91,19 @@ public class GearReceivableServiceImpl implements IGearReceivableService {
|
||||
lqw.between(bo.getStartTime() != null && bo.getEndTime() != null, "r.create_time", bo.getStartTime(), bo.getEndTime());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
private List<Long> queryOrderIdsBySalesman(Long salesmanId) {
|
||||
if (salesmanId == null) return Collections.emptyList();
|
||||
LambdaQueryWrapper<GearOrder> lqw = Wrappers.lambdaQuery();
|
||||
lqw.select(GearOrder::getOrderId);
|
||||
lqw.eq(GearOrder::getSalesmanId, salesmanId);
|
||||
lqw.eq(GearOrder::getDelFlag, 0);
|
||||
List<GearOrder> list = orderMapper.selectList(lqw);
|
||||
if (list == null || list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return list.stream().map(GearOrder::getOrderId).filter(id -> id != null).distinct().collect(Collectors.toList());
|
||||
}
|
||||
/**
|
||||
* 查询应收款管理(宽松版)列表
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.gear.oa.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.gear.common.core.domain.PageQuery;
|
||||
import com.gear.common.core.page.TableDataInfo;
|
||||
import com.gear.common.utils.StringUtils;
|
||||
import com.gear.oa.domain.GearShippingOrderDetail;
|
||||
import com.gear.oa.domain.bo.GearShippingOrderDetailBo;
|
||||
import com.gear.oa.domain.vo.GearShippingOrderDetailVo;
|
||||
import com.gear.oa.mapper.GearShippingOrderDetailMapper;
|
||||
import com.gear.oa.service.IGearShippingOrderDetailService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class GearShippingOrderDetailServiceImpl implements IGearShippingOrderDetailService {
|
||||
|
||||
private final GearShippingOrderDetailMapper baseMapper;
|
||||
|
||||
@Override
|
||||
public GearShippingOrderDetailVo queryById(Long detailId) {
|
||||
return baseMapper.selectVoById(detailId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<GearShippingOrderDetailVo> queryPageList(GearShippingOrderDetailBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<GearShippingOrderDetail> lqw = buildQueryWrapper(bo);
|
||||
Page<GearShippingOrderDetailVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GearShippingOrderDetailVo> queryList(GearShippingOrderDetailBo bo) {
|
||||
LambdaQueryWrapper<GearShippingOrderDetail> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<GearShippingOrderDetail> buildQueryWrapper(GearShippingOrderDetailBo bo) {
|
||||
LambdaQueryWrapper<GearShippingOrderDetail> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getDetailId() != null, GearShippingOrderDetail::getDetailId, bo.getDetailId());
|
||||
lqw.eq(bo.getShippingId() != null, GearShippingOrderDetail::getShippingId, bo.getShippingId());
|
||||
lqw.eq(bo.getProductId() != null, GearShippingOrderDetail::getProductId, bo.getProductId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getProductName()), GearShippingOrderDetail::getProductName, bo.getProductName());
|
||||
lqw.orderByAsc(GearShippingOrderDetail::getSort);
|
||||
lqw.orderByAsc(GearShippingOrderDetail::getCreateTime);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean insertByBo(GearShippingOrderDetailBo bo) {
|
||||
GearShippingOrderDetail add = BeanUtil.toBean(bo, GearShippingOrderDetail.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setDetailId(add.getDetailId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean updateByBo(GearShippingOrderDetailBo bo) {
|
||||
GearShippingOrderDetail update = BeanUtil.toBean(bo, GearShippingOrderDetail.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
private void validEntityBeforeSave(GearShippingOrderDetail entity) {
|
||||
boolean isInsert = entity.getDetailId() == null;
|
||||
if (entity.getDetailId() == null) {
|
||||
entity.setDetailId(IdUtil.getSnowflakeNextId());
|
||||
}
|
||||
if (isInsert) {
|
||||
if (StringUtils.isBlank(entity.getDelFlag())) {
|
||||
entity.setDelFlag("0");
|
||||
}
|
||||
if (entity.getSort() == null) {
|
||||
entity.setSort(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,6 @@ public class GearShippingOrderServiceImpl implements IGearShippingOrderService {
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setShippingId(add.getShippingId());
|
||||
syncOrderStatusIfShipped(add);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
@@ -122,43 +121,9 @@ public class GearShippingOrderServiceImpl implements IGearShippingOrderService {
|
||||
GearShippingOrder update = BeanUtil.toBean(bo, GearShippingOrder.class);
|
||||
validEntityBeforeSave(update);
|
||||
boolean ok = baseMapper.updateById(update) > 0;
|
||||
if (ok) {
|
||||
syncOrderStatusIfShipped(update);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private void syncOrderStatusIfShipped(GearShippingOrder entity) {
|
||||
if (entity == null || entity.getOrderId() == null) return;
|
||||
Integer statusNum = null;
|
||||
try {
|
||||
if (StringUtils.isNotBlank(entity.getStatus())) {
|
||||
statusNum = Integer.parseInt(entity.getStatus());
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
statusNum = null;
|
||||
}
|
||||
if (statusNum == null) return;
|
||||
// 发货单据状态与订单状态联动:
|
||||
// - 发货单据 >=2(已发货):若订单仍为预订单(0),推进到进行中(1)
|
||||
// - 发货单据 >=3(已完成):若订单为预订单(0)/进行中(1),推进到已完成(2)
|
||||
// - 订单若已取消(3),不做推进
|
||||
if (statusNum >= 3) {
|
||||
orderMapper.update(null, Wrappers.<GearOrder>lambdaUpdate()
|
||||
.eq(GearOrder::getOrderId, entity.getOrderId())
|
||||
.ne(GearOrder::getOrderStatus, 3)
|
||||
.in(GearOrder::getOrderStatus, 0, 1)
|
||||
.set(GearOrder::getOrderStatus, 2));
|
||||
return;
|
||||
}
|
||||
if (statusNum >= 2) {
|
||||
orderMapper.update(null, Wrappers.<GearOrder>lambdaUpdate()
|
||||
.eq(GearOrder::getOrderId, entity.getOrderId())
|
||||
.eq(GearOrder::getOrderStatus, 0)
|
||||
.set(GearOrder::getOrderStatus, 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void validEntityBeforeSave(GearShippingOrder entity) {
|
||||
boolean isInsert = entity.getShippingId() == null;
|
||||
// 发货单据ID:项目内大部分业务表使用雪花ID,这里保持一致
|
||||
|
||||
@@ -362,6 +362,32 @@ public class GearStockIoOrderServiceImpl implements IGearStockIoOrderService {
|
||||
if (itemId == null) {
|
||||
throw new ServiceException("物料ID不能为空");
|
||||
}
|
||||
return queryMaterialFlowByItemIds(java.util.Collections.singletonList(itemId), itemId, startTime, endTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaterialFlowResp queryMaterialFlowByFactory(String factory, Date startTime, Date endTime) {
|
||||
if (StringUtils.isBlank(factory)) {
|
||||
throw new ServiceException("厂家不能为空");
|
||||
}
|
||||
List<Long> ids = matMaterialMapper.selectIdsByFactory(factory);
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
MaterialFlowResp resp = new MaterialFlowResp();
|
||||
resp.setItemId(null);
|
||||
resp.setStartTime(startTime);
|
||||
resp.setEndTime(endTime);
|
||||
resp.setConfirmInQty(BigDecimal.ZERO);
|
||||
resp.setOutQty(BigDecimal.ZERO);
|
||||
resp.setRevokeInQty(BigDecimal.ZERO);
|
||||
resp.setRevokeOutQty(BigDecimal.ZERO);
|
||||
resp.setNetQty(BigDecimal.ZERO);
|
||||
resp.setRows(new ArrayList<>());
|
||||
return resp;
|
||||
}
|
||||
return queryMaterialFlowByItemIds(ids, null, startTime, endTime);
|
||||
}
|
||||
|
||||
private MaterialFlowResp queryMaterialFlowByItemIds(List<Long> itemIds, Long singleItemId, Date startTime, Date endTime) {
|
||||
List<GearStockIoOrder> confirmOrders = baseMapper.selectList(Wrappers.<GearStockIoOrder>lambdaQuery()
|
||||
.eq(GearStockIoOrder::getIoType, "I")
|
||||
.eq(GearStockIoOrder::getConfirmInFlag, "1")
|
||||
@@ -400,7 +426,7 @@ public class GearStockIoOrderServiceImpl implements IGearStockIoOrderService {
|
||||
List<GearStockIoOrderDetail> details = detailMapper.selectList(Wrappers.<GearStockIoOrderDetail>lambdaQuery()
|
||||
.in(GearStockIoOrderDetail::getOrderId, orderIds)
|
||||
.eq(GearStockIoOrderDetail::getItemType, "material")
|
||||
.eq(GearStockIoOrderDetail::getItemId, itemId)
|
||||
.in(GearStockIoOrderDetail::getItemId, itemIds)
|
||||
.eq(GearStockIoOrderDetail::getDelFlag, "0"));
|
||||
for (GearStockIoOrderDetail d : details) {
|
||||
if (d == null || d.getOrderId() == null) continue;
|
||||
@@ -468,7 +494,7 @@ public class GearStockIoOrderServiceImpl implements IGearStockIoOrderService {
|
||||
BigDecimal net = confirmInQty.subtract(outQty).add(revokeOutQty).subtract(revokeInQty);
|
||||
|
||||
MaterialFlowResp resp = new MaterialFlowResp();
|
||||
resp.setItemId(itemId);
|
||||
resp.setItemId(singleItemId);
|
||||
resp.setStartTime(startTime);
|
||||
resp.setEndTime(endTime);
|
||||
resp.setConfirmInQty(confirmInQty);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="taxPrice" column="tax_price"/>
|
||||
<result property="noTaxPrice" column="no_tax_price"/>
|
||||
<result property="spec" column="spec"/>
|
||||
<result property="model" column="model"/>
|
||||
</resultMap>
|
||||
<select id="selectVoListByOrderId" resultType="com.gear.oa.domain.vo.GearOrderDetailVo">
|
||||
SELECT
|
||||
@@ -25,8 +27,8 @@
|
||||
COALESCE(p.product_name, mp.product_name) AS productName,
|
||||
COALESCE(p.product_code, CAST(mp.product_id AS CHAR)) AS productCode,
|
||||
COALESCE(p.type, mp.product_type) AS productType,
|
||||
mp.spec AS spec,
|
||||
mp.model AS model,
|
||||
COALESCE(d.spec, mp.spec) AS spec,
|
||||
COALESCE(d.model, mp.model) AS model,
|
||||
mp.unit_price AS unitPrice
|
||||
FROM gear_order_detail d
|
||||
LEFT JOIN gear_product p ON d.product_id = p.product_id
|
||||
@@ -39,8 +41,8 @@
|
||||
COALESCE(p.product_name, mp.product_name) AS productName,
|
||||
COALESCE(p.product_code, CAST(mp.product_id AS CHAR)) AS productCode,
|
||||
COALESCE(p.type, mp.product_type) AS productType,
|
||||
mp.spec AS spec,
|
||||
mp.model AS model,
|
||||
COALESCE(d.spec, mp.spec) AS spec,
|
||||
COALESCE(d.model, mp.model) AS model,
|
||||
mp.unit_price AS unitPrice
|
||||
FROM gear_order_detail d
|
||||
LEFT JOIN gear_product p ON d.product_id = p.product_id
|
||||
|
||||
@@ -30,20 +30,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
o.company,
|
||||
o.salesman_id,
|
||||
s.name as sales_manager,
|
||||
(CASE
|
||||
WHEN o.order_status = 3 THEN 3
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 3) THEN 2
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 2) THEN 1
|
||||
ELSE o.order_status
|
||||
END) AS order_status,
|
||||
o.order_status AS order_status,
|
||||
o.remark,
|
||||
o.del_flag,
|
||||
o.create_time,
|
||||
@@ -83,20 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
o.company,
|
||||
o.salesman_id,
|
||||
s.name as sales_manager,
|
||||
(CASE
|
||||
WHEN o.order_status = 3 THEN 3
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 3) THEN 2
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM gear_shipping_order so
|
||||
WHERE so.order_id = o.order_id
|
||||
AND so.del_flag = '0'
|
||||
AND CAST(so.status AS SIGNED) >= 2) THEN 1
|
||||
ELSE o.order_status
|
||||
END) AS order_status,
|
||||
o.order_status AS order_status,
|
||||
o.remark,
|
||||
o.del_flag,
|
||||
o.create_time,
|
||||
|
||||
@@ -66,7 +66,11 @@ export function importProductData(data, updateSupport) {
|
||||
return request({
|
||||
url: '/mat/product/importData?updateSupport=' + (updateSupport ? 1 : 0),
|
||||
method: 'post',
|
||||
data: data
|
||||
data: data,
|
||||
timeout: 1800000,
|
||||
headers: {
|
||||
repeatSubmit: false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,48 @@ export function delShippingOrder(shippingId) {
|
||||
})
|
||||
}
|
||||
|
||||
// ================================
|
||||
// 发货单据明细(gear_shipping_order_detail)
|
||||
// ================================
|
||||
|
||||
export function listShippingOrderDetail(query) {
|
||||
return request({
|
||||
url: '/oa/shippingOrderDetail/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getShippingOrderDetail(detailId) {
|
||||
return request({
|
||||
url: '/oa/shippingOrderDetail/' + detailId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addShippingOrderDetail(data) {
|
||||
return request({
|
||||
url: '/oa/shippingOrderDetail',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateShippingOrderDetail(data) {
|
||||
return request({
|
||||
url: '/oa/shippingOrderDetail',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export function delShippingOrderDetail(detailId) {
|
||||
return request({
|
||||
url: '/oa/shippingOrderDetail/' + detailId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// ================================
|
||||
// 发货计划(独立表 gear_shipping_plan)
|
||||
// ================================
|
||||
|
||||
@@ -36,6 +36,10 @@ service.interceptors.request.use(config => {
|
||||
config.params = {}
|
||||
config.url = url
|
||||
}
|
||||
const isFormData = typeof FormData !== 'undefined' && config.data instanceof FormData
|
||||
if (isFormData) {
|
||||
delete config.headers['Content-Type']
|
||||
}
|
||||
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
|
||||
const requestObj = {
|
||||
url: config.url,
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
<el-form-item label="辅料名称" prop="materialName">
|
||||
<el-input v-model="queryParams.materialName" placeholder="请输入辅料名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规格" prop="spec">
|
||||
<el-input v-model="queryParams.spec" placeholder="请输入规格" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="型号" prop="model">
|
||||
<el-input v-model="queryParams.model" placeholder="请输入型号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="厂家" prop="factory">
|
||||
<el-input v-model="queryParams.factory" placeholder="请输入厂家" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -40,6 +46,8 @@
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<!-- <el-table-column label="辅料ID 主键" align="center" prop="materialId" v-if="true" /> -->
|
||||
<el-table-column label="辅料名称" align="center" prop="materialName" />
|
||||
<el-table-column label="规格" align="center" prop="spec" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="型号" align="center" prop="model" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="物料类型" align="center" prop="materialType">
|
||||
<template #default="scope">
|
||||
{{ scope.row.materialType === 1 ? '辅料' : '原料' }}
|
||||
@@ -75,6 +83,12 @@
|
||||
<el-form-item label="辅料名称" prop="materialName">
|
||||
<el-input v-model="form.materialName" placeholder="请输入辅料名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规格" prop="spec">
|
||||
<el-input v-model="form.spec" placeholder="请输入规格" />
|
||||
</el-form-item>
|
||||
<el-form-item label="型号" prop="model">
|
||||
<el-input v-model="form.model" placeholder="请输入型号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="物料类型" prop="materialType">
|
||||
<el-select v-model="form.materialType" placeholder="请选择物料类型" disabled>
|
||||
<el-option label="辅料" value="1" />
|
||||
|
||||
@@ -2,6 +2,30 @@
|
||||
<div class="stock-dashboard-container" style="padding: 20px;">
|
||||
<!-- 加载状态 -->
|
||||
<el-loading v-loading="loading" text="数据加载中...">
|
||||
<el-card shadow="never" style="margin-bottom: 12px;">
|
||||
<el-form :inline="true" size="small" label-width="70px">
|
||||
<el-form-item label="厂家">
|
||||
<el-input v-model="statQuery.factory" placeholder="请输入厂家" clearable style="width: 220px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时间段">
|
||||
<el-date-picker
|
||||
v-model="statQuery.timeRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onStatSearch">查询</el-button>
|
||||
<el-button @click="onStatReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<!-- 第一行:4个指标卡 -->
|
||||
<el-row :gutter="20" mb="20">
|
||||
<el-col :span="6" v-for="item in statCards" :key="item.key">
|
||||
@@ -114,6 +138,11 @@ const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
|
||||
const statQuery = reactive({
|
||||
factory: undefined,
|
||||
timeRange: undefined
|
||||
})
|
||||
const total = ref(0) // 材料总条数
|
||||
|
||||
// 格式化数值:保留2位小数,处理字符串转数字
|
||||
@@ -144,7 +173,10 @@ const inOutCompareData = ref([]) // 出入库对比折线图数据
|
||||
// 1. 获取材料列表(带分页)
|
||||
const getMaterialList = async () => {
|
||||
try {
|
||||
const res = await listMaterial(queryParams) // 传入分页参数
|
||||
const res = await listMaterial({
|
||||
...queryParams,
|
||||
factory: statQuery.factory || undefined
|
||||
})
|
||||
materialList.value = res.rows || []
|
||||
total.value = res.total || 0 // 若依接口返回total总条数
|
||||
} catch (err) {
|
||||
@@ -153,13 +185,32 @@ const getMaterialList = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const buildStatParams = () => {
|
||||
const beginTime = statQuery.timeRange && statQuery.timeRange[0] ? statQuery.timeRange[0] : undefined
|
||||
const endTime = statQuery.timeRange && statQuery.timeRange[1] ? statQuery.timeRange[1] : undefined
|
||||
return {
|
||||
beginTime,
|
||||
endTime,
|
||||
factory: statQuery.factory || undefined
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 获取入库+出库列表(无需分页,取全部数据做统计)
|
||||
const getInOutList = async () => {
|
||||
try {
|
||||
const params = buildStatParams()
|
||||
// 并行请求,提高效率
|
||||
const [inRes, outRes] = await Promise.all([
|
||||
listPurchaseInDetail(),
|
||||
listMaterialOut()
|
||||
listPurchaseInDetail({
|
||||
pageNum: 1,
|
||||
pageSize: 10000,
|
||||
...params
|
||||
}),
|
||||
listMaterialOut({
|
||||
pageNum: 1,
|
||||
pageSize: 10000,
|
||||
...params
|
||||
})
|
||||
])
|
||||
purchaseInDetailList.value = inRes.rows || []
|
||||
materialOutList.value = outRes.rows || []
|
||||
@@ -301,6 +352,18 @@ const loadAllData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const onStatSearch = () => {
|
||||
queryParams.pageNum = 1
|
||||
loadAllData()
|
||||
}
|
||||
|
||||
const onStatReset = () => {
|
||||
statQuery.factory = undefined
|
||||
statQuery.timeRange = undefined
|
||||
queryParams.pageNum = 1
|
||||
loadAllData()
|
||||
}
|
||||
|
||||
// 页面挂载时加载数据
|
||||
onMounted(() => {
|
||||
loadAllData()
|
||||
@@ -318,4 +381,4 @@ onMounted(() => {
|
||||
.el-card {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -196,7 +196,7 @@ import { Document } from '@element-plus/icons-vue';
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const productTypeLabel = (val) => (val === 'semi' ? '半成品' : '产品');
|
||||
const productTypeLabel = (val) => (val === 'semi' ? '半成品' : '成品');
|
||||
const productTypeTagType = (val) => (val === 'semi' ? 'warning' : 'success');
|
||||
|
||||
const productDetail = ref({});
|
||||
|
||||
@@ -86,16 +86,20 @@
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="产品图片" align="center" prop="productImages">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.productImages && scope.row.productImages.trim()" class="image-preview">
|
||||
<div v-if="imageUrls(scope.row).length" class="image-preview">
|
||||
<el-image
|
||||
v-for="(image, index) in scope.row.productImages.split(',')"
|
||||
:key="index"
|
||||
v-for="(image, index) in imageUrls(scope.row)"
|
||||
:key="image + '_' + index"
|
||||
:src="image"
|
||||
:preview-src-list="scope.row.productImages.split(',')"
|
||||
:preview-src-list="imageUrls(scope.row)"
|
||||
:z-index="9999"
|
||||
:preview-teleported="true"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
>
|
||||
<template #error>
|
||||
<div class="image-error">—</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<span v-else>无</span>
|
||||
</template>
|
||||
@@ -146,7 +150,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="产品类型" prop="productType">
|
||||
<el-select v-model="form.productType" placeholder="请选择产品类型" style="width: 100%">
|
||||
<el-option label="产品" value="product" />
|
||||
<el-option label="成品" value="product" />
|
||||
<el-option label="半成品" value="semi" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -405,6 +409,7 @@ const updateSupport = ref(false);
|
||||
const importFile = ref(null);
|
||||
const additionPreviewMap = ref({});
|
||||
const additionPreviewLoading = ref({});
|
||||
const imagePreviewMap = ref({});
|
||||
const manualOpen = ref(false);
|
||||
const manualLoading = ref(false);
|
||||
const manualFiles = ref([]);
|
||||
@@ -430,7 +435,7 @@ const formatterTime = (time) => {
|
||||
return proxy.parseTime(time, '{y}-{m}-{d}')
|
||||
}
|
||||
|
||||
const productTypeLabel = (val) => (val === 'semi' ? '半成品' : '产品');
|
||||
const productTypeLabel = (val) => (val === 'semi' ? '半成品' : '成品');
|
||||
const productTypeTagType = (val) => (val === 'semi' ? 'warning' : 'success');
|
||||
|
||||
const data = reactive({
|
||||
@@ -471,6 +476,7 @@ function getList() {
|
||||
productList.value = response.rows;
|
||||
total.value = response.total;
|
||||
prefetchAdditions(productList.value);
|
||||
prefetchImages(productList.value);
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
@@ -549,11 +555,28 @@ function handleUpdate(row) {
|
||||
|
||||
// 处理图片文件列表
|
||||
if (form.value.productImages) {
|
||||
imageFileList.value = form.value.productImages.split(',').map((url, index) => ({
|
||||
name: url.substring(url.lastIndexOf('/') + 1),
|
||||
url: url,
|
||||
uid: Date.now() + index
|
||||
}));
|
||||
const raw = String(form.value.productImages).trim();
|
||||
if (isOssIdList(raw)) {
|
||||
listByIds(raw).then(res => {
|
||||
const list = res?.data || [];
|
||||
const urls = list.map(oss => oss?.url).filter(Boolean);
|
||||
form.value.productImages = urls.join(',');
|
||||
imageFileList.value = urls.map((url, index) => ({
|
||||
name: getFileNameFromUrl(url),
|
||||
url,
|
||||
uid: Date.now() + index
|
||||
}));
|
||||
}).catch(() => {
|
||||
imageFileList.value = [];
|
||||
});
|
||||
} else {
|
||||
const urls = raw.split(',').map(s => String(s).trim()).filter(Boolean);
|
||||
imageFileList.value = urls.map((url, index) => ({
|
||||
name: getFileNameFromUrl(url),
|
||||
url,
|
||||
uid: Date.now() + index
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
imageFileList.value = [];
|
||||
}
|
||||
@@ -884,6 +907,59 @@ function prefetchAdditions(list) {
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
function normalizeImageUrlList(raw) {
|
||||
const str = raw == null ? '' : String(raw).trim();
|
||||
if (!str) return [];
|
||||
if (isOssIdList(str)) return [];
|
||||
return str
|
||||
.split(',')
|
||||
.map(s => String(s).trim())
|
||||
.filter(Boolean)
|
||||
.filter(u => /^https?:\/\//i.test(u) || u.startsWith('/') || u.startsWith('data:'));
|
||||
}
|
||||
|
||||
function imageUrls(row) {
|
||||
const key = row?.productId != null ? String(row.productId) : '';
|
||||
const cached = key ? imagePreviewMap.value[key] : null;
|
||||
if (Array.isArray(cached)) return cached;
|
||||
return normalizeImageUrlList(row?.productImages);
|
||||
}
|
||||
|
||||
function prefetchImages(list) {
|
||||
const rows = Array.isArray(list) ? list : [];
|
||||
const targets = rows
|
||||
.map(r => ({ productId: r?.productId, raw: String(r?.productImages || '').trim() }))
|
||||
.filter(x => x.productId != null && x.raw && isOssIdList(x.raw));
|
||||
if (!targets.length) return;
|
||||
const idSet = new Set();
|
||||
for (const it of targets) {
|
||||
const ids = it.raw.split(',').map(s => String(s).trim()).filter(Boolean);
|
||||
for (const id of ids) idSet.add(id);
|
||||
}
|
||||
const all = Array.from(idSet);
|
||||
if (!all.length) return;
|
||||
listByIds(all.join(',')).then(res => {
|
||||
const list = res?.data || [];
|
||||
const map = {};
|
||||
for (const oss of list) {
|
||||
if (oss?.ossId != null && oss?.url) {
|
||||
map[String(oss.ossId)] = oss.url;
|
||||
}
|
||||
}
|
||||
const next = { ...imagePreviewMap.value };
|
||||
for (const it of targets) {
|
||||
const urls = it.raw
|
||||
.split(',')
|
||||
.map(s => String(s).trim())
|
||||
.filter(Boolean)
|
||||
.map(id => map[String(id)])
|
||||
.filter(Boolean);
|
||||
next[String(it.productId)] = urls;
|
||||
}
|
||||
imagePreviewMap.value = next;
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
function pdfCount(row) {
|
||||
const raw = String(row?.productPdfs || '').trim();
|
||||
if (!raw) return 0;
|
||||
@@ -1092,6 +1168,18 @@ getList();
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.image-error {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f5f7fa;
|
||||
color: #c0c4cc;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
:deep(.el-table .el-table__cell) {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
|
||||
@@ -74,13 +74,13 @@
|
||||
{{ formatDecimal(scope.row.receivedQty) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="收回状态" align="center" width="110">
|
||||
<!-- <el-table-column label="收回状态" align="center" width="110">
|
||||
<template #default="scope">
|
||||
<el-tag :type="receiveStatusTagType(scope.row)" effect="dark">
|
||||
{{ receiveStatusText(scope.row) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="委外时间" align="center" prop="outTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ formatterTime(scope.row.outTime) }}</span>
|
||||
@@ -90,7 +90,7 @@
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="Check" :disabled="isFullyReceived(scope.row)" @click="openReceive(scope.row)">收回</el-button>
|
||||
<!-- <el-button link type="primary" icon="Check" :disabled="isFullyReceived(scope.row)" @click="openReceive(scope.row)">收回</el-button> -->
|
||||
<el-button link type="primary" icon="Delete" :disabled="hasReceived(scope.row)" @click="handleDelete(scope.row)">撤回</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -149,7 +149,7 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="收回" v-model="receiveOpen" width="460px" append-to-body>
|
||||
<!-- <el-dialog title="收回" v-model="receiveOpen" width="460px" append-to-body>
|
||||
<el-form ref="receiveRef" :model="receiveForm" label-width="90px">
|
||||
<el-form-item label="收回数量">
|
||||
<el-input-number v-model="receiveForm.receiveQty" style="width: 100%" :controls="false" :min="0" :precision="4" />
|
||||
@@ -167,12 +167,12 @@
|
||||
<el-button @click="receiveOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ProductOutsourceOrder">
|
||||
import { listProductOutsourceOrder, addProductOutsourceOrder, delProductOutsourceOrder, receiveProductOutsourceOrder } from "@/api/mat/productOutsourceOrder";
|
||||
import { listProductOutsourceOrder, addProductOutsourceOrder, delProductOutsourceOrder } from "@/api/mat/productOutsourceOrder";
|
||||
import { listProductBase } from "@/api/mat/product";
|
||||
import useUserStore from '@/store/modules/user'
|
||||
import { formatDecimal } from '@/utils/gear'
|
||||
@@ -194,9 +194,9 @@ const title = ref("");
|
||||
const productOptions = ref([]);
|
||||
const productLoading = ref(false);
|
||||
|
||||
const receiveOpen = ref(false);
|
||||
const receiveLoading = ref(false);
|
||||
const receiveForm = ref({ orderId: null, receiveQty: null, receiveTime: null, receiveBy: null });
|
||||
// const receiveOpen = ref(false);
|
||||
// const receiveLoading = ref(false);
|
||||
// const receiveForm = ref({ orderId: null, receiveQty: null, receiveTime: null, receiveBy: null });
|
||||
|
||||
const formatterTime = (time) => proxy.parseTime(time, '{y}-{m}-{d}')
|
||||
const productTypeLabel = (val) => (val === 'semi' ? '半成品' : '成品');
|
||||
@@ -204,20 +204,24 @@ const productTypeTagType = (val) => (val === 'semi' ? 'warning' : 'success');
|
||||
|
||||
const rowQty = (v) => (v === null || v === undefined ? 0 : Number(v) || 0);
|
||||
const hasReceived = (row) => rowQty(row?.receivedQty) > 0;
|
||||
const isFullyReceived = (row) => rowQty(row?.receivedQty) >= rowQty(row?.quantity);
|
||||
const receiveStatusText = (row) => {
|
||||
const totalQty = rowQty(row?.quantity);
|
||||
const receivedQty = rowQty(row?.receivedQty);
|
||||
if (receivedQty <= 0) return '未收回';
|
||||
if (receivedQty >= totalQty) return '已收回';
|
||||
return '部分收回';
|
||||
};
|
||||
const receiveStatusTagType = (row) => {
|
||||
const t = receiveStatusText(row);
|
||||
if (t === '已收回') return 'success';
|
||||
if (t === '部分收回') return 'warning';
|
||||
return 'info';
|
||||
const isFullyReceived = (row) => {
|
||||
const total = rowQty(row?.quantity);
|
||||
if (total <= 0) return false;
|
||||
return rowQty(row?.receivedQty) >= total;
|
||||
};
|
||||
// const receiveStatusText = (row) => {
|
||||
// const totalQty = rowQty(row?.quantity);
|
||||
// const receivedQty = rowQty(row?.receivedQty);
|
||||
// if (receivedQty <= 0) return '未收回';
|
||||
// if (receivedQty >= totalQty) return '已收回';
|
||||
// return '部分收回';
|
||||
// };
|
||||
// const receiveStatusTagType = (row) => {
|
||||
// const t = receiveStatusText(row);
|
||||
// if (t === '已收回') return 'success';
|
||||
// if (t === '部分收回') return 'warning';
|
||||
// return 'info';
|
||||
// };
|
||||
|
||||
const productOptionLabel = (p) => {
|
||||
const name = p?.productName || '-';
|
||||
@@ -243,7 +247,15 @@ const { queryParams, form, rules } = toRefs(data);
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
listProductOutsourceOrder(queryParams.value).then(res => {
|
||||
list.value = res.rows;
|
||||
const rows = (res && res.rows) ? res.rows : [];
|
||||
const filtered = rows.filter(r => !isFullyReceived(r));
|
||||
if (filtered.length === 0 && queryParams.value.pageNum > 1) {
|
||||
queryParams.value.pageNum = queryParams.value.pageNum - 1;
|
||||
loading.value = false;
|
||||
getList();
|
||||
return;
|
||||
}
|
||||
list.value = filtered;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
});
|
||||
@@ -325,36 +337,36 @@ function handleDelete(row) {
|
||||
});
|
||||
}
|
||||
|
||||
function openReceive(row) {
|
||||
const outTime = proxy.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
|
||||
const totalQty = rowQty(row?.quantity);
|
||||
const receivedQty = rowQty(row?.receivedQty);
|
||||
const remaining = totalQty - receivedQty;
|
||||
receiveForm.value = {
|
||||
orderId: row?.orderId,
|
||||
receiveQty: remaining > 0 ? remaining : 0,
|
||||
receiveTime: outTime,
|
||||
receiveBy: nickName
|
||||
};
|
||||
receiveOpen.value = true;
|
||||
}
|
||||
// function openReceive(row) {
|
||||
// const outTime = proxy.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
|
||||
// const totalQty = rowQty(row?.quantity);
|
||||
// const receivedQty = rowQty(row?.receivedQty);
|
||||
// const remaining = totalQty - receivedQty;
|
||||
// receiveForm.value = {
|
||||
// orderId: row?.orderId,
|
||||
// receiveQty: remaining > 0 ? remaining : 0,
|
||||
// receiveTime: outTime,
|
||||
// receiveBy: nickName
|
||||
// };
|
||||
// receiveOpen.value = true;
|
||||
// }
|
||||
|
||||
function submitReceive() {
|
||||
if (receiveLoading.value) return;
|
||||
const orderId = receiveForm.value.orderId;
|
||||
receiveLoading.value = true;
|
||||
receiveProductOutsourceOrder(orderId, {
|
||||
receiveQty: receiveForm.value.receiveQty,
|
||||
receiveTime: receiveForm.value.receiveTime,
|
||||
receiveBy: receiveForm.value.receiveBy
|
||||
}).then(() => {
|
||||
proxy.$modal.msgSuccess("收回成功");
|
||||
receiveOpen.value = false;
|
||||
getList();
|
||||
}).finally(() => {
|
||||
receiveLoading.value = false;
|
||||
});
|
||||
}
|
||||
// function submitReceive() {
|
||||
// if (receiveLoading.value) return;
|
||||
// const orderId = receiveForm.value.orderId;
|
||||
// receiveLoading.value = true;
|
||||
// receiveProductOutsourceOrder(orderId, {
|
||||
// receiveQty: receiveForm.value.receiveQty,
|
||||
// receiveTime: receiveForm.value.receiveTime,
|
||||
// receiveBy: receiveForm.value.receiveBy
|
||||
// }).then(() => {
|
||||
// proxy.$modal.msgSuccess("收回成功");
|
||||
// receiveOpen.value = false;
|
||||
// getList();
|
||||
// }).finally(() => {
|
||||
// receiveLoading.value = false;
|
||||
// });
|
||||
// }
|
||||
|
||||
function handleExport() {
|
||||
proxy.download('mat/productOutsourceOrder/export', {
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
<el-form-item label="配料名称" prop="materialName">
|
||||
<el-input v-model="queryParams.materialName" placeholder="请输入配料名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规格" prop="spec">
|
||||
<el-input v-model="queryParams.spec" placeholder="请输入规格" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="型号" prop="model">
|
||||
<el-input v-model="queryParams.model" placeholder="请输入型号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="厂家" prop="factory">
|
||||
<el-input v-model="queryParams.factory" placeholder="请输入厂家" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
@@ -44,6 +50,8 @@
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<!-- <el-table-column label="配料ID 主键" align="center" prop="materialId" v-if="true" /> -->
|
||||
<el-table-column label="配料名称" align="center" prop="materialName" />
|
||||
<el-table-column label="规格" align="center" prop="spec" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="型号" align="center" prop="model" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="物料类型" align="center" prop="materialType">
|
||||
<template #default="scope">
|
||||
{{ scope.row.materialType === 1 ? '辅料' : '主材' }}
|
||||
@@ -79,6 +87,12 @@
|
||||
<el-form-item label="配料名称" prop="materialName">
|
||||
<el-input v-model="form.materialName" placeholder="请输入配料名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="规格" prop="spec">
|
||||
<el-input v-model="form.spec" placeholder="请输入规格" />
|
||||
</el-form-item>
|
||||
<el-form-item label="型号" prop="model">
|
||||
<el-input v-model="form.model" placeholder="请输入型号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="物料类型" prop="materialType">
|
||||
<el-select v-model="form.materialType" placeholder="请选择物料类型" disabled>
|
||||
<el-option label="主材" value="2" />
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<span class="order-left-card__value">{{ item.customerName || "—" }}</span>
|
||||
</div>
|
||||
<div class="order-left-card__header-right">
|
||||
<dict-tag class="order-left-card__status" :options="order_status" :value="item.orderStatus" />
|
||||
<div class="order-left-card__status-spacer" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -86,13 +86,18 @@
|
||||
<div class="top-attachment-title">
|
||||
<span>订单附件</span>
|
||||
<div class="top-attachment-actions">
|
||||
<!-- 预览按钮:打开弹窗预览(避免在Tab上侧区域占用过多空间) -->
|
||||
<el-button plain size="small" :disabled="!form.contractExcelOssIds" @click="openAttachmentPreview">
|
||||
预览
|
||||
<el-button
|
||||
v-if="form.contractExcelOssIds"
|
||||
plain
|
||||
size="small"
|
||||
style="margin-right: 8px;"
|
||||
@click="clearAttachment"
|
||||
>
|
||||
删除附件
|
||||
</el-button>
|
||||
<FileUpload
|
||||
v-model="form.contractExcelOssIds"
|
||||
:limit="5"
|
||||
:limit="1"
|
||||
:file-size="20"
|
||||
:file-type="['xls', 'xlsx', 'pdf']"
|
||||
:is-show-tip="false"
|
||||
@@ -102,50 +107,9 @@
|
||||
</div>
|
||||
|
||||
<div class="top-attachment-body">
|
||||
<!-- 附件概览:只展示数量与文件名(不在这里做预览) -->
|
||||
<div class="top-attachment-summary">
|
||||
<div class="top-attachment-summary__left">
|
||||
<span class="top-attachment-summary__label">数量:</span>
|
||||
<span class="top-attachment-summary__value">{{ attachmentFiles.length }}</span>
|
||||
</div>
|
||||
<div class="top-attachment-summary__right">
|
||||
<el-empty v-if="!attachmentFiles.length" description="暂无附件" />
|
||||
<el-scrollbar v-else max-height="120px">
|
||||
<div
|
||||
v-for="(item, index) in attachmentFiles"
|
||||
:key="item.ossId || item.url || index"
|
||||
class="top-attachment-file"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单附件预览弹窗:左侧列表 + 右侧预览(对齐产品说明书预览的形式) -->
|
||||
<el-dialog title="订单附件预览" v-model="attachmentPreviewOpen" width="1200px" append-to-body>
|
||||
<div class="attachment-preview-wrap" v-loading="attachmentPreviewLoading">
|
||||
<div class="attachment-list">
|
||||
<div class="attachment-list-title">附件列表({{ attachmentFiles.length }})</div>
|
||||
<el-empty v-if="!attachmentFiles.length" description="暂无附件" />
|
||||
<el-scrollbar v-else max-height="520px">
|
||||
<div
|
||||
v-for="(item, index) in attachmentFiles"
|
||||
:key="item.ossId || item.url || index"
|
||||
class="attachment-item"
|
||||
:class="{ active: item.url === currentAttachmentUrl }"
|
||||
@click="selectAttachment(item)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<div class="attachment-viewer">
|
||||
<el-empty v-if="!currentAttachmentUrl" description="请选择附件" />
|
||||
<div class="attachment-viewer top-attachment-single-viewer">
|
||||
<el-empty v-if="!currentAttachmentUrl" description="暂无附件" />
|
||||
<iframe v-else-if="canIframePreview" :src="iframePreviewUrl" class="attachment-iframe" />
|
||||
<!-- Excel 预览:不依赖第三方在线预览,直接在前端解析展示(不保证样式100%一致,但可看数据) -->
|
||||
<div v-else-if="isExcelPreview" class="excel-preview-wrap" v-loading="excelPreviewLoading">
|
||||
<component
|
||||
v-if="vueOfficeExcelComp && officeExcelUrl"
|
||||
@@ -163,7 +127,7 @@
|
||||
<div class="excel-table-tip" v-if="excelTruncated">
|
||||
预览已截断,仅展示前 {{ excelMaxRows }} 行、前 {{ excelMaxCols }} 列
|
||||
</div>
|
||||
<el-scrollbar max-height="520px">
|
||||
<el-scrollbar max-height="420px">
|
||||
<table class="excel-preview-table">
|
||||
<tbody>
|
||||
<tr v-for="(row, rIdx) in excelPreviewRows" :key="rIdx">
|
||||
@@ -183,12 +147,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="attachmentPreviewOpen = false">关闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<!-- 发货单据新增弹窗:写入新表 gear_shipping_order(与出入库单据无关) -->
|
||||
<el-dialog title="新增发货单据" v-model="shippingAddOpen" width="900px" append-to-body>
|
||||
@@ -304,12 +263,86 @@
|
||||
<el-table :data="orderDetailList" size="small" border>
|
||||
<el-table-column label="产品编号" prop="productCode" min-width="120" />
|
||||
<el-table-column label="产品名称" prop="productName" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="产品规格" prop="spec" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="产品型号" prop="model" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="预计发出数量" prop="quantity" width="120" align="center" />
|
||||
<el-table-column label="单位" prop="unit" width="80" align="center" />
|
||||
<el-table-column label="产品单价" prop="unitPrice" width="100" align="center" />
|
||||
<el-table-column label="备注" prop="remark" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="产品规格" min-width="150">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row._editSpec"
|
||||
size="small"
|
||||
placeholder="规格"
|
||||
:disabled="!orderDetailEditable || scope.row._saving"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="产品型号" min-width="140">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row._editModel"
|
||||
size="small"
|
||||
placeholder="型号"
|
||||
:disabled="!orderDetailEditable || scope.row._saving"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计发出数量" width="140" align="center">
|
||||
<template #default="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row._editQuantity"
|
||||
size="small"
|
||||
:controls="false"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
style="width: 120px;"
|
||||
:disabled="!orderDetailEditable || scope.row._saving"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" width="110" align="center">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row._editUnit"
|
||||
size="small"
|
||||
placeholder="单位"
|
||||
:disabled="!orderDetailEditable || scope.row._saving"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="产品单价" width="140" align="center">
|
||||
<template #default="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row._editUnitPrice"
|
||||
size="small"
|
||||
:controls="false"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
style="width: 120px;"
|
||||
:disabled="!orderDetailEditable || scope.row._saving"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="160">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row._editRemark"
|
||||
size="small"
|
||||
placeholder="备注"
|
||||
:disabled="!orderDetailEditable || scope.row._saving"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="scope.row._saving"
|
||||
:disabled="!orderDetailEditable || !isOrderDetailRowChanged(scope.row)"
|
||||
@click="saveOrderDetailRow(scope.row)"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
@@ -394,6 +427,7 @@
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!--
|
||||
<el-tab-pane label="生产成果" name="production">
|
||||
<div class="mt-4" v-loading="productionLoading">
|
||||
<div class="production-toolbar">
|
||||
@@ -458,6 +492,7 @@
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
-->
|
||||
|
||||
<el-tab-pane label="发货" name="shippingAlloc">
|
||||
<div class="mt-4">
|
||||
@@ -650,7 +685,7 @@ import ReturnExchange from './return.vue';
|
||||
import Receive from './receive.vue';
|
||||
import { listSalesman } from "@/api/oms/salesman";
|
||||
import { listShippingOrder, addShippingOrder, delShippingOrder } from "@/api/oms/shippingOrder";
|
||||
import { listOrderDetail, addOrderDetail } from "@/api/oms/orderDetail";
|
||||
import { listOrderDetail, addOrderDetail, updateOrderDetail } from "@/api/oms/orderDetail";
|
||||
import { listProductBase } from "@/api/mat/product";
|
||||
import { sumStockQuantityByItemIds } from "@/api/wms/stock";
|
||||
import { listReceivable } from "@/api/finance/receivable";
|
||||
@@ -709,6 +744,11 @@ export default {
|
||||
const list = Array.isArray(this.productionList) ? this.productionList : [];
|
||||
if (!this.productionOnlyProduct) return list;
|
||||
return list.filter(r => String(r && r.productType || "") === "product");
|
||||
},
|
||||
orderDetailEditable() {
|
||||
const s = this.form && this.form.orderStatus != null ? Number(this.form.orderStatus) : NaN;
|
||||
if (Number.isNaN(s)) return true;
|
||||
return s !== EOrderStatus.CANCEL;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -748,8 +788,6 @@ export default {
|
||||
receivedAmount: 0,
|
||||
unreceivedAmount: 0
|
||||
},
|
||||
// 附件预览弹窗:点击“预览”按钮后打开
|
||||
attachmentPreviewOpen: false,
|
||||
attachmentPreviewLoading: false,
|
||||
attachmentFiles: [],
|
||||
currentAttachmentOssId: null,
|
||||
@@ -788,6 +826,8 @@ export default {
|
||||
quantity: 1,
|
||||
unit: "",
|
||||
unitPrice: "",
|
||||
spec: "",
|
||||
model: "",
|
||||
remark: ""
|
||||
},
|
||||
productionLoading: false,
|
||||
@@ -985,9 +1025,9 @@ export default {
|
||||
if (this.activeTab === "logs") {
|
||||
this.loadOperLogs();
|
||||
}
|
||||
if (this.activeTab === "production") {
|
||||
this.loadProduction(item.orderId);
|
||||
}
|
||||
// if (this.activeTab === "production") {
|
||||
// this.loadProduction(item.orderId);
|
||||
// }
|
||||
},
|
||||
|
||||
/** 加载订单详情 */
|
||||
@@ -1018,9 +1058,9 @@ export default {
|
||||
if (this.activeTab === "logs") {
|
||||
this.loadOperLogs();
|
||||
}
|
||||
if (this.activeTab === "production") {
|
||||
this.loadProduction(orderId);
|
||||
}
|
||||
// if (this.activeTab === "production") {
|
||||
// this.loadProduction(orderId);
|
||||
// }
|
||||
}).catch(() => {
|
||||
this.$modal.msgError("加载订单详情失败");
|
||||
}).finally(() => {
|
||||
@@ -1042,12 +1082,18 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/** 点击“预览”按钮:打开附件预览弹窗 */
|
||||
openAttachmentPreview() {
|
||||
this.attachmentPreviewOpen = true;
|
||||
if (!this.attachmentFiles.length) {
|
||||
clearAttachment() {
|
||||
if (!this.form || !this.form.orderId) return;
|
||||
updateOrder({
|
||||
orderId: this.form.orderId,
|
||||
contractExcelOssIds: ""
|
||||
}).then(() => {
|
||||
this.form.contractExcelOssIds = "";
|
||||
this.$modal.msgSuccess("附件已删除");
|
||||
this.loadAttachmentFiles();
|
||||
}
|
||||
}).catch(() => {
|
||||
this.$modal.msgError("附件删除失败");
|
||||
});
|
||||
},
|
||||
|
||||
/** 加载附件列表(ossId串 -> 文件列表) */
|
||||
@@ -1060,18 +1106,28 @@ export default {
|
||||
this.currentAttachmentExt = "";
|
||||
return;
|
||||
}
|
||||
const firstId = raw.split(",").map(s => String(s).trim()).filter(Boolean)[0] || "";
|
||||
if (firstId && firstId !== raw) {
|
||||
this.form.contractExcelOssIds = firstId;
|
||||
}
|
||||
this.attachmentPreviewLoading = true;
|
||||
listByIds(raw).then(res => {
|
||||
listByIds(firstId || raw).then(res => {
|
||||
const list = (res && res.data) ? res.data : [];
|
||||
this.attachmentFiles = list.map((oss, idx) => {
|
||||
const name = oss.originalName || oss.fileName || String(oss.ossId);
|
||||
const url = oss.url;
|
||||
return { name, url, ossId: oss.ossId, uid: oss.ossId || (Date.now() + idx) };
|
||||
});
|
||||
// 如果当前没有选中附件,或者选中的附件已经不在列表里(例如切换订单/删除/重新上传),默认选中第一条
|
||||
// 如果当前没有选中附件,或者选中的附件已经不在列表里(例如切换订单/删除/重新上传),默认选中一条(优先 Excel)
|
||||
const hasCurrent = this.attachmentFiles.some(f => f.url === this.currentAttachmentUrl);
|
||||
if (this.attachmentFiles.length > 0 && (!this.currentAttachmentUrl || !hasCurrent)) {
|
||||
this.selectAttachment(this.attachmentFiles[0]);
|
||||
const prefer = this.attachmentFiles.find(f => {
|
||||
const name = String(f && f.name ? f.name : "");
|
||||
const ext = name.split(".").pop();
|
||||
const e = ext ? String(ext).toLowerCase() : "";
|
||||
return e === "xls" || e === "xlsx";
|
||||
});
|
||||
this.selectAttachment(prefer || this.attachmentFiles[0]);
|
||||
}
|
||||
}).catch(() => {
|
||||
this.attachmentFiles = [];
|
||||
@@ -1282,9 +1338,9 @@ export default {
|
||||
if (name === "logs") {
|
||||
this.loadOperLogs();
|
||||
}
|
||||
if (name === "production") {
|
||||
this.loadProduction(this.selectedOrderId);
|
||||
}
|
||||
// if (name === "production") {
|
||||
// this.loadProduction(this.selectedOrderId);
|
||||
// }
|
||||
},
|
||||
|
||||
loadProduction(orderId) {
|
||||
@@ -1468,7 +1524,22 @@ export default {
|
||||
return listOrderDetail({ orderId: orderId, pageNum: 1, pageSize: 9999 }).then(res => {
|
||||
const rows = res.rows || [];
|
||||
// 订单管理页的“发货/订单明细”口径:只展示成品(product),避免半成品/原料进入发货视图
|
||||
this.orderDetailList = rows.filter(r => String(r.productType || "").toLowerCase() === "product");
|
||||
this.orderDetailList = rows
|
||||
.filter(r => String(r.productType || "").toLowerCase() === "product")
|
||||
.map(r => {
|
||||
const q = r && r.quantity != null ? Number(r.quantity) : 0;
|
||||
const unitPrice = r && r.taxPrice != null ? Number(r.taxPrice) : (r && r.unitPrice != null ? Number(r.unitPrice) : 0);
|
||||
return {
|
||||
...r,
|
||||
_editSpec: r && r.spec != null ? String(r.spec) : "",
|
||||
_editModel: r && r.model != null ? String(r.model) : "",
|
||||
_editQuantity: Number.isFinite(q) ? q : 0,
|
||||
_editUnit: r && r.unit != null ? String(r.unit) : "",
|
||||
_editUnitPrice: Number.isFinite(unitPrice) ? unitPrice : 0,
|
||||
_editRemark: r && r.remark != null ? String(r.remark) : "",
|
||||
_saving: false
|
||||
};
|
||||
});
|
||||
return this.orderDetailList;
|
||||
}).catch(() => {
|
||||
this.orderDetailList = [];
|
||||
@@ -1608,6 +1679,8 @@ export default {
|
||||
quantity: 1,
|
||||
unit: "",
|
||||
unitPrice: "",
|
||||
spec: "",
|
||||
model: "",
|
||||
remark: ""
|
||||
};
|
||||
this.orderDetailAddOpen = true;
|
||||
@@ -1630,6 +1703,8 @@ export default {
|
||||
if (!hit) return;
|
||||
this.orderDetailAddForm.unit = hit.unit || this.orderDetailAddForm.unit;
|
||||
this.orderDetailAddForm.unitPrice = hit.unitPrice != null ? String(hit.unitPrice) : this.orderDetailAddForm.unitPrice;
|
||||
this.orderDetailAddForm.spec = hit.spec != null ? String(hit.spec) : this.orderDetailAddForm.spec;
|
||||
this.orderDetailAddForm.model = hit.model != null ? String(hit.model) : this.orderDetailAddForm.model;
|
||||
},
|
||||
|
||||
/** 订单明细:提交新增(写入 gear_order_detail) */
|
||||
@@ -1644,12 +1719,18 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.orderDetailAddLoading = true;
|
||||
const priceNum = this.orderDetailAddForm.unitPrice != null && String(this.orderDetailAddForm.unitPrice).trim() !== ""
|
||||
? Number(this.orderDetailAddForm.unitPrice)
|
||||
: null;
|
||||
addOrderDetail({
|
||||
orderId: this.selectedOrderId,
|
||||
productId: this.orderDetailAddForm.productId,
|
||||
quantity: this.orderDetailAddForm.quantity,
|
||||
unit: this.orderDetailAddForm.unit,
|
||||
remark: this.orderDetailAddForm.remark
|
||||
remark: this.orderDetailAddForm.remark,
|
||||
spec: this.orderDetailAddForm.spec,
|
||||
model: this.orderDetailAddForm.model,
|
||||
taxPrice: priceNum != null && Number.isFinite(priceNum) ? priceNum : undefined
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess("新增成功");
|
||||
this.orderDetailAddOpen = false;
|
||||
@@ -1659,6 +1740,46 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
isOrderDetailRowChanged(row) {
|
||||
if (!row) return false;
|
||||
const q0 = row.quantity != null ? Number(row.quantity) : 0;
|
||||
const q1 = row._editQuantity != null ? Number(row._editQuantity) : 0;
|
||||
const u0 = row.unit != null ? String(row.unit) : "";
|
||||
const u1 = row._editUnit != null ? String(row._editUnit) : "";
|
||||
const r0 = row.remark != null ? String(row.remark) : "";
|
||||
const r1 = row._editRemark != null ? String(row._editRemark) : "";
|
||||
const s0 = row.spec != null ? String(row.spec) : "";
|
||||
const s1 = row._editSpec != null ? String(row._editSpec) : "";
|
||||
const m0 = row.model != null ? String(row.model) : "";
|
||||
const m1 = row._editModel != null ? String(row._editModel) : "";
|
||||
const p0 = row.taxPrice != null ? Number(row.taxPrice) : (row.unitPrice != null ? Number(row.unitPrice) : 0);
|
||||
const p1 = row._editUnitPrice != null ? Number(row._editUnitPrice) : 0;
|
||||
const priceChanged = (Number.isFinite(p0) ? p0 : 0) !== (Number.isFinite(p1) ? p1 : 0);
|
||||
return q0 !== q1 || u0 !== u1 || r0 !== r1 || s0 !== s1 || m0 !== m1 || priceChanged;
|
||||
},
|
||||
|
||||
saveOrderDetailRow(row) {
|
||||
if (!row || !row.detailId) return;
|
||||
if (!this.isOrderDetailRowChanged(row)) return;
|
||||
row._saving = true;
|
||||
const payload = {
|
||||
detailId: row.detailId,
|
||||
orderId: row.orderId,
|
||||
quantity: row._editQuantity != null ? Number(row._editQuantity) : 0,
|
||||
unit: row._editUnit != null ? String(row._editUnit) : "",
|
||||
remark: row._editRemark != null ? String(row._editRemark) : "",
|
||||
spec: row._editSpec != null ? String(row._editSpec) : "",
|
||||
model: row._editModel != null ? String(row._editModel) : "",
|
||||
taxPrice: row._editUnitPrice != null ? Number(row._editUnitPrice) : 0
|
||||
};
|
||||
updateOrderDetail(payload).then(() => {
|
||||
this.$modal.msgSuccess("已保存");
|
||||
this.loadShippingAllocData(this.selectedOrderId);
|
||||
}).finally(() => {
|
||||
row._saving = false;
|
||||
});
|
||||
},
|
||||
|
||||
// 发货单据:打开新增弹窗(默认带入订单信息,由后端生成发货单号)
|
||||
openShippingAdd() {
|
||||
if (!this.selectedOrderId) return;
|
||||
@@ -1919,6 +2040,10 @@ export default {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.order-left-card__status-spacer {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
.order-left-card__status :deep(.el-tag) {
|
||||
border-radius: 4px;
|
||||
}
|
||||
@@ -2090,8 +2215,7 @@ export default {
|
||||
}
|
||||
|
||||
.top-attachment-actions :deep(.upload-file-list) {
|
||||
/* 显示上传后的文件列表(用户能直观看到“已上传了哪些文件”) */
|
||||
display: block;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.top-attachment-actions :deep(.upload-file-list .el-upload-list__item) {
|
||||
@@ -2102,6 +2226,10 @@ export default {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.top-attachment-single-viewer {
|
||||
height: 420px;
|
||||
}
|
||||
|
||||
.top-attachment-summary {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
@@ -65,9 +65,12 @@
|
||||
<el-tabs v-model="activeTab" class="right-tabs" @tab-change="handleTabChange">
|
||||
<el-tab-pane label="跟进客户" name="followCustomer" />
|
||||
<el-tab-pane label="跟进合同" name="followContract" />
|
||||
<el-tab-pane label="历史订单" name="historyOrders" />
|
||||
<el-tab-pane label="订单异议" name="dispute" />
|
||||
<el-tab-pane label="财务状态" name="finance" />
|
||||
<el-tab-pane label="发货单据" name="shippingDocs" />
|
||||
<el-tab-pane label="生产成果" name="production" />
|
||||
<el-tab-pane label="计划发货" name="planShipping" />
|
||||
<el-tab-pane label="销售信息编辑" name="salesmanInfo" />
|
||||
</el-tabs>
|
||||
|
||||
<div class="right-body">
|
||||
@@ -120,6 +123,115 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'historyOrders'">
|
||||
<div class="orders-section">
|
||||
<div class="finance-title-row">
|
||||
<div class="finance-title">历史订单</div>
|
||||
<div class="history-actions">
|
||||
<el-input
|
||||
v-model="historyQuery.orderCode"
|
||||
size="small"
|
||||
clearable
|
||||
placeholder="订单编号"
|
||||
style="width: 180px"
|
||||
@keyup.enter="loadHistoryOrders"
|
||||
/>
|
||||
<el-button size="small" plain icon="Refresh" :loading="historyLoading" @click="loadHistoryOrders">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table v-loading="historyLoading" :data="historyOrderList" border stripe :header-cell-style="{ background: '#f5f7fa' }">
|
||||
<el-table-column label="订单编号" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="客户" prop="customerName" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="orderStatusTagType(scope.row.orderStatus)" size="small">
|
||||
{{ orderStatusLabel(scope.row.orderStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime" width="180" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'dispute'">
|
||||
<div class="orders-section">
|
||||
<div class="finance-title-row">
|
||||
<div class="finance-title">订单异议</div>
|
||||
<el-button size="small" plain icon="Refresh" :loading="disputeLoading" @click="loadDisputeOrders">刷新</el-button>
|
||||
</div>
|
||||
<el-table v-loading="disputeLoading" :data="disputeOrderList" border stripe :header-cell-style="{ background: '#f5f7fa' }">
|
||||
<el-table-column label="订单编号" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="客户" prop="customerName" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="orderStatusTagType(scope.row.orderStatus)" size="small">
|
||||
{{ orderStatusLabel(scope.row.orderStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime" width="180" />
|
||||
<el-table-column label="操作" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" icon="View" @click="openDispute(scope.row)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog title="订单异议" v-model="disputeOpen" width="1200px" append-to-body>
|
||||
<el-empty v-if="!disputeOrderId" description="请选择订单" />
|
||||
<ReturnExchange v-else :orderId="disputeOrderId" />
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="disputeOpen = false">关闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'finance'">
|
||||
<div class="orders-section">
|
||||
<div class="finance-title-row">
|
||||
<div class="finance-title">财务状态</div>
|
||||
<el-button size="small" plain icon="Refresh" :loading="financeLoading" @click="loadFinance">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<div class="finance-strip" v-loading="financeLoading">
|
||||
<div class="finance-item">
|
||||
<div class="finance-item__label">应收总金额</div>
|
||||
<div class="finance-item__value">{{ formatMoney(financeSummary.receivableAmount) }}</div>
|
||||
</div>
|
||||
<div class="finance-item">
|
||||
<div class="finance-item__label">已收款金额</div>
|
||||
<div class="finance-item__value">{{ formatMoney(financeSummary.receivedAmount) }}</div>
|
||||
</div>
|
||||
<div class="finance-item">
|
||||
<div class="finance-item__label">未收款金额</div>
|
||||
<div class="finance-item__value">{{ formatMoney(financeSummary.unreceivedAmount) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="finance-subtitle">收款明细</div>
|
||||
<el-table v-loading="financeLoading" :data="financeList" border stripe :header-cell-style="{ background: '#f5f7fa' }">
|
||||
<el-table-column label="订单编号" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="客户" prop="customerName" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="到期日" prop="dueDate" width="180" />
|
||||
<el-table-column label="应收" prop="amount" width="140" align="right">
|
||||
<template #default="scope">{{ formatMoney(scope.row.amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="已收" prop="paidAmount" width="140" align="right">
|
||||
<template #default="scope">{{ formatMoney(scope.row.paidAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="未收" prop="balanceAmount" width="140" align="right">
|
||||
<template #default="scope">{{ formatMoney(scope.row.balanceAmount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status" width="120" />
|
||||
<el-table-column label="备注" prop="remark" min-width="180" show-overflow-tooltip />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'shippingDocs'">
|
||||
<el-table v-loading="shippingLoading" :data="shippingList">
|
||||
<el-table-column label="发货单号" prop="shippingNo" min-width="180" />
|
||||
@@ -132,29 +244,6 @@
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'production'">
|
||||
<div class="production-wrap">
|
||||
<div class="production-summary">
|
||||
<div class="production-summary__item">订单数:{{ productionSummary.orderCount }}</div>
|
||||
<div class="production-summary__item">计划总量:{{ formatQty(productionSummary.planQty) }}</div>
|
||||
<div class="production-summary__item">已完成总量:{{ formatQty(productionSummary.finishedQty) }}</div>
|
||||
<div class="production-summary__item">完成率:{{ productionSummary.rateText }}</div>
|
||||
</div>
|
||||
<el-table v-loading="productionLoading" :data="productionOrderSummaryList" size="small" border>
|
||||
<el-table-column label="订单编号" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="客户" prop="customerName" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="计划" prop="planQty" width="120" align="center" />
|
||||
<el-table-column label="完成" prop="finishedQty" width="120" align="center" />
|
||||
<el-table-column label="完成率" width="120" align="center">
|
||||
<template #default="scope">
|
||||
{{ formatRate(scope.row.finishedQty, scope.row.planQty) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最近更新" prop="lastUpdateTime" min-width="160" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'planShipping'">
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
@@ -212,6 +301,30 @@
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<div v-show="activeTab === 'salesmanInfo'">
|
||||
<el-form ref="salesmanInfoRef" :model="salesmanInfoForm" label-width="90px" class="salesman-info-form">
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="salesmanInfoForm.name" placeholder="请输入销售员姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机">
|
||||
<el-input v-model="salesmanInfoForm.mobile" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="salesmanInfoForm.status">
|
||||
<el-radio :value="0">正常</el-radio>
|
||||
<el-radio :value="1">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="salesmanInfoForm.remark" type="textarea" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="salesmanInfoSaving" @click="saveSalesmanInfo">保存</el-button>
|
||||
<el-button @click="resetSalesmanInfo">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -252,16 +365,19 @@ import { listSalesman, addSalesman, updateSalesman, delSalesman, listSalesmanCus
|
||||
import { listOrder } from "@/api/oms/order";
|
||||
import { listShippingOrder } from "@/api/oms/shippingOrder";
|
||||
import { listOrderDetail } from "@/api/oms/orderDetail";
|
||||
import { listOrderProduction } from "@/api/oms/orderProduction";
|
||||
import { listReceivable } from "@/api/finance/receivable";
|
||||
import ReturnExchange from "@/views/oms/order/panels/return.vue";
|
||||
|
||||
export default {
|
||||
name: "Salesman",
|
||||
components: { ReturnExchange },
|
||||
data() {
|
||||
return {
|
||||
leftLoading: false,
|
||||
salesmanList: [],
|
||||
selectedSalesmanId: undefined,
|
||||
selectedSalesmanName: "",
|
||||
selectedSalesman: {},
|
||||
activeTab: "followCustomer",
|
||||
|
||||
// 左侧查询条件(用于筛选销售员列表)
|
||||
@@ -290,18 +406,38 @@ export default {
|
||||
pageSize: 20
|
||||
},
|
||||
|
||||
historyLoading: false,
|
||||
historyOrderList: [],
|
||||
historyQuery: {
|
||||
orderCode: ""
|
||||
},
|
||||
|
||||
// 发货单据
|
||||
shippingLoading: false,
|
||||
shippingList: [],
|
||||
|
||||
// 生产成果:按订单汇总(只读)
|
||||
productionLoading: false,
|
||||
productionOrderSummaryList: [],
|
||||
financeLoading: false,
|
||||
financeList: [],
|
||||
financeSummary: {
|
||||
receivableAmount: 0,
|
||||
receivedAmount: 0,
|
||||
unreceivedAmount: 0
|
||||
},
|
||||
financeOrderCodeMap: {},
|
||||
|
||||
disputeLoading: false,
|
||||
disputeOrderList: [],
|
||||
disputeOpen: false,
|
||||
disputeOrderId: null,
|
||||
|
||||
// 计划发货:产品明细(按销售员全部订单汇总)
|
||||
planDetailLoading: false,
|
||||
planOrderDetailList: [],
|
||||
|
||||
salesmanInfoForm: {},
|
||||
salesmanInfoFormOrigin: {},
|
||||
salesmanInfoSaving: false,
|
||||
|
||||
// 新增/修改弹窗
|
||||
dialogOpen: false,
|
||||
dialogTitle: "",
|
||||
@@ -326,14 +462,6 @@ export default {
|
||||
shippedOrders
|
||||
};
|
||||
},
|
||||
productionSummary() {
|
||||
const list = Array.isArray(this.productionOrderSummaryList) ? this.productionOrderSummaryList : [];
|
||||
const orderCount = list.length;
|
||||
const planQty = list.reduce((sum, r) => sum + Number(r && r.planQty != null ? r.planQty : 0), 0);
|
||||
const finishedQty = list.reduce((sum, r) => sum + Number(r && r.finishedQty != null ? r.finishedQty : 0), 0);
|
||||
const rateText = planQty > 0 ? `${((finishedQty / planQty) * 100).toFixed(1)}%` : "0%";
|
||||
return { orderCount, planQty, finishedQty, rateText };
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getSalesmanList();
|
||||
@@ -373,18 +501,26 @@ export default {
|
||||
if (!item || !item.salesmanId) return;
|
||||
this.selectedSalesmanId = item.salesmanId;
|
||||
this.selectedSalesmanName = item.name;
|
||||
this.selectedSalesman = { ...item };
|
||||
this.initSalesmanInfoForm(item);
|
||||
this.customerQuery.pageNum = 1;
|
||||
this.orderQuery.pageNum = 1;
|
||||
this.loadCustomers();
|
||||
if (this.activeTab === "followContract") {
|
||||
this.loadOrders();
|
||||
}
|
||||
if (this.activeTab === "historyOrders") {
|
||||
this.loadHistoryOrders();
|
||||
}
|
||||
if (this.activeTab === "dispute") {
|
||||
this.loadDisputeOrders();
|
||||
}
|
||||
if (this.activeTab === "finance") {
|
||||
this.loadFinance();
|
||||
}
|
||||
if (this.activeTab === "shippingDocs") {
|
||||
this.loadShippingOrders();
|
||||
}
|
||||
if (this.activeTab === "production") {
|
||||
this.loadProductionSummary();
|
||||
}
|
||||
if (this.activeTab === "planShipping") {
|
||||
this.loadPlanShipping();
|
||||
}
|
||||
@@ -399,15 +535,24 @@ export default {
|
||||
if (this.activeTab === "followContract") {
|
||||
this.loadOrders();
|
||||
}
|
||||
if (this.activeTab === "historyOrders") {
|
||||
this.loadHistoryOrders();
|
||||
}
|
||||
if (this.activeTab === "dispute") {
|
||||
this.loadDisputeOrders();
|
||||
}
|
||||
if (this.activeTab === "finance") {
|
||||
this.loadFinance();
|
||||
}
|
||||
if (this.activeTab === "shippingDocs") {
|
||||
this.loadShippingOrders();
|
||||
}
|
||||
if (this.activeTab === "production") {
|
||||
this.loadProductionSummary();
|
||||
}
|
||||
if (this.activeTab === "planShipping") {
|
||||
this.loadPlanShipping();
|
||||
}
|
||||
if (this.activeTab === "salesmanInfo") {
|
||||
this.initSalesmanInfoForm(this.selectedSalesman);
|
||||
}
|
||||
},
|
||||
|
||||
// 跟进客户:后端反查客户
|
||||
@@ -441,6 +586,53 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
loadHistoryOrders() {
|
||||
if (!this.selectedSalesmanId) return;
|
||||
this.historyLoading = true;
|
||||
listOrder({
|
||||
salesmanId: this.selectedSalesmanId,
|
||||
orderCode: this.historyQuery.orderCode || undefined,
|
||||
pageNum: 1,
|
||||
pageSize: 9999
|
||||
})
|
||||
.then(res => {
|
||||
const list = (res && res.rows) ? res.rows : [];
|
||||
this.historyOrderList = list.filter(o => this.isHistoryOrder(o));
|
||||
})
|
||||
.finally(() => {
|
||||
this.historyLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
isHistoryOrder(order) {
|
||||
const s = order && order.orderStatus != null ? order.orderStatus : null;
|
||||
const n = Number(s);
|
||||
if (Number.isFinite(n)) return n === 2 || n === 3;
|
||||
const t = String(s || "");
|
||||
return t.includes("完成") || t.includes("取消");
|
||||
},
|
||||
|
||||
orderStatusLabel(status) {
|
||||
const n = Number(status);
|
||||
if (Number.isFinite(n)) {
|
||||
if (n === 0) return "预订单";
|
||||
if (n === 1) return "进行中";
|
||||
if (n === 2) return "已完成";
|
||||
if (n === 3) return "已取消";
|
||||
}
|
||||
return String(status || "-");
|
||||
},
|
||||
|
||||
orderStatusTagType(status) {
|
||||
const n = Number(status);
|
||||
if (Number.isFinite(n)) {
|
||||
if (n === 2) return "success";
|
||||
if (n === 3) return "info";
|
||||
if (n === 1) return "warning";
|
||||
}
|
||||
return "info";
|
||||
},
|
||||
|
||||
loadShippingOrders() {
|
||||
if (!this.selectedSalesmanId) return;
|
||||
this.shippingLoading = true;
|
||||
@@ -453,40 +645,71 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
async loadProductionSummary() {
|
||||
loadDisputeOrders() {
|
||||
if (!this.selectedSalesmanId) return;
|
||||
this.productionLoading = true;
|
||||
try {
|
||||
const orderRes = await listOrder({ salesmanId: this.selectedSalesmanId, pageNum: 1, pageSize: 9999 });
|
||||
const orders = (orderRes && orderRes.rows) ? orderRes.rows : [];
|
||||
const promises = orders
|
||||
.filter(o => o && o.orderId != null)
|
||||
.map(async (o) => {
|
||||
const prodRes = await listOrderProduction({ orderId: o.orderId, pageNum: 1, pageSize: 9999 });
|
||||
const rows = (prodRes && prodRes.rows) ? prodRes.rows : [];
|
||||
const planQty = rows.reduce((sum, r) => sum + Number(r && r.planQty != null ? r.planQty : 0), 0);
|
||||
const finishedQty = rows.reduce((sum, r) => sum + Number(r && r.finishedQty != null ? r.finishedQty : 0), 0);
|
||||
let lastUpdateTime = "";
|
||||
rows.forEach(r => {
|
||||
const t = r && r.updateTime ? String(r.updateTime) : "";
|
||||
if (t && (!lastUpdateTime || new Date(t).getTime() > new Date(lastUpdateTime).getTime())) {
|
||||
lastUpdateTime = t;
|
||||
}
|
||||
});
|
||||
this.disputeLoading = true;
|
||||
listOrder({ salesmanId: this.selectedSalesmanId, pageNum: 1, pageSize: 9999 })
|
||||
.then(res => {
|
||||
this.disputeOrderList = (res && res.rows) ? res.rows : [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.disputeLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
openDispute(row) {
|
||||
if (!row || !row.orderId) return;
|
||||
this.disputeOrderId = row.orderId;
|
||||
this.disputeOpen = true;
|
||||
},
|
||||
|
||||
formatMoney(v) {
|
||||
const n = Number(v);
|
||||
return Number.isFinite(n) ? n.toFixed(2) : "0.00";
|
||||
},
|
||||
|
||||
loadFinance() {
|
||||
if (!this.selectedSalesmanId) return;
|
||||
this.financeLoading = true;
|
||||
Promise.all([
|
||||
listReceivable({ salesmanId: this.selectedSalesmanId, pageNum: 1, pageSize: 9999 }),
|
||||
listOrder({ salesmanId: this.selectedSalesmanId, pageNum: 1, pageSize: 9999 })
|
||||
])
|
||||
.then(([recRes, orderRes]) => {
|
||||
const receivables = (recRes && recRes.rows) ? recRes.rows : [];
|
||||
const orders = (orderRes && orderRes.rows) ? orderRes.rows : [];
|
||||
|
||||
const codeMap = {};
|
||||
orders.forEach(o => {
|
||||
if (!o || o.orderId == null) return;
|
||||
codeMap[o.orderId] = o.orderCode || String(o.orderId);
|
||||
});
|
||||
this.financeOrderCodeMap = codeMap;
|
||||
|
||||
const rows = receivables.map(r => {
|
||||
const orderId = r && r.orderId != null ? r.orderId : null;
|
||||
return {
|
||||
orderId: o.orderId,
|
||||
orderCode: o.orderCode,
|
||||
customerName: o.customerName,
|
||||
planQty,
|
||||
finishedQty,
|
||||
lastUpdateTime
|
||||
...r,
|
||||
orderCode: orderId != null ? (codeMap[orderId] || String(orderId)) : "-"
|
||||
};
|
||||
});
|
||||
this.financeList = rows;
|
||||
|
||||
this.productionOrderSummaryList = await Promise.all(promises);
|
||||
} finally {
|
||||
this.productionLoading = false;
|
||||
}
|
||||
const receivableAmount = rows.reduce((sum, r) => sum + (Number(r && r.amount != null ? r.amount : 0) || 0), 0);
|
||||
const receivedAmount = rows.reduce((sum, r) => sum + (Number(r && r.paidAmount != null ? r.paidAmount : 0) || 0), 0);
|
||||
const unreceivedAmount = rows.reduce((sum, r) => {
|
||||
const bal = r && r.balanceAmount != null ? Number(r.balanceAmount) : NaN;
|
||||
if (Number.isFinite(bal)) return sum + bal;
|
||||
const a = Number(r && r.amount != null ? r.amount : 0) || 0;
|
||||
const p = Number(r && r.paidAmount != null ? r.paidAmount : 0) || 0;
|
||||
return sum + Math.max(0, a - p);
|
||||
}, 0);
|
||||
|
||||
this.financeSummary = { receivableAmount, receivedAmount, unreceivedAmount };
|
||||
})
|
||||
.finally(() => {
|
||||
this.financeLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
loadPlanShipping() {
|
||||
@@ -564,6 +787,42 @@ export default {
|
||||
return `${((fa / fb) * 100).toFixed(1)}%`;
|
||||
},
|
||||
|
||||
initSalesmanInfoForm(item) {
|
||||
const row = item && item.salesmanId ? item : null;
|
||||
if (!row) {
|
||||
this.salesmanInfoForm = {};
|
||||
this.salesmanInfoFormOrigin = {};
|
||||
return;
|
||||
}
|
||||
const form = {
|
||||
salesmanId: row.salesmanId,
|
||||
name: row.name || "",
|
||||
mobile: row.mobile || "",
|
||||
status: row.status != null ? row.status : 0,
|
||||
remark: row.remark || ""
|
||||
};
|
||||
this.salesmanInfoForm = { ...form };
|
||||
this.salesmanInfoFormOrigin = { ...form };
|
||||
},
|
||||
|
||||
resetSalesmanInfo() {
|
||||
this.salesmanInfoForm = { ...this.salesmanInfoFormOrigin };
|
||||
},
|
||||
|
||||
saveSalesmanInfo() {
|
||||
if (!this.salesmanInfoForm || !this.salesmanInfoForm.salesmanId) return;
|
||||
this.salesmanInfoSaving = true;
|
||||
updateSalesman(this.salesmanInfoForm)
|
||||
.then(() => {
|
||||
this.$modal.msgSuccess("保存成功");
|
||||
this.salesmanInfoFormOrigin = { ...this.salesmanInfoForm };
|
||||
this.getSalesmanList();
|
||||
})
|
||||
.finally(() => {
|
||||
this.salesmanInfoSaving = false;
|
||||
});
|
||||
},
|
||||
|
||||
// 新增
|
||||
handleAdd() {
|
||||
this.dialogTitle = "新增销售员";
|
||||
@@ -758,4 +1017,63 @@ export default {
|
||||
.production-summary__item {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.orders-section {
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.finance-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.history-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.salesman-info-form {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.finance-title {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.finance-strip {
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 10px;
|
||||
background: #f5f7fa;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.finance-item {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.finance-item__label {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.finance-item__value {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.finance-subtitle {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -141,8 +141,13 @@
|
||||
</el-descriptions>
|
||||
|
||||
<div class="shipping-detail-products" v-loading="detailProductsLoading">
|
||||
<!-- 发货单绑定订单后:展示该订单的订单明细(仅成品),用于发货与打印 -->
|
||||
<div class="shipping-detail-products__title">订单明细(成品)</div>
|
||||
<div class="shipping-detail-products__title-row">
|
||||
<div class="shipping-detail-products__title">发货单据明细</div>
|
||||
<div class="shipping-detail-products__ops">
|
||||
<el-button size="small" type="primary" plain icon="Plus" @click="handleDetailAdd">新增产品</el-button>
|
||||
<el-button size="small" plain icon="Refresh" @click="reloadCurrentDetail">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="detailProducts" size="small" border>
|
||||
<el-table-column label="产品编号" prop="productCode" min-width="120" />
|
||||
<el-table-column label="产品名称" prop="productName" min-width="160" show-overflow-tooltip />
|
||||
@@ -151,6 +156,12 @@
|
||||
<el-table-column label="数量" prop="quantity" width="100" align="center" />
|
||||
<el-table-column label="单位" prop="unit" width="90" align="center" />
|
||||
<el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="140" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" size="small" @click="handleDetailEdit(scope.row)">修改</el-button>
|
||||
<el-button link type="danger" size="small" @click="handleDetailDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="shipping-detail-products__extra" v-if="detailProducts && detailProducts.length">
|
||||
@@ -265,9 +276,15 @@
|
||||
</el-form>
|
||||
<el-table v-loading="orderSelectLoading" :data="orderSelectList" highlight-current-row @row-dblclick="confirmOrderBind">
|
||||
<el-table-column label="订单编号" prop="orderCode" min-width="160" />
|
||||
<el-table-column label="订单ID" prop="orderId" min-width="160" />
|
||||
<el-table-column label="客户ID" prop="customerId" min-width="120" />
|
||||
<el-table-column label="状态" prop="orderStatus" width="100" />
|
||||
<el-table-column label="客户" prop="customerName" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="销售员" prop="salesManager" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="订单状态" width="110" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="orderStatusTagType(scope.row.orderStatus)" effect="dark">
|
||||
{{ orderStatusLabel(scope.row.orderStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime" min-width="160" />
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template #default="scope">
|
||||
@@ -287,6 +304,50 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="detailEditTitle" v-model="detailEditOpen" width="520px" append-to-body>
|
||||
<el-form ref="detailEditFormRef" :model="detailEditForm" :rules="detailEditRules" label-width="90px" v-loading="detailEditLoading">
|
||||
<el-form-item label="产品" prop="productId">
|
||||
<el-select
|
||||
v-model="detailEditForm.productId"
|
||||
placeholder="请选择产品"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="remoteSearchDetailProducts"
|
||||
:loading="detailProductLoading"
|
||||
style="width: 100%"
|
||||
@change="handleDetailProductPicked"
|
||||
>
|
||||
<el-option
|
||||
v-for="p in detailProductOptions"
|
||||
:key="p.productId"
|
||||
:label="detailProductOptionLabel(p)"
|
||||
:value="p.productId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="规格">
|
||||
<el-input v-model="detailEditForm.spec" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="型号">
|
||||
<el-input v-model="detailEditForm.model" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="数量" prop="quantity">
|
||||
<el-input-number v-model="detailEditForm.quantity" style="width: 100%" :controls="false" :min="0" :precision="4" />
|
||||
</el-form-item>
|
||||
<el-form-item label="单位" prop="unit">
|
||||
<el-input v-model="detailEditForm.unit" placeholder="单位" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="detailEditForm.remark" type="textarea" :rows="2" placeholder="备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="detailEditOpen = false">取消</el-button>
|
||||
<el-button type="primary" :loading="detailEditLoading" @click="submitDetailEdit">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 发货计划 新增/编辑弹窗 -->
|
||||
<el-dialog :title="planTitle" v-model="planOpen" width="520px" append-to-body>
|
||||
<el-form ref="planFormRef" :model="planForm" label-width="90px" v-loading="planSubmitLoading">
|
||||
@@ -403,6 +464,10 @@ import {
|
||||
addShippingOrder,
|
||||
updateShippingOrder,
|
||||
delShippingOrder,
|
||||
listShippingOrderDetail,
|
||||
addShippingOrderDetail,
|
||||
updateShippingOrderDetail,
|
||||
delShippingOrderDetail,
|
||||
listShippingPlanWithCount,
|
||||
addShippingPlan,
|
||||
updateShippingPlan,
|
||||
@@ -412,9 +477,10 @@ import {
|
||||
import { listOrder, getOrder } from '@/api/oms/order'
|
||||
import { getCustomer } from '@/api/oms/customer'
|
||||
import { listSalesman } from '@/api/oms/salesman'
|
||||
import { listOrderDetail } from '@/api/oms/orderDetail'
|
||||
import { getDicts } from '@/api/system/dict/data'
|
||||
import { listProductAdditionByProductIds } from '@/api/mat/product'
|
||||
import { listProductBase } from '@/api/mat/product'
|
||||
import { getProduct as getOaProduct } from '@/api/oa/product'
|
||||
import * as XLSX from 'xlsx'
|
||||
import CustomerSelect from '@/components/CustomerSelect/index.vue'
|
||||
|
||||
@@ -442,10 +508,21 @@ export default {
|
||||
// 右侧当前选中行(下方明细区展示)
|
||||
currentRow: null,
|
||||
currentOrderInfo: null,
|
||||
// 当前发货单绑定订单的订单明细(用于页面展示与打印)
|
||||
// 当前发货单据的明细(用于页面展示与打印)
|
||||
detailProductsLoading: false,
|
||||
detailProducts: [],
|
||||
detailAdditionMap: {},
|
||||
detailEditOpen: false,
|
||||
detailEditTitle: '',
|
||||
detailEditLoading: false,
|
||||
detailEditForm: {},
|
||||
detailEditRules: {
|
||||
productId: [{ required: true, message: '请选择产品', trigger: 'change' }],
|
||||
quantity: [{ required: true, message: '请输入数量', trigger: 'blur' }],
|
||||
unit: [{ required: true, message: '请输入单位', trigger: 'blur' }]
|
||||
},
|
||||
detailProductOptions: [],
|
||||
detailProductLoading: false,
|
||||
salesmanOptions: [],
|
||||
orderCompanyOptions: [],
|
||||
// 完成状态选项(对应 gear_shipping_order.status)
|
||||
@@ -685,24 +762,24 @@ export default {
|
||||
},
|
||||
handleCurrentChange(row) {
|
||||
this.currentRow = row || null
|
||||
// 选择发货单时,加载其绑定订单的订单明细(仅成品)
|
||||
if (this.currentRow && this.currentRow.orderId) {
|
||||
this.loadOrderDetailProducts(this.currentRow.orderId)
|
||||
this.loadOrderInfo(this.currentRow.orderId)
|
||||
if (this.currentRow && this.currentRow.shippingId) {
|
||||
this.loadShippingOrderDetailProducts(this.currentRow.shippingId)
|
||||
} else {
|
||||
this.detailProducts = []
|
||||
this.detailAdditionMap = {}
|
||||
}
|
||||
if (this.currentRow && this.currentRow.orderId) {
|
||||
this.loadOrderInfo(this.currentRow.orderId)
|
||||
} else {
|
||||
this.currentOrderInfo = null
|
||||
}
|
||||
},
|
||||
/** 发货单绑定订单后:获取对应订单的订单明细产品数据(不包含库存,只取订单明细) */
|
||||
loadOrderDetailProducts(orderId) {
|
||||
loadShippingOrderDetailProducts(shippingId) {
|
||||
this.detailProductsLoading = true
|
||||
listOrderDetail({ orderId: orderId, pageNum: 1, pageSize: 9999 })
|
||||
listShippingOrderDetail({ shippingId: shippingId, pageNum: 1, pageSize: 9999 })
|
||||
.then(res => {
|
||||
const rows = (res && res.rows) ? res.rows : []
|
||||
// 仅展示成品(product),符合“只有成品类型才可以进入发货”
|
||||
this.detailProducts = rows.filter(r => String(r.productType || '').toLowerCase() === 'product')
|
||||
this.detailProducts = rows
|
||||
const ids = this.detailProducts.map(r => r.productId).filter(Boolean)
|
||||
this.loadProductAdditions(ids).then(map => {
|
||||
this.detailAdditionMap = map
|
||||
@@ -747,7 +824,118 @@ export default {
|
||||
this.$modal.msgSuccess('状态已更新')
|
||||
})
|
||||
},
|
||||
/** 打印预览:打开弹窗并加载绑定订单的订单明细产品数据 */
|
||||
reloadCurrentDetail() {
|
||||
if (!this.currentRow || !this.currentRow.shippingId) return
|
||||
this.loadShippingOrderDetailProducts(this.currentRow.shippingId)
|
||||
},
|
||||
resetDetailEdit() {
|
||||
const shippingId = this.currentRow && this.currentRow.shippingId ? this.currentRow.shippingId : undefined
|
||||
this.detailEditForm = {
|
||||
detailId: undefined,
|
||||
shippingId,
|
||||
productId: undefined,
|
||||
productCode: '',
|
||||
productName: '',
|
||||
spec: '',
|
||||
model: '',
|
||||
quantity: 0,
|
||||
unit: '',
|
||||
remark: '',
|
||||
sort: 0
|
||||
}
|
||||
this.detailProductOptions = []
|
||||
this.resetForm('detailEditFormRef')
|
||||
},
|
||||
handleDetailAdd() {
|
||||
if (!this.currentRow || !this.currentRow.shippingId) {
|
||||
this.$modal.msgError('请先选择发货单')
|
||||
return
|
||||
}
|
||||
this.resetDetailEdit()
|
||||
this.detailEditTitle = '新增产品'
|
||||
this.detailEditOpen = true
|
||||
},
|
||||
handleDetailEdit(row) {
|
||||
if (!row || !row.detailId) return
|
||||
this.resetDetailEdit()
|
||||
this.detailEditForm = Object.assign(this.detailEditForm, row)
|
||||
this.detailEditTitle = '修改产品'
|
||||
this.detailEditOpen = true
|
||||
if (row.productId) {
|
||||
this.detailProductOptions = [{
|
||||
productId: row.productId,
|
||||
productName: row.productName,
|
||||
spec: row.spec,
|
||||
model: row.model,
|
||||
productType: 'product'
|
||||
}]
|
||||
}
|
||||
},
|
||||
handleDetailDelete(row) {
|
||||
if (!row || !row.detailId) return
|
||||
this.$modal.confirm('是否确认删除该产品行?').then(() => {
|
||||
return delShippingOrderDetail(String(row.detailId))
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
this.reloadCurrentDetail()
|
||||
})
|
||||
},
|
||||
remoteSearchDetailProducts(keyword) {
|
||||
const q = String(keyword || '').trim()
|
||||
this.detailProductLoading = true
|
||||
listProductBase({
|
||||
pageNum: 1,
|
||||
pageSize: 50,
|
||||
productType: 'product',
|
||||
productName: q
|
||||
}).then(res => {
|
||||
this.detailProductOptions = (res && res.rows) ? res.rows : []
|
||||
}).finally(() => {
|
||||
this.detailProductLoading = false
|
||||
})
|
||||
},
|
||||
detailProductOptionLabel(p) {
|
||||
const name = p && p.productName ? p.productName : '-'
|
||||
const parts = [p && p.spec, p && p.model].filter(Boolean)
|
||||
return parts.length ? `${name}(${parts.join(' / ')})` : name
|
||||
},
|
||||
handleDetailProductPicked(productId) {
|
||||
const id = productId
|
||||
const hit = (this.detailProductOptions || []).find(p => String(p.productId) === String(id))
|
||||
if (hit) {
|
||||
this.detailEditForm.productName = hit.productName || this.detailEditForm.productName
|
||||
this.detailEditForm.spec = hit.spec || ''
|
||||
this.detailEditForm.model = hit.model || ''
|
||||
}
|
||||
if (id != null && String(id) !== '') {
|
||||
getOaProduct(id).then(res => {
|
||||
const d = res && res.data ? res.data : null
|
||||
if (!d) return
|
||||
if (d.productCode) this.detailEditForm.productCode = d.productCode
|
||||
if (!this.detailEditForm.unit && d.unit) this.detailEditForm.unit = d.unit
|
||||
}).catch(() => {})
|
||||
}
|
||||
},
|
||||
submitDetailEdit() {
|
||||
if (!this.detailEditForm || !this.detailEditForm.shippingId) {
|
||||
this.$modal.msgError('请先选择发货单')
|
||||
return
|
||||
}
|
||||
this.$refs.detailEditFormRef.validate(valid => {
|
||||
if (!valid) return
|
||||
this.detailEditLoading = true
|
||||
const payload = Object.assign({}, this.detailEditForm)
|
||||
const req = payload.detailId ? updateShippingOrderDetail(payload) : addShippingOrderDetail(payload)
|
||||
req.then(() => {
|
||||
this.$modal.msgSuccess('保存成功')
|
||||
this.detailEditOpen = false
|
||||
this.reloadCurrentDetail()
|
||||
}).finally(() => {
|
||||
this.detailEditLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
/** 打印预览:打开弹窗并加载发货单据明细产品数据 */
|
||||
printPreview(row) {
|
||||
const target = row || this.currentRow
|
||||
if (!target || !target.orderId) {
|
||||
@@ -759,10 +947,10 @@ export default {
|
||||
this.printLoading = true
|
||||
Promise.all([
|
||||
getOrder(target.orderId).then(res => (res && res.data) ? res.data : null).catch(() => null),
|
||||
listOrderDetail({ orderId: target.orderId, pageNum: 1, pageSize: 9999 }).then(res => (res && res.rows) ? res.rows : []).catch(() => []),
|
||||
listShippingOrderDetail({ shippingId: target.shippingId, pageNum: 1, pageSize: 9999 }).then(res => (res && res.rows) ? res.rows : []).catch(() => []),
|
||||
]).then(([orderInfo, rows]) => {
|
||||
this.printOrderInfo = orderInfo
|
||||
this.printProducts = rows.filter(r => String(r.productType || '').toLowerCase() === 'product')
|
||||
this.printProducts = rows
|
||||
const ids = this.printProducts.map(r => r.productId).filter(Boolean)
|
||||
return this.loadProductAdditions(ids)
|
||||
}).then(map => {
|
||||
@@ -1086,9 +1274,6 @@ export default {
|
||||
}).then(res => {
|
||||
this.orderSelectList = (res && res.rows) ? res.rows : []
|
||||
this.orderSelectTotal = res && res.total ? res.total : 0
|
||||
if (this.orderSelectList.length === 1) {
|
||||
this.confirmOrderBind(this.orderSelectList[0])
|
||||
}
|
||||
}).finally(() => {
|
||||
this.orderSelectLoading = false
|
||||
})
|
||||
@@ -1098,6 +1283,7 @@ export default {
|
||||
this.editForm.orderId = row.orderId
|
||||
this.editForm.orderCode = row.orderCode
|
||||
this.orderSelectOpen = false
|
||||
this.$modal.msgSuccess('订单已绑定')
|
||||
getOrder(row.orderId).then(res => {
|
||||
const order = res && res.data ? res.data : null
|
||||
if (!order) return
|
||||
@@ -1107,9 +1293,23 @@ export default {
|
||||
} else if (order.customerName) {
|
||||
this.editForm.receiverCompany = String(order.customerName)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.$modal.msgSuccess('订单已绑定')
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
orderStatusLabel(val) {
|
||||
const n = val == null || val === '' ? NaN : Number(val)
|
||||
if (n === 0) return '预订单'
|
||||
if (n === 1) return '进行中'
|
||||
if (n === 2) return '已完成'
|
||||
if (n === 3) return '已取消'
|
||||
return '—'
|
||||
},
|
||||
orderStatusTagType(val) {
|
||||
const n = val == null || val === '' ? NaN : Number(val)
|
||||
if (n === 0) return 'info'
|
||||
if (n === 1) return 'warning'
|
||||
if (n === 2) return 'success'
|
||||
if (n === 3) return 'danger'
|
||||
return 'info'
|
||||
},
|
||||
handleReceiverCustomerPicked(customerId) {
|
||||
if (customerId == null || String(customerId) === '') {
|
||||
|
||||
@@ -218,25 +218,30 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="物料出入库统计" v-model="flowOpen" width="980px" top="6vh" append-to-body>
|
||||
<el-form :inline="true" size="small" label-width="90px">
|
||||
<el-form-item label="物料">
|
||||
<RawSelector v-model="flowForm.itemId" :allowAdd="false" @change="onFlowMaterialChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时间范围">
|
||||
<el-date-picker
|
||||
v-model="flowForm.timeRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="flowLoading" @click="fetchFlow">查询</el-button>
|
||||
</el-form-item>
|
||||
<el-form :inline="true" size="small" label-width="70px">
|
||||
<div style="display:flex; flex-wrap:wrap; align-items:flex-end; gap:10px 16px;">
|
||||
<el-form-item label="物料" style="margin-bottom: 0;">
|
||||
<RawSelector v-model="flowForm.itemId" :allowAdd="false" @change="onFlowMaterialChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="厂家" style="margin-bottom: 0;">
|
||||
<el-input v-model="flowForm.factory" placeholder="请输入厂家" clearable :disabled="!!flowForm.itemId" style="width: 220px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时间" style="margin-bottom: 0;">
|
||||
<el-date-picker
|
||||
v-model="flowForm.timeRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-bottom: 0; margin-left: auto;">
|
||||
<el-button type="primary" :loading="flowLoading" @click="fetchFlow">查询</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div v-loading="flowLoading" style="margin-top: 10px;">
|
||||
@@ -245,7 +250,8 @@
|
||||
物料出入库统计
|
||||
</div>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<span>物料:</span><span>{{ flowItemName || '-' }}</span>
|
||||
<span v-if="flowForm.itemId">物料:</span><span v-if="flowForm.itemId">{{ flowItemName || '-' }}</span>
|
||||
<span v-else>厂家:</span><span v-else>{{ flowForm.factory || '-' }}</span>
|
||||
<span style="margin-left: 18px;">时间:</span>
|
||||
<span>{{ (flowForm.timeRange && flowForm.timeRange[0]) || '-' }}</span>
|
||||
<span> 至 </span>
|
||||
@@ -277,7 +283,7 @@
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="10" style="margin-bottom: 10px;">
|
||||
<el-row :gutter="10" justify="center" style="margin-bottom: 10px;">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="never">
|
||||
<div>净变动</div>
|
||||
@@ -351,6 +357,7 @@ export default {
|
||||
flowLoading: false,
|
||||
flowForm: {
|
||||
itemId: undefined,
|
||||
factory: undefined,
|
||||
timeRange: []
|
||||
},
|
||||
flowItemName: '',
|
||||
@@ -515,23 +522,34 @@ export default {
|
||||
},
|
||||
onFlowMaterialChange(material) {
|
||||
this.flowItemName = material && material.materialName ? material.materialName : ''
|
||||
if (this.flowForm.itemId) {
|
||||
this.flowForm.factory = undefined
|
||||
}
|
||||
},
|
||||
fetchFlow() {
|
||||
if (!this.flowForm.itemId) {
|
||||
this.$modal.msgError('请选择物料')
|
||||
return
|
||||
}
|
||||
if (!this.flowForm.timeRange || this.flowForm.timeRange.length !== 2) {
|
||||
this.$modal.msgError('请选择时间范围')
|
||||
return
|
||||
}
|
||||
const factory = this.flowForm.factory ? String(this.flowForm.factory).trim() : ''
|
||||
const itemId = this.flowForm.itemId
|
||||
if (!itemId && !factory) {
|
||||
this.$modal.msgError('请选择物料或填写厂家')
|
||||
return
|
||||
}
|
||||
this.flowLoading = true
|
||||
this.flowResult = null
|
||||
getMaterialFlow({
|
||||
itemId: this.flowForm.itemId,
|
||||
const params = {
|
||||
startTime: this.flowForm.timeRange[0],
|
||||
endTime: this.flowForm.timeRange[1]
|
||||
})
|
||||
}
|
||||
if (itemId) {
|
||||
params.itemId = itemId
|
||||
} else {
|
||||
params.factory = factory
|
||||
this.flowItemName = ''
|
||||
}
|
||||
getMaterialFlow(params)
|
||||
.then((res) => {
|
||||
this.flowResult = res.data || null
|
||||
})
|
||||
@@ -540,22 +558,29 @@ export default {
|
||||
})
|
||||
},
|
||||
exportFlow() {
|
||||
if (!this.flowForm.itemId) {
|
||||
this.$modal.msgError('请选择物料')
|
||||
return
|
||||
}
|
||||
if (!this.flowForm.timeRange || this.flowForm.timeRange.length !== 2) {
|
||||
this.$modal.msgError('请选择时间范围')
|
||||
return
|
||||
}
|
||||
const factory = this.flowForm.factory ? String(this.flowForm.factory).trim() : ''
|
||||
const itemId = this.flowForm.itemId
|
||||
if (!itemId && !factory) {
|
||||
this.$modal.msgError('请选择物料或填写厂家')
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
startTime: this.flowForm.timeRange[0],
|
||||
endTime: this.flowForm.timeRange[1]
|
||||
}
|
||||
if (itemId) {
|
||||
params.itemId = itemId
|
||||
} else {
|
||||
params.factory = factory
|
||||
}
|
||||
this.download(
|
||||
'/gear/stockIoOrder/materialFlow/export',
|
||||
{
|
||||
itemId: this.flowForm.itemId,
|
||||
startTime: this.flowForm.timeRange[0],
|
||||
endTime: this.flowForm.timeRange[1]
|
||||
},
|
||||
`material_flow_${this.flowForm.itemId}_${new Date().getTime()}.xlsx`
|
||||
params,
|
||||
itemId ? `material_flow_${itemId}_${new Date().getTime()}.xlsx` : `material_flow_factory_${factory}_${new Date().getTime()}.xlsx`
|
||||
)
|
||||
},
|
||||
submitEdit() {
|
||||
|
||||
@@ -199,25 +199,30 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="物料出入库统计" v-model="flowOpen" width="980px" top="6vh" append-to-body>
|
||||
<el-form :inline="true" size="small" label-width="90px">
|
||||
<el-form-item label="物料">
|
||||
<RawSelector v-model="flowForm.itemId" :allowAdd="false" @change="onFlowMaterialChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时间范围">
|
||||
<el-date-picker
|
||||
v-model="flowForm.timeRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="flowLoading" @click="fetchFlow">查询</el-button>
|
||||
</el-form-item>
|
||||
<el-form :inline="true" size="small" label-width="70px">
|
||||
<div style="display:flex; flex-wrap:wrap; align-items:flex-end; gap:10px 16px;">
|
||||
<el-form-item label="物料" style="margin-bottom: 0;">
|
||||
<RawSelector v-model="flowForm.itemId" :allowAdd="false" @change="onFlowMaterialChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="厂家" style="margin-bottom: 0;">
|
||||
<el-input v-model="flowForm.factory" placeholder="请输入厂家" clearable :disabled="!!flowForm.itemId" style="width: 220px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时间" style="margin-bottom: 0;">
|
||||
<el-date-picker
|
||||
v-model="flowForm.timeRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-bottom: 0; margin-left: auto;">
|
||||
<el-button type="primary" :loading="flowLoading" @click="fetchFlow">查询</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div v-loading="flowLoading" style="margin-top: 10px;">
|
||||
@@ -226,7 +231,8 @@
|
||||
物料出入库统计
|
||||
</div>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<span>物料:</span><span>{{ flowItemName || '-' }}</span>
|
||||
<span v-if="flowForm.itemId">物料:</span><span v-if="flowForm.itemId">{{ flowItemName || '-' }}</span>
|
||||
<span v-else>厂家:</span><span v-else>{{ flowForm.factory || '-' }}</span>
|
||||
<span style="margin-left: 18px;">时间:</span>
|
||||
<span>{{ (flowForm.timeRange && flowForm.timeRange[0]) || '-' }}</span>
|
||||
<span> 至 </span>
|
||||
@@ -258,7 +264,7 @@
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="10" style="margin-bottom: 10px;">
|
||||
<el-row :gutter="10" justify="center" style="margin-bottom: 10px;">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="never">
|
||||
<div>净变动</div>
|
||||
@@ -330,6 +336,7 @@ export default {
|
||||
flowLoading: false,
|
||||
flowForm: {
|
||||
itemId: undefined,
|
||||
factory: undefined,
|
||||
timeRange: []
|
||||
},
|
||||
flowItemName: '',
|
||||
@@ -473,23 +480,34 @@ export default {
|
||||
},
|
||||
onFlowMaterialChange(material) {
|
||||
this.flowItemName = material && material.materialName ? material.materialName : ''
|
||||
if (this.flowForm.itemId) {
|
||||
this.flowForm.factory = undefined
|
||||
}
|
||||
},
|
||||
fetchFlow() {
|
||||
if (!this.flowForm.itemId) {
|
||||
this.$modal.msgError('请选择物料')
|
||||
return
|
||||
}
|
||||
if (!this.flowForm.timeRange || this.flowForm.timeRange.length !== 2) {
|
||||
this.$modal.msgError('请选择时间范围')
|
||||
return
|
||||
}
|
||||
const factory = this.flowForm.factory ? String(this.flowForm.factory).trim() : ''
|
||||
const itemId = this.flowForm.itemId
|
||||
if (!itemId && !factory) {
|
||||
this.$modal.msgError('请选择物料或填写厂家')
|
||||
return
|
||||
}
|
||||
this.flowLoading = true
|
||||
this.flowResult = null
|
||||
getMaterialFlow({
|
||||
itemId: this.flowForm.itemId,
|
||||
const params = {
|
||||
startTime: this.flowForm.timeRange[0],
|
||||
endTime: this.flowForm.timeRange[1]
|
||||
})
|
||||
}
|
||||
if (itemId) {
|
||||
params.itemId = itemId
|
||||
} else {
|
||||
params.factory = factory
|
||||
this.flowItemName = ''
|
||||
}
|
||||
getMaterialFlow(params)
|
||||
.then((res) => {
|
||||
this.flowResult = res.data || null
|
||||
})
|
||||
@@ -498,22 +516,29 @@ export default {
|
||||
})
|
||||
},
|
||||
exportFlow() {
|
||||
if (!this.flowForm.itemId) {
|
||||
this.$modal.msgError('请选择物料')
|
||||
return
|
||||
}
|
||||
if (!this.flowForm.timeRange || this.flowForm.timeRange.length !== 2) {
|
||||
this.$modal.msgError('请选择时间范围')
|
||||
return
|
||||
}
|
||||
const factory = this.flowForm.factory ? String(this.flowForm.factory).trim() : ''
|
||||
const itemId = this.flowForm.itemId
|
||||
if (!itemId && !factory) {
|
||||
this.$modal.msgError('请选择物料或填写厂家')
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
startTime: this.flowForm.timeRange[0],
|
||||
endTime: this.flowForm.timeRange[1]
|
||||
}
|
||||
if (itemId) {
|
||||
params.itemId = itemId
|
||||
} else {
|
||||
params.factory = factory
|
||||
}
|
||||
this.download(
|
||||
'/gear/stockIoOrder/materialFlow/export',
|
||||
{
|
||||
itemId: this.flowForm.itemId,
|
||||
startTime: this.flowForm.timeRange[0],
|
||||
endTime: this.flowForm.timeRange[1]
|
||||
},
|
||||
`material_flow_${this.flowForm.itemId}_${new Date().getTime()}.xlsx`
|
||||
params,
|
||||
itemId ? `material_flow_${itemId}_${new Date().getTime()}.xlsx` : `material_flow_factory_${factory}_${new Date().getTime()}.xlsx`
|
||||
)
|
||||
},
|
||||
submitEdit() {
|
||||
|
||||
@@ -293,25 +293,30 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="物料出入库统计" v-model="flowOpen" width="980px" top="6vh" append-to-body>
|
||||
<el-form :inline="true" size="small" label-width="90px">
|
||||
<el-form-item label="物料">
|
||||
<RawSelector v-model="flowForm.itemId" :allowAdd="false" @change="onFlowMaterialChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时间范围">
|
||||
<el-date-picker
|
||||
v-model="flowForm.timeRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="flowLoading" @click="fetchFlow">查询</el-button>
|
||||
</el-form-item>
|
||||
<el-form :inline="true" size="small" label-width="70px">
|
||||
<div style="display:flex; flex-wrap:wrap; align-items:flex-end; gap:10px 16px;">
|
||||
<el-form-item label="物料" style="margin-bottom: 0;">
|
||||
<RawSelector v-model="flowForm.itemId" :allowAdd="false" @change="onFlowMaterialChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="厂家" style="margin-bottom: 0;">
|
||||
<el-input v-model="flowForm.factory" placeholder="请输入厂家" clearable :disabled="!!flowForm.itemId" style="width: 220px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="时间" style="margin-bottom: 0;">
|
||||
<el-date-picker
|
||||
v-model="flowForm.timeRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-bottom: 0; margin-left: auto;">
|
||||
<el-button type="primary" :loading="flowLoading" @click="fetchFlow">查询</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div v-loading="flowLoading" style="margin-top: 10px;">
|
||||
@@ -320,7 +325,8 @@
|
||||
物料出入库统计
|
||||
</div>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<span>物料:</span><span>{{ flowItemName || '-' }}</span>
|
||||
<span v-if="flowForm.itemId">物料:</span><span v-if="flowForm.itemId">{{ flowItemName || '-' }}</span>
|
||||
<span v-else>厂家:</span><span v-else>{{ flowForm.factory || '-' }}</span>
|
||||
<span style="margin-left: 18px;">时间:</span>
|
||||
<span>{{ (flowForm.timeRange && flowForm.timeRange[0]) || '-' }}</span>
|
||||
<span> 至 </span>
|
||||
@@ -352,7 +358,7 @@
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="10" style="margin-bottom: 10px;">
|
||||
<el-row :gutter="10" justify="center" style="margin-bottom: 10px;">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="never">
|
||||
<div>净变动</div>
|
||||
@@ -456,6 +462,7 @@ export default {
|
||||
flowLoading: false,
|
||||
flowForm: {
|
||||
itemId: undefined,
|
||||
factory: undefined,
|
||||
timeRange: []
|
||||
},
|
||||
flowItemName: '',
|
||||
@@ -670,23 +677,34 @@ export default {
|
||||
},
|
||||
onFlowMaterialChange(material) {
|
||||
this.flowItemName = material && material.materialName ? material.materialName : ''
|
||||
if (this.flowForm.itemId) {
|
||||
this.flowForm.factory = undefined
|
||||
}
|
||||
},
|
||||
fetchFlow() {
|
||||
if (!this.flowForm.itemId) {
|
||||
this.$modal.msgError('请选择物料')
|
||||
return
|
||||
}
|
||||
if (!this.flowForm.timeRange || this.flowForm.timeRange.length !== 2) {
|
||||
this.$modal.msgError('请选择时间范围')
|
||||
return
|
||||
}
|
||||
const factory = this.flowForm.factory ? String(this.flowForm.factory).trim() : ''
|
||||
const itemId = this.flowForm.itemId
|
||||
if (!itemId && !factory) {
|
||||
this.$modal.msgError('请选择物料或填写厂家')
|
||||
return
|
||||
}
|
||||
this.flowLoading = true
|
||||
this.flowResult = null
|
||||
getMaterialFlow({
|
||||
itemId: this.flowForm.itemId,
|
||||
const params = {
|
||||
startTime: this.flowForm.timeRange[0],
|
||||
endTime: this.flowForm.timeRange[1]
|
||||
})
|
||||
}
|
||||
if (itemId) {
|
||||
params.itemId = itemId
|
||||
} else {
|
||||
params.factory = factory
|
||||
this.flowItemName = ''
|
||||
}
|
||||
getMaterialFlow(params)
|
||||
.then((res) => {
|
||||
this.flowResult = res.data || null
|
||||
})
|
||||
@@ -695,22 +713,29 @@ export default {
|
||||
})
|
||||
},
|
||||
exportFlow() {
|
||||
if (!this.flowForm.itemId) {
|
||||
this.$modal.msgError('请选择物料')
|
||||
return
|
||||
}
|
||||
if (!this.flowForm.timeRange || this.flowForm.timeRange.length !== 2) {
|
||||
this.$modal.msgError('请选择时间范围')
|
||||
return
|
||||
}
|
||||
const factory = this.flowForm.factory ? String(this.flowForm.factory).trim() : ''
|
||||
const itemId = this.flowForm.itemId
|
||||
if (!itemId && !factory) {
|
||||
this.$modal.msgError('请选择物料或填写厂家')
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
startTime: this.flowForm.timeRange[0],
|
||||
endTime: this.flowForm.timeRange[1]
|
||||
}
|
||||
if (itemId) {
|
||||
params.itemId = itemId
|
||||
} else {
|
||||
params.factory = factory
|
||||
}
|
||||
this.download(
|
||||
'/gear/stockIoOrder/materialFlow/export',
|
||||
{
|
||||
itemId: this.flowForm.itemId,
|
||||
startTime: this.flowForm.timeRange[0],
|
||||
endTime: this.flowForm.timeRange[1]
|
||||
},
|
||||
`material_flow_${this.flowForm.itemId}_${new Date().getTime()}.xlsx`
|
||||
params,
|
||||
itemId ? `material_flow_${itemId}_${new Date().getTime()}.xlsx` : `material_flow_factory_${factory}_${new Date().getTime()}.xlsx`
|
||||
)
|
||||
},
|
||||
showDetail(row) {
|
||||
|
||||
@@ -42,6 +42,29 @@ CREATE TABLE gear_shipping_order (
|
||||
KEY idx_ship_time (ship_time)
|
||||
) ENGINE=InnoDB COMMENT='发货单据表';
|
||||
|
||||
DROP TABLE IF EXISTS gear_shipping_order_detail;
|
||||
CREATE TABLE gear_shipping_order_detail (
|
||||
detail_id bigint(20) NOT NULL COMMENT '发货单据明细ID',
|
||||
shipping_id bigint(20) NOT NULL COMMENT '发货单据ID gear_shipping_order.shipping_id',
|
||||
product_id bigint(20) NOT NULL COMMENT '产品ID',
|
||||
product_code varchar(64) DEFAULT '' COMMENT '产品编号快照',
|
||||
product_name varchar(255) DEFAULT '' COMMENT '产品名称快照',
|
||||
spec varchar(255) DEFAULT '' COMMENT '规格快照',
|
||||
model varchar(255) DEFAULT '' COMMENT '型号快照',
|
||||
quantity decimal(18,4) NOT NULL DEFAULT 0 COMMENT '数量',
|
||||
unit varchar(32) DEFAULT '' COMMENT '单位',
|
||||
remark varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
sort int DEFAULT 0 COMMENT '排序',
|
||||
del_flag char(1) NOT NULL DEFAULT '0' COMMENT '删除标志(0存在 2删除)',
|
||||
create_by varchar(64) DEFAULT '' COMMENT '创建者',
|
||||
create_time datetime COMMENT '创建时间',
|
||||
update_by varchar(64) DEFAULT '' COMMENT '更新者',
|
||||
update_time datetime COMMENT '更新时间',
|
||||
PRIMARY KEY (detail_id),
|
||||
KEY idx_shipping_id (shipping_id),
|
||||
KEY idx_product_id (product_id)
|
||||
) ENGINE=InnoDB COMMENT='发货单据明细表';
|
||||
|
||||
-- ================================
|
||||
-- 发货计划(独立表)
|
||||
-- 目标:用于“发货单据”左侧计划列表,计划下可关联多张发货单据(gear_shipping_order.plan_id)
|
||||
|
||||
Reference in New Issue
Block a user