feat(biz): 新增成品入出库功能并优化销售发货界面
This commit is contained in:
parent
14b19dc223
commit
70b9038a1e
@ -66,9 +66,9 @@ public class SaleDeliveryController {
|
|||||||
@Operation(summary = "获得销售出库单主")
|
@Operation(summary = "获得销售出库单主")
|
||||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||||
@PreAuthorize("@ss.hasPermission('tso:sale-delivery:query')")
|
@PreAuthorize("@ss.hasPermission('tso:sale-delivery:query')")
|
||||||
public CommonResult<SaleDeliveryRespVO> getSaleDelivery(@RequestParam("id") Integer id) {
|
public CommonResult<SaleDeliverySaveReqVO> getSaleDelivery(@RequestParam("id") Integer id) {
|
||||||
SaleDeliveryDO saleDelivery = saleDeliveryService.getSaleDelivery(id);
|
SaleDeliverySaveReqVO saleDelivery = saleDeliveryService.getSaleDeliveryWithDetails(id);
|
||||||
return success(BeanUtils.toBean(saleDelivery, SaleDeliveryRespVO.class));
|
return success(BeanUtils.toBean(saleDelivery, SaleDeliverySaveReqVO.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/page")
|
@GetMapping("/page")
|
||||||
|
|||||||
@ -2,12 +2,15 @@ package com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledelivery.vo;
|
|||||||
|
|
||||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
import com.alibaba.excel.annotation.ExcelProperty;
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledeliverydetail.vo.SaleDeliveryDetailRespVO;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 销售出库单主 Response VO")
|
@Schema(description = "管理后台 - 销售出库单主 Response VO")
|
||||||
@Data
|
@Data
|
||||||
@ -28,6 +31,7 @@ public class SaleDeliveryRespVO {
|
|||||||
|
|
||||||
@Schema(description = "单据日期")
|
@Schema(description = "单据日期")
|
||||||
@ExcelProperty("单据日期")
|
@ExcelProperty("单据日期")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||||
private LocalDate deliveryDate;
|
private LocalDate deliveryDate;
|
||||||
|
|
||||||
@Schema(description = "客户id", example = "11842")
|
@Schema(description = "客户id", example = "11842")
|
||||||
@ -118,4 +122,7 @@ public class SaleDeliveryRespVO {
|
|||||||
@ExcelProperty("发货数量")
|
@ExcelProperty("发货数量")
|
||||||
private BigDecimal deliveriedQty;
|
private BigDecimal deliveriedQty;
|
||||||
|
|
||||||
|
@Schema(description = "出库明细列表")
|
||||||
|
private List<SaleDeliveryDetailRespVO> detailList;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
package com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledelivery.vo;
|
package com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledelivery.vo;
|
||||||
|
|
||||||
|
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledeliverydetail.vo.SaleDeliveryDetailSaveReqVO;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 销售出库单主新增/修改 Request VO")
|
@Schema(description = "管理后台 - 销售出库单主新增/修改 Request VO")
|
||||||
@Data
|
@Data
|
||||||
@ -85,4 +87,6 @@ public class SaleDeliverySaveReqVO {
|
|||||||
@Schema(description = "发货数量")
|
@Schema(description = "发货数量")
|
||||||
private BigDecimal deliveriedQty;
|
private BigDecimal deliveriedQty;
|
||||||
|
|
||||||
|
private List<SaleDeliveryDetailSaveReqVO> detailList;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,4 +45,11 @@ public interface SaleDeliveryMapper extends BaseMapperX<SaleDeliveryDO> {
|
|||||||
.orderByDesc(SaleDeliveryDO::getId));
|
.orderByDesc(SaleDeliveryDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default String selectMaxSaleDeliveryNo() {
|
||||||
|
SaleDeliveryDO saleDelivery = selectOne(new LambdaQueryWrapperX<SaleDeliveryDO>()
|
||||||
|
.orderByDesc(SaleDeliveryDO::getSaleDeliveryNo)
|
||||||
|
.last("LIMIT 1"));
|
||||||
|
return saleDelivery != null ? saleDelivery.getSaleDeliveryNo() : null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
package com.ningxia.yunxi.chemmes.module.biz.dal.mysql.saledeliverydetail;
|
package com.ningxia.yunxi.chemmes.module.biz.dal.mysql.saledeliverydetail;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||||
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
|
import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
|
||||||
import com.ningxia.yunxi.chemmes.framework.mybatis.core.mapper.BaseMapperX;
|
import com.ningxia.yunxi.chemmes.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
import com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledeliverydetail.vo.SaleDeliveryDetailPageReqVO;
|
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledeliverydetail.vo.SaleDeliveryDetailPageReqVO;
|
||||||
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.saledeliverydetail.SaleDeliveryDetailDO;
|
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.saledeliverydetail.SaleDeliveryDetailDO;
|
||||||
|
import org.apache.ibatis.annotations.Delete;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 销售出库单子 Mapper
|
* 销售出库单子 Mapper
|
||||||
@ -15,6 +20,15 @@ import org.apache.ibatis.annotations.Mapper;
|
|||||||
@Mapper
|
@Mapper
|
||||||
public interface SaleDeliveryDetailMapper extends BaseMapperX<SaleDeliveryDetailDO> {
|
public interface SaleDeliveryDetailMapper extends BaseMapperX<SaleDeliveryDetailDO> {
|
||||||
|
|
||||||
|
default List<SaleDeliveryDetailDO> selectBySaleDeliveryId(Integer saleDeliveryId) {
|
||||||
|
return selectList(SaleDeliveryDetailDO::getSaleDeliveryId, saleDeliveryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete("DELETE FROM tso_sale_delivery_detail WHERE sale_delivery_id = #{saleDeliveryId}")
|
||||||
|
@InterceptorIgnore(tenantLine = "true")
|
||||||
|
int physicalDeleteBySaleDeliveryId(@Param("saleDeliveryId") Integer saleDeliveryId);
|
||||||
|
|
||||||
|
|
||||||
default PageResult<SaleDeliveryDetailDO> selectPage(SaleDeliveryDetailPageReqVO reqVO) {
|
default PageResult<SaleDeliveryDetailDO> selectPage(SaleDeliveryDetailPageReqVO reqVO) {
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<SaleDeliveryDetailDO>()
|
return selectPage(reqVO, new LambdaQueryWrapperX<SaleDeliveryDetailDO>()
|
||||||
.betweenIfPresent(SaleDeliveryDetailDO::getCreateTime, reqVO.getCreateTime())
|
.betweenIfPresent(SaleDeliveryDetailDO::getCreateTime, reqVO.getCreateTime())
|
||||||
|
|||||||
@ -64,6 +64,9 @@ public class OrderServiceImpl implements OrderService {
|
|||||||
if ("9".equals(order.getOrdStatus())) {
|
if ("9".equals(order.getOrdStatus())) {
|
||||||
updateReqVO.setOrdStatus("1");
|
updateReqVO.setOrdStatus("1");
|
||||||
}
|
}
|
||||||
|
if ("2".equals(order.getOrdStatus())) {
|
||||||
|
updateReqVO.setOrdStatus("3");
|
||||||
|
}
|
||||||
|
|
||||||
// 更新主表
|
// 更新主表
|
||||||
OrderDO updateObj = BeanUtils.toBean(updateReqVO, OrderDO.class);
|
OrderDO updateObj = BeanUtils.toBean(updateReqVO, OrderDO.class);
|
||||||
|
|||||||
@ -44,6 +44,14 @@ public interface SaleDeliveryService {
|
|||||||
*/
|
*/
|
||||||
SaleDeliveryDO getSaleDelivery(Integer id);
|
SaleDeliveryDO getSaleDelivery(Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得销售出库单详情(包含明细)
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @return 销售出库单详情
|
||||||
|
*/
|
||||||
|
SaleDeliverySaveReqVO getSaleDeliveryWithDetails(Integer id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得销售出库单主分页
|
* 获得销售出库单主分页
|
||||||
*
|
*
|
||||||
|
|||||||
@ -4,12 +4,19 @@ import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult;
|
|||||||
import com.ningxia.yunxi.chemmes.framework.common.util.object.BeanUtils;
|
import com.ningxia.yunxi.chemmes.framework.common.util.object.BeanUtils;
|
||||||
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledelivery.vo.SaleDeliveryPageReqVO;
|
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledelivery.vo.SaleDeliveryPageReqVO;
|
||||||
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledelivery.vo.SaleDeliverySaveReqVO;
|
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledelivery.vo.SaleDeliverySaveReqVO;
|
||||||
|
import com.ningxia.yunxi.chemmes.module.biz.controller.admin.saledeliverydetail.vo.SaleDeliveryDetailSaveReqVO;
|
||||||
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.saledelivery.SaleDeliveryDO;
|
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.saledelivery.SaleDeliveryDO;
|
||||||
|
import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.saledeliverydetail.SaleDeliveryDetailDO;
|
||||||
import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.saledelivery.SaleDeliveryMapper;
|
import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.saledelivery.SaleDeliveryMapper;
|
||||||
|
import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.saledeliverydetail.SaleDeliveryDetailMapper;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static com.ningxia.yunxi.chemmes.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static com.ningxia.yunxi.chemmes.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
|
||||||
@ -25,30 +32,78 @@ public class SaleDeliveryServiceImpl implements SaleDeliveryService {
|
|||||||
@Resource
|
@Resource
|
||||||
private SaleDeliveryMapper saleDeliveryMapper;
|
private SaleDeliveryMapper saleDeliveryMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SaleDeliveryDetailMapper saleDeliveryDetailMapper;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public Integer createSaleDelivery(SaleDeliverySaveReqVO createReqVO) {
|
public Integer createSaleDelivery(SaleDeliverySaveReqVO createReqVO) {
|
||||||
|
String saleDeliveryNo = generateSaleDeliveryNo();
|
||||||
|
createReqVO.setSaleDeliveryNo(saleDeliveryNo);
|
||||||
// 插入
|
// 插入
|
||||||
SaleDeliveryDO saleDelivery = BeanUtils.toBean(createReqVO, SaleDeliveryDO.class);
|
SaleDeliveryDO saleDelivery = BeanUtils.toBean(createReqVO, SaleDeliveryDO.class);
|
||||||
saleDeliveryMapper.insert(saleDelivery);
|
saleDeliveryMapper.insert(saleDelivery);
|
||||||
|
|
||||||
|
createSaleDeliveryDetailList(saleDelivery.getId(), createReqVO.getDetailList());
|
||||||
|
|
||||||
// 返回
|
// 返回
|
||||||
return saleDelivery.getId();
|
return saleDelivery.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void updateSaleDelivery(SaleDeliverySaveReqVO updateReqVO) {
|
public void updateSaleDelivery(SaleDeliverySaveReqVO updateReqVO) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
validateSaleDeliveryExists(updateReqVO.getId());
|
validateSaleDeliveryExists(updateReqVO.getId());
|
||||||
// 更新
|
// 更新
|
||||||
SaleDeliveryDO updateObj = BeanUtils.toBean(updateReqVO, SaleDeliveryDO.class);
|
SaleDeliveryDO updateObj = BeanUtils.toBean(updateReqVO, SaleDeliveryDO.class);
|
||||||
saleDeliveryMapper.updateById(updateObj);
|
saleDeliveryMapper.updateById(updateObj);
|
||||||
|
updateSaleDeliveryDetailList(updateReqVO.getId(), updateReqVO.getDetailList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建销售出库单子表列表
|
||||||
|
*/
|
||||||
|
private void createSaleDeliveryDetailList(Integer saleDeliveryId, List<SaleDeliveryDetailSaveReqVO> list) {
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<SaleDeliveryDetailDO> saleDeliveryDetails = BeanUtils.toBean(list, SaleDeliveryDetailDO.class);
|
||||||
|
saleDeliveryDetails.forEach(detail ->
|
||||||
|
detail.setSaleDeliveryId(saleDeliveryId)
|
||||||
|
.setId(null)
|
||||||
|
);
|
||||||
|
saleDeliveryDetailMapper.insertBatch(saleDeliveryDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新销售出库单子表列表
|
||||||
|
*/
|
||||||
|
private void updateSaleDeliveryDetailList(Integer saleDeliveryId, List<SaleDeliveryDetailSaveReqVO> list) {
|
||||||
|
// 先删除旧的子表记录
|
||||||
|
deleteSaleDeliveryDetailBySaleDeliveryId(saleDeliveryId);
|
||||||
|
// 再插入新的子表记录
|
||||||
|
createSaleDeliveryDetailList(saleDeliveryId, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据销售出库单ID删除子表记录
|
||||||
|
*/
|
||||||
|
private void deleteSaleDeliveryDetailBySaleDeliveryId(Integer saleDeliveryId) {
|
||||||
|
saleDeliveryDetailMapper.physicalDeleteBySaleDeliveryId(saleDeliveryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void deleteSaleDelivery(Integer id) {
|
public void deleteSaleDelivery(Integer id) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
validateSaleDeliveryExists(id);
|
validateSaleDeliveryExists(id);
|
||||||
// 删除
|
// 删除
|
||||||
saleDeliveryMapper.deleteById(id);
|
saleDeliveryMapper.deleteById(id);
|
||||||
|
// 删除子表 物理删除
|
||||||
|
saleDeliveryDetailMapper.physicalDeleteBySaleDeliveryId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateSaleDeliveryExists(Integer id) {
|
private void validateSaleDeliveryExists(Integer id) {
|
||||||
@ -62,9 +117,49 @@ public class SaleDeliveryServiceImpl implements SaleDeliveryService {
|
|||||||
return saleDeliveryMapper.selectById(id);
|
return saleDeliveryMapper.selectById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SaleDeliverySaveReqVO getSaleDeliveryWithDetails(Integer id) {
|
||||||
|
// 查询主表
|
||||||
|
SaleDeliveryDO saleDelivery = saleDeliveryMapper.selectById(id);
|
||||||
|
if (saleDelivery == null) {
|
||||||
|
throw exception("销售出库单主不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为主表VO
|
||||||
|
SaleDeliverySaveReqVO saleDeliveryVO = BeanUtils.toBean(saleDelivery, SaleDeliverySaveReqVO.class);
|
||||||
|
|
||||||
|
// 查询子表
|
||||||
|
List<SaleDeliveryDetailDO> detailList = saleDeliveryDetailMapper.selectBySaleDeliveryId(id);
|
||||||
|
// 转换为子表VO列表
|
||||||
|
saleDeliveryVO.setDetailList(BeanUtils.toBean(detailList, SaleDeliveryDetailSaveReqVO.class));
|
||||||
|
|
||||||
|
return saleDeliveryVO;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<SaleDeliveryDO> getSaleDeliveryPage(SaleDeliveryPageReqVO pageReqVO) {
|
public PageResult<SaleDeliveryDO> getSaleDeliveryPage(SaleDeliveryPageReqVO pageReqVO) {
|
||||||
return saleDeliveryMapper.selectPage(pageReqVO);
|
return saleDeliveryMapper.selectPage(pageReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成销售出库单号
|
||||||
|
* 规则:SC + 年份(4位) + 月份(2位) + 流水号(3位)
|
||||||
|
*/
|
||||||
|
private String generateSaleDeliveryNo() {
|
||||||
|
String ym = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
|
||||||
|
|
||||||
|
String maxSaleDeliveryNo = saleDeliveryMapper.selectMaxSaleDeliveryNo();
|
||||||
|
|
||||||
|
if (maxSaleDeliveryNo == null || maxSaleDeliveryNo.length() < 9
|
||||||
|
|| !maxSaleDeliveryNo.substring(2, 8).equals(ym)) {
|
||||||
|
return "SC" + ym + "001";
|
||||||
|
} else {
|
||||||
|
String prefix = maxSaleDeliveryNo.substring(0, 8);
|
||||||
|
int sequence = Integer.parseInt(maxSaleDeliveryNo.substring(8));
|
||||||
|
sequence++;
|
||||||
|
return prefix + String.format("%03d", sequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
71
mes-ui/mes-ui-admin-vue3/.gitignore
vendored
71
mes-ui/mes-ui-admin-vue3/.gitignore
vendored
@ -1,12 +1,61 @@
|
|||||||
node_modules
|
# 测试文件夹
|
||||||
|
tests/
|
||||||
|
test-results/
|
||||||
|
test-reports/
|
||||||
|
html-report/
|
||||||
|
|
||||||
|
# 截图文件
|
||||||
|
*.png
|
||||||
|
*.jpg
|
||||||
|
*.jpeg
|
||||||
|
|
||||||
|
# 测试日志
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# pytest 缓存
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# Node.js 依赖
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# 构建输出
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.zip
|
||||||
|
|
||||||
|
# 编辑器配置
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# 操作系统文件
|
||||||
.DS_Store
|
.DS_Store
|
||||||
dist
|
Thumbs.db
|
||||||
dist-ssr
|
|
||||||
*.local
|
# 环境变量
|
||||||
/dist*
|
.env
|
||||||
*-lock.*
|
.env.local
|
||||||
pnpm-debug
|
.env.*.local
|
||||||
auto-*.d.ts
|
|
||||||
.idea
|
# 临时文件
|
||||||
.history
|
*.tmp
|
||||||
/.trae/
|
*.temp
|
||||||
|
|
||||||
|
# Python 字节码
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
|
||||||
|
# 虚拟环境
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
.venv/
|
||||||
|
/pytest.ini
|
||||||
|
/pnpm-lock.yaml
|
||||||
|
|||||||
48
mes-ui/mes-ui-admin-vue3/src/api/biz/prostorage/index.ts
Normal file
48
mes-ui/mes-ui-admin-vue3/src/api/biz/prostorage/index.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface ProStorageVO {
|
||||||
|
id: number
|
||||||
|
billNo: string
|
||||||
|
operatorType: boolean
|
||||||
|
businessType: number
|
||||||
|
remark: string
|
||||||
|
status: boolean
|
||||||
|
billDate: localdate
|
||||||
|
operatorId: number
|
||||||
|
operatorName: string
|
||||||
|
relarionNo: string
|
||||||
|
relarionId: number
|
||||||
|
billType: string
|
||||||
|
sourceNo: string
|
||||||
|
sourceId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询成品入/出库分页
|
||||||
|
export const getProStoragePage = async (params) => {
|
||||||
|
return await request.get({ url: `/twm/pro-storage/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询成品入/出库详情
|
||||||
|
export const getProStorage = async (id: number) => {
|
||||||
|
return await request.get({ url: `/twm/pro-storage/get?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增成品入/出库
|
||||||
|
export const createProStorage = async (data: ProStorageVO) => {
|
||||||
|
return await request.post({ url: `/twm/pro-storage/create`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改成品入/出库
|
||||||
|
export const updateProStorage = async (data: ProStorageVO) => {
|
||||||
|
return await request.put({ url: `/twm/pro-storage/update`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除成品入/出库
|
||||||
|
export const deleteProStorage = async (id: number) => {
|
||||||
|
return await request.delete({ url: `/twm/pro-storage/delete?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出成品入/出库 Excel
|
||||||
|
export const exportProStorage = async (params) => {
|
||||||
|
return await request.download({ url: `/twm/pro-storage/export-excel`, params })
|
||||||
|
}
|
||||||
56
mes-ui/mes-ui-admin-vue3/src/api/biz/prostoragelog/index.ts
Normal file
56
mes-ui/mes-ui-admin-vue3/src/api/biz/prostoragelog/index.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface ProStorageLogVO {
|
||||||
|
id: number
|
||||||
|
stockId: number
|
||||||
|
description: string
|
||||||
|
status: boolean
|
||||||
|
storeHouseId: number
|
||||||
|
storeAreaId: number
|
||||||
|
storeHouseCd: string
|
||||||
|
storeHouseName: string
|
||||||
|
storeAreCd: string
|
||||||
|
storeAreaName: string
|
||||||
|
materialId: number
|
||||||
|
matName: string
|
||||||
|
matCode: string
|
||||||
|
spec: string
|
||||||
|
unit: string
|
||||||
|
lotNo: string
|
||||||
|
operatorQty: number
|
||||||
|
inOutType: boolean
|
||||||
|
billType: number
|
||||||
|
storageAft: number
|
||||||
|
storageBef: number
|
||||||
|
stockItemId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询成品入/出库日志分页
|
||||||
|
export const getProStorageLogPage = async (params) => {
|
||||||
|
return await request.get({ url: `/twm/pro-storage-log/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询成品入/出库日志详情
|
||||||
|
export const getProStorageLog = async (id: number) => {
|
||||||
|
return await request.get({ url: `/twm/pro-storage-log/get?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增成品入/出库日志
|
||||||
|
export const createProStorageLog = async (data: ProStorageLogVO) => {
|
||||||
|
return await request.post({ url: `/twm/pro-storage-log/create`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改成品入/出库日志
|
||||||
|
export const updateProStorageLog = async (data: ProStorageLogVO) => {
|
||||||
|
return await request.put({ url: `/twm/pro-storage-log/update`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除成品入/出库日志
|
||||||
|
export const deleteProStorageLog = async (id: number) => {
|
||||||
|
return await request.delete({ url: `/twm/pro-storage-log/delete?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出成品入/出库日志 Excel
|
||||||
|
export const exportProStorageLog = async (params) => {
|
||||||
|
return await request.download({ url: `/twm/pro-storage-log/export-excel`, params })
|
||||||
|
}
|
||||||
56
mes-ui/mes-ui-admin-vue3/src/api/biz/prostoragemat/index.ts
Normal file
56
mes-ui/mes-ui-admin-vue3/src/api/biz/prostoragemat/index.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface ProStorageMatVO {
|
||||||
|
id: number
|
||||||
|
stockId: number
|
||||||
|
description: string
|
||||||
|
storeHouseId: number
|
||||||
|
storeAreaId: number
|
||||||
|
storeHouseCd: string
|
||||||
|
storeHouseName: string
|
||||||
|
storeAreCd: string
|
||||||
|
storeAreaName: string
|
||||||
|
materialId: number
|
||||||
|
matName: string
|
||||||
|
matCode: string
|
||||||
|
spec: string
|
||||||
|
unit: string
|
||||||
|
lotNo: string
|
||||||
|
operatorQty: number
|
||||||
|
sourceId: number
|
||||||
|
relarionId: number
|
||||||
|
bagSpec: number
|
||||||
|
bagQty: number
|
||||||
|
planId: number
|
||||||
|
proNo: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询成品入/出库子分页
|
||||||
|
export const getProStorageMatPage = async (params) => {
|
||||||
|
return await request.get({ url: `/twm/pro-storage-mat/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询成品入/出库子详情
|
||||||
|
export const getProStorageMat = async (id: number) => {
|
||||||
|
return await request.get({ url: `/twm/pro-storage-mat/get?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增成品入/出库子
|
||||||
|
export const createProStorageMat = async (data: ProStorageMatVO) => {
|
||||||
|
return await request.post({ url: `/twm/pro-storage-mat/create`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改成品入/出库子
|
||||||
|
export const updateProStorageMat = async (data: ProStorageMatVO) => {
|
||||||
|
return await request.put({ url: `/twm/pro-storage-mat/update`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除成品入/出库子
|
||||||
|
export const deleteProStorageMat = async (id: number) => {
|
||||||
|
return await request.delete({ url: `/twm/pro-storage-mat/delete?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出成品入/出库子 Excel
|
||||||
|
export const exportProStorageMat = async (params) => {
|
||||||
|
return await request.download({ url: `/twm/pro-storage-mat/export-excel`, params })
|
||||||
|
}
|
||||||
331
mes-ui/mes-ui-admin-vue3/src/views/biz/components/MoneyInput.vue
Normal file
331
mes-ui/mes-ui-admin-vue3/src/views/biz/components/MoneyInput.vue
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
<template>
|
||||||
|
<!--
|
||||||
|
通用金额输入组件
|
||||||
|
支持小数位数限制、负数控制、前后缀显示等功能
|
||||||
|
-->
|
||||||
|
<el-input
|
||||||
|
v-model="displayValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:disabled="disabled"
|
||||||
|
:readonly="readonly"
|
||||||
|
:clearable="clearable"
|
||||||
|
@input="handleInput"
|
||||||
|
@blur="handleBlur"
|
||||||
|
@focus="handleFocus"
|
||||||
|
>
|
||||||
|
<!-- 前缀模板:根据 showPrefix 控制显示 -->
|
||||||
|
<template v-if="showPrefix" #prefix>{{ prefix }}</template>
|
||||||
|
<!-- 后缀模板:根据 showSuffix 控制显示 -->
|
||||||
|
<template v-if="showSuffix" #suffix>{{ suffix }}</template>
|
||||||
|
</el-input>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
/**
|
||||||
|
* 通用金额输入组件 MoneyInput
|
||||||
|
*
|
||||||
|
* 功能特性:
|
||||||
|
* - 自动限制小数位数
|
||||||
|
* - 可配置是否允许负数输入
|
||||||
|
* - 支持自定义前后缀符号(如 ¥、$、% 等)
|
||||||
|
* - 自动格式化显示,失焦时自动补全小数位
|
||||||
|
* - 输入时自动过滤非法字符
|
||||||
|
*
|
||||||
|
* 使用示例:
|
||||||
|
* ```vue
|
||||||
|
* <!-- 基本用法:人民币金额,2位小数 -->
|
||||||
|
* <MoneyInput v-model="amount" />
|
||||||
|
*
|
||||||
|
* <!-- 订单数量:整数,不允许负数,无符号 -->
|
||||||
|
* <MoneyInput
|
||||||
|
* v-model="quantity"
|
||||||
|
* :decimal-places="0"
|
||||||
|
* :allow-negative="false"
|
||||||
|
* :show-prefix="false"
|
||||||
|
* />
|
||||||
|
*
|
||||||
|
* <!-- 美元金额 -->
|
||||||
|
* <MoneyInput
|
||||||
|
* v-model="usdAmount"
|
||||||
|
* prefix="$"
|
||||||
|
* :decimal-places="2"
|
||||||
|
* />
|
||||||
|
*
|
||||||
|
* <!-- 百分比显示:2位小数,显示%后缀 -->
|
||||||
|
* <MoneyInput
|
||||||
|
* v-model="rate"
|
||||||
|
* :decimal-places="2"
|
||||||
|
* suffix="%"
|
||||||
|
* :show-suffix="true"
|
||||||
|
* :show-prefix="false"
|
||||||
|
* />
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Props 定义
|
||||||
|
*/
|
||||||
|
interface Props {
|
||||||
|
/**
|
||||||
|
* 绑定值(双向绑定)
|
||||||
|
*/
|
||||||
|
modelValue?: number | string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输入框占位文本
|
||||||
|
* @default '请输入'
|
||||||
|
*/
|
||||||
|
placeholder?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否禁用输入框
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
disabled?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否只读
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
readonly?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否显示清除按钮
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
clearable?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小数位数
|
||||||
|
* @default 2
|
||||||
|
*/
|
||||||
|
decimalPlaces?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许输入负数
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
allowNegative?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 前缀符号(如 ¥、$)
|
||||||
|
* @default '¥'
|
||||||
|
*/
|
||||||
|
prefix?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后缀符号(如 %、元)
|
||||||
|
* @default ''
|
||||||
|
*/
|
||||||
|
suffix?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否显示前缀
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
showPrefix?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否显示后缀
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
showSuffix?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Props 默认值配置
|
||||||
|
*/
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
modelValue: undefined,
|
||||||
|
placeholder: '请输入',
|
||||||
|
disabled: false,
|
||||||
|
readonly: false,
|
||||||
|
clearable: true,
|
||||||
|
decimalPlaces: 2,
|
||||||
|
allowNegative: true,
|
||||||
|
prefix: '¥',
|
||||||
|
suffix: '',
|
||||||
|
showPrefix: true,
|
||||||
|
showSuffix: false
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件定义
|
||||||
|
*/
|
||||||
|
const emit = defineEmits<{
|
||||||
|
/**
|
||||||
|
* 值变化事件(双向绑定)
|
||||||
|
* @param value - 新的值(number | string | undefined)
|
||||||
|
*/
|
||||||
|
'update:modelValue': [value: number | string | undefined]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 值改变事件(失焦时触发)
|
||||||
|
* @param value - 新的值(number | string | undefined)
|
||||||
|
*/
|
||||||
|
'change': [value: number | string | undefined]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示值(格式化后的字符串)
|
||||||
|
*/
|
||||||
|
const displayValue = ref<string>('')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化显示值
|
||||||
|
* 将 modelValue 转换为格式化后的字符串
|
||||||
|
*/
|
||||||
|
const initDisplayValue = () => {
|
||||||
|
if (props.modelValue !== undefined && props.modelValue !== null && props.modelValue !== '') {
|
||||||
|
const num = Number(props.modelValue)
|
||||||
|
if (!isNaN(num)) {
|
||||||
|
displayValue.value = num.toFixed(props.decimalPlaces)
|
||||||
|
} else {
|
||||||
|
displayValue.value = ''
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
displayValue.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化显示值
|
||||||
|
initDisplayValue()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听 modelValue 变化,同步更新显示值
|
||||||
|
*/
|
||||||
|
watch(() => props.modelValue, () => {
|
||||||
|
initDisplayValue()
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理输入事件
|
||||||
|
* 过滤非法字符,限制小数位数
|
||||||
|
*/
|
||||||
|
const handleInput = (value: string) => {
|
||||||
|
let inputValue = value
|
||||||
|
|
||||||
|
// 处理负数:如果允许负数,保留一个负号在开头
|
||||||
|
if (props.allowNegative) {
|
||||||
|
inputValue = inputValue.replace(/[^\d.-]/g, '')
|
||||||
|
const negativeCount = (inputValue.match(/-/g) || []).length
|
||||||
|
if (negativeCount > 1) {
|
||||||
|
inputValue = '-' + inputValue.replace(/-/g, '').slice(1)
|
||||||
|
}
|
||||||
|
if (inputValue.indexOf('-') > 0) {
|
||||||
|
inputValue = inputValue.replace(/-/g, '')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 不允许负数:只保留数字和小数点
|
||||||
|
inputValue = inputValue.replace(/[^\d.]/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正整数模式(decimalPlaces 为 0):不允许输入小数点
|
||||||
|
if (props.decimalPlaces === 0) {
|
||||||
|
inputValue = inputValue.replace(/\./g, '')
|
||||||
|
displayValue.value = inputValue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入以小数点开头时,自动在前面补 0
|
||||||
|
if (inputValue.startsWith('.')) {
|
||||||
|
inputValue = '0' + inputValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 负数情况下,小数点在负号后直接输入时补 0
|
||||||
|
if (inputValue.startsWith('-') && inputValue.length > 1 && inputValue[1] === '.') {
|
||||||
|
inputValue = '-0' + inputValue.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制小数位数
|
||||||
|
const dotIndex = inputValue.indexOf('.')
|
||||||
|
if (dotIndex !== -1) {
|
||||||
|
const integerPart = inputValue.slice(0, dotIndex)
|
||||||
|
const decimalPart = inputValue.slice(dotIndex + 1)
|
||||||
|
const limitedDecimal = decimalPart.slice(0, props.decimalPlaces)
|
||||||
|
inputValue = integerPart + '.' + limitedDecimal
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只允许一个小数点
|
||||||
|
const dotCount = (inputValue.match(/\./g) || []).length
|
||||||
|
if (dotCount > 1) {
|
||||||
|
const firstDotIndex = inputValue.indexOf('.')
|
||||||
|
inputValue = inputValue.slice(0, firstDotIndex) + inputValue.slice(firstDotIndex).replace(/\./g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 特殊情况处理:单独的负号或小数点
|
||||||
|
if (inputValue === '-' || inputValue === '.') {
|
||||||
|
displayValue.value = inputValue
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证并处理负数限制
|
||||||
|
if (inputValue !== '' && inputValue !== '-') {
|
||||||
|
const num = Number(inputValue)
|
||||||
|
if (!isNaN(num)) {
|
||||||
|
if (!props.allowNegative && num < 0) {
|
||||||
|
inputValue = Math.abs(num).toFixed(props.decimalPlaces)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayValue.value = inputValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理失焦事件
|
||||||
|
* 格式化最终值并触发事件
|
||||||
|
*/
|
||||||
|
const handleBlur = () => {
|
||||||
|
let value = displayValue.value
|
||||||
|
|
||||||
|
// 空值处理
|
||||||
|
if (value === '' || value === '-' || value === '.') {
|
||||||
|
displayValue.value = ''
|
||||||
|
emit('update:modelValue', undefined)
|
||||||
|
emit('change', undefined)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证数字有效性
|
||||||
|
const num = Number(value)
|
||||||
|
if (isNaN(num)) {
|
||||||
|
displayValue.value = ''
|
||||||
|
emit('update:modelValue', undefined)
|
||||||
|
emit('change', undefined)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 负数限制处理
|
||||||
|
if (!props.allowNegative && num < 0) {
|
||||||
|
const absNum = Math.abs(num)
|
||||||
|
displayValue.value = absNum.toFixed(props.decimalPlaces)
|
||||||
|
emit('update:modelValue', absNum)
|
||||||
|
emit('change', absNum)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化并输出
|
||||||
|
displayValue.value = num.toFixed(props.decimalPlaces)
|
||||||
|
emit('update:modelValue', num)
|
||||||
|
emit('change', num)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理聚焦事件
|
||||||
|
* 聚焦时移除格式化,显示原始数值
|
||||||
|
*/
|
||||||
|
const handleFocus = () => {
|
||||||
|
if (displayValue.value !== '') {
|
||||||
|
const num = Number(displayValue.value)
|
||||||
|
if (!isNaN(num)) {
|
||||||
|
displayValue.value = num.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 组件样式 */
|
||||||
|
</style>
|
||||||
@ -0,0 +1,293 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog :title="'选择库存'" v-model="dialogVisible" width="1500px">
|
||||||
|
<!-- 搜索区域 -->
|
||||||
|
<el-form :model="queryParams" inline class="mb-4">
|
||||||
|
<el-form-item label="仓库">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.storeHouseId"
|
||||||
|
placeholder="请选择仓库"
|
||||||
|
clearable
|
||||||
|
class="!w-240px"
|
||||||
|
@change="handleStoreHouseChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in storeHouseList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.storeHouseName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="库区">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.storeAreaId"
|
||||||
|
placeholder="请选择库区"
|
||||||
|
clearable
|
||||||
|
class="!w-240px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in storeAreaList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.storeAreaName"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @click="handleQuery" type="primary">搜索</el-button>
|
||||||
|
<el-button @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 库存列表 -->
|
||||||
|
<el-table
|
||||||
|
ref="inventoryTableRef"
|
||||||
|
v-loading="loading"
|
||||||
|
:data="inventoryList"
|
||||||
|
:show-overflow-tooltip="true"
|
||||||
|
:height="400"
|
||||||
|
border
|
||||||
|
:row-key="'id'"
|
||||||
|
:reserve-selection="true"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="50px" align="center" />
|
||||||
|
<el-table-column label="序号" type="index" width="60px" align="center" />
|
||||||
|
<el-table-column label="仓库" align="center" prop="storeHouseName" width="120px" />
|
||||||
|
<el-table-column label="库区" align="center" prop="storeAreaName" width="120px" />
|
||||||
|
<el-table-column label="批次号" align="center" prop="lotNo" width="120px" />
|
||||||
|
<el-table-column label="库存袋数" align="center" prop="packQty" width="100px" />
|
||||||
|
<el-table-column label="单袋规格" align="center" prop="bagSpec" width="100px" />
|
||||||
|
<el-table-column label="库存数量" align="center" prop="yardQty" width="100px" />
|
||||||
|
<el-table-column label="最早入库日期" align="center" prop="earStoreDate" width="130px" />
|
||||||
|
<el-table-column label="产品编码" align="center" prop="matCode" width="120px" />
|
||||||
|
<el-table-column label="产品名称" align="center" prop="matName" width="180px" />
|
||||||
|
<el-table-column label="规格型号" align="center" prop="spec" width="120px" />
|
||||||
|
<el-table-column label="单位" align="center" prop="unit" width="80px" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-container">
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
class="mb-4 mt-4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleSave" type="primary">保存</el-button>
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, watch } from 'vue'
|
||||||
|
import * as InventoryApi from '@/api/biz/prostorageinventory'
|
||||||
|
import * as StoreHouseApi from '@/api/biz/storehouse'
|
||||||
|
import * as StoreAreaApi from '@/api/biz/storearea'
|
||||||
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const inventoryList = ref([])
|
||||||
|
const total = ref(0)
|
||||||
|
const inventoryTableRef = ref(null)
|
||||||
|
|
||||||
|
const emit = defineEmits(['select', 'close'])
|
||||||
|
|
||||||
|
// 已选择的库存ID列表(用于记住已选择的项,下次打开自动选中)
|
||||||
|
const selectedInventoryIds = ref<number[]>([])
|
||||||
|
|
||||||
|
// 设置已选择的ID(从父组件传入)
|
||||||
|
const setSelectedIds = (ids: number[]) => {
|
||||||
|
selectedInventoryIds.value = ids
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
storeHouseId: undefined,
|
||||||
|
storeAreaId: undefined,
|
||||||
|
matName: undefined,
|
||||||
|
spec: undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 仓储列表(条件:store_type = '3' and enabled_status = 1)
|
||||||
|
const storeHouseList = ref([])
|
||||||
|
|
||||||
|
// 库区列表(条件:store_house_id = 选中的仓储ID and enabled_status = 1)
|
||||||
|
const storeAreaList = ref([])
|
||||||
|
|
||||||
|
/** 获取仓储列表 */
|
||||||
|
const getStoreHouseList = async () => {
|
||||||
|
try {
|
||||||
|
// 使用分页接口,传入条件:store_type = '3' and enabled_status = 1
|
||||||
|
const data = await StoreHouseApi.getStoreHousePage({
|
||||||
|
storeType: '3',
|
||||||
|
enabledStatus: 1,
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 100,
|
||||||
|
})
|
||||||
|
storeHouseList.value = data.list || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取仓储列表失败:', error)
|
||||||
|
storeHouseList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取库区列表 */
|
||||||
|
const getStoreAreaList = async (storeHouseId?: number) => {
|
||||||
|
storeAreaList.value = []
|
||||||
|
if (!storeHouseId) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用分页接口,传入条件:store_house_id = 选中的仓储ID and enabled_status = 1
|
||||||
|
const data = await StoreAreaApi.getStoreAreaPage({
|
||||||
|
storeHouseId,
|
||||||
|
enabledStatus: 1,
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 100,
|
||||||
|
})
|
||||||
|
storeAreaList.value = data.list || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取库区列表失败:', error)
|
||||||
|
storeAreaList.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 仓储变更时刷新库区列表 */
|
||||||
|
const handleStoreHouseChange = (val) => {
|
||||||
|
queryParams.storeAreaId = undefined
|
||||||
|
getStoreAreaList(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取库存列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const params: any = {
|
||||||
|
...queryParams
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据用户需求添加模糊查询条件
|
||||||
|
if (queryParams.storeHouseId) {
|
||||||
|
params.storeHouseName = { like: '%仓库%' }
|
||||||
|
}
|
||||||
|
if (queryParams.storeAreaId) {
|
||||||
|
params.storeAreaName = { like: '%库区%' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await InventoryApi.getProStorageInventoryPage(params)
|
||||||
|
// 按批次号升序排列
|
||||||
|
inventoryList.value = (data.list || []).sort((a, b) => {
|
||||||
|
const lotA = a.lotNo || ''
|
||||||
|
const lotB = b.lotNo || ''
|
||||||
|
return lotA.localeCompare(lotB)
|
||||||
|
})
|
||||||
|
total.value = data.total || 0
|
||||||
|
|
||||||
|
// 数据加载完成后,自动选中已选择的行
|
||||||
|
setTimeout(() => {
|
||||||
|
setDefaultSelection()
|
||||||
|
}, 100)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取库存列表失败:', error)
|
||||||
|
inventoryList.value = []
|
||||||
|
total.value = 0
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设置默认选中 */
|
||||||
|
const setDefaultSelection = () => {
|
||||||
|
if (!inventoryTableRef.value || !selectedInventoryIds.value.length) return
|
||||||
|
|
||||||
|
inventoryList.value.forEach(row => {
|
||||||
|
if (selectedInventoryIds.value.includes(row.id)) {
|
||||||
|
inventoryTableRef.value.toggleRowSelection(row, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索 */
|
||||||
|
const handleQuery = () => {
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置 */
|
||||||
|
const resetQuery = () => {
|
||||||
|
queryParams.storeHouseId = undefined
|
||||||
|
queryParams.storeAreaId = undefined
|
||||||
|
queryParams.matName = undefined
|
||||||
|
queryParams.spec = undefined
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
storeAreaList.value = []
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 保存按钮 */
|
||||||
|
const handleSave = () => {
|
||||||
|
if (!inventoryTableRef.value) return
|
||||||
|
|
||||||
|
// 获取选中的行 - 使用 getSelectionRows() 方法
|
||||||
|
const selectedRows = inventoryTableRef.value.getSelectionRows ? inventoryTableRef.value.getSelectionRows() : []
|
||||||
|
|
||||||
|
if (selectedRows.length === 0) {
|
||||||
|
message.warning('请至少选择一条库存记录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换数据格式
|
||||||
|
const selectData = selectedRows.map(row => ({
|
||||||
|
id: row.id,
|
||||||
|
storeHouseId: row.storeHouseId,
|
||||||
|
storeHouseName: row.storeHouseName,
|
||||||
|
storeAreaId: row.storeAreaId,
|
||||||
|
storeAreaName: row.storeAreaName,
|
||||||
|
lotNo: row.lotNo,
|
||||||
|
packQty: row.packQty,
|
||||||
|
bagSpec: row.bagSpec,
|
||||||
|
yardQty: row.yardQty,
|
||||||
|
earStoreDate: row.earStoreDate,
|
||||||
|
matCode: row.matCode,
|
||||||
|
matName: row.matName,
|
||||||
|
spec: row.spec,
|
||||||
|
unit: row.unit,
|
||||||
|
materialId: row.materialId,
|
||||||
|
}))
|
||||||
|
|
||||||
|
emit('select', selectData)
|
||||||
|
dialogVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = () => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
// 初始化仓储列表
|
||||||
|
getStoreHouseList()
|
||||||
|
// 执行搜索
|
||||||
|
// handleQuery()
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open, setSelectedIds })
|
||||||
|
|
||||||
|
watch(dialogVisible, (val) => {
|
||||||
|
if (!val) {
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pagination-container {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -9,6 +9,7 @@
|
|||||||
>
|
>
|
||||||
<!-- 基础信息 -->
|
<!-- 基础信息 -->
|
||||||
<el-card title="基础信息" class="mb-4">
|
<el-card title="基础信息" class="mb-4">
|
||||||
|
<span>基础信息</span>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="销售订单" prop="saleOrdId">
|
<el-form-item label="销售订单" prop="saleOrdId">
|
||||||
@ -84,12 +85,11 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="发货数量" prop="deliveriedQty">
|
<el-form-item label="发货数量" prop="deliveriedQty">
|
||||||
<el-input
|
<MoneyInput
|
||||||
v-model="formData.deliveriedQty"
|
v-model="formData.deliveriedQty"
|
||||||
placeholder="请输入发货数量"
|
:decimal-places="0"
|
||||||
type="number"
|
:allow-negative="false"
|
||||||
:min="0"
|
:show-prefix="false"
|
||||||
:max="formData.remaimQty || 0"
|
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -110,6 +110,7 @@
|
|||||||
|
|
||||||
<!-- 收货信息 -->
|
<!-- 收货信息 -->
|
||||||
<el-card title="收货信息" class="mb-4">
|
<el-card title="收货信息" class="mb-4">
|
||||||
|
<span>收货信息</span>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="联系人" prop="contact">
|
<el-form-item label="联系人" prop="contact">
|
||||||
@ -130,59 +131,61 @@
|
|||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<!-- 产品信息 -->
|
<!-- 产品信息 -->
|
||||||
<el-card title="产品信息">
|
<el-card>
|
||||||
<div class="mb-3 flex items-center justify-between">
|
<template #header>
|
||||||
<span></span>
|
<div class="flex items-center">
|
||||||
<el-button type="primary" plain @click="addProductItem">新增</el-button>
|
<span>产品信息</span>
|
||||||
|
<el-button type="primary" plain @click="addProductItem" class="ml-4">新增</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
<el-table :data="productList" show-summary border :summary-method="getSummary">
|
<el-table :data="productList" show-summary border :summary-method="getSummary">
|
||||||
<el-table-column label="序号" type="index" width="60px" />
|
<el-table-column label="序号" type="index" width="60px" align="center" />
|
||||||
<el-table-column label="仓库(*)" prop="warehouse" width="100px">
|
<el-table-column label="仓库(*)" prop="warehouse" width="150px" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.warehouse" placeholder="请输入仓库" />
|
<el-input v-model="scope.row.warehouse" placeholder="从库存选择" readonly />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="库区(*)" prop="warehouseArea" width="100px">
|
<el-table-column label="库区(*)" prop="warehouseArea" width="150px" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.warehouseArea" placeholder="请输入库区" />
|
<el-input v-model="scope.row.warehouseArea" placeholder="从库存选择" readonly />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="批次号(*)" prop="batchNo" width="120px">
|
<el-table-column label="批次号(*)" prop="batchNo" width="150px" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.batchNo" placeholder="请输入批次号" />
|
<el-input v-model="scope.row.batchNo" placeholder="从库存选择" readonly />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="库存数量" prop="stockQty" width="100px">
|
<el-table-column label="库存数量" prop="stockQty" width="150px" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.stockQty" placeholder="库存数量" readonly />
|
<el-input v-model="scope.row.stockQty" placeholder="库存数量" readonly />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="库存袋数" prop="stockBag" width="100px">
|
<el-table-column label="库存袋数" prop="stockBag" width="150px" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.stockBag" placeholder="库存袋数" readonly />
|
<el-input v-model="scope.row.stockBag" placeholder="库存袋数" readonly />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="单袋规格" prop="bagSpec" width="100px">
|
<el-table-column label="单袋规格" prop="bagSpec" width="150px" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.bagSpec" placeholder="手动录入" />
|
<el-input v-model="scope.row.bagSpec" placeholder="手动录入" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="发货袋数(*)" prop="deliveriedBag" width="120px">
|
<el-table-column label="发货袋数(*)" prop="deliveriedBag" width="120px" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.deliveriedBag" placeholder="请输入" />
|
<el-input v-model="scope.row.deliveriedBag" placeholder="请输入" @input="refreshTable" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="发货数量(*)" prop="deliveriedQty" width="120px">
|
<el-table-column label="发货数量(*)" prop="deliveriedQty" width="120px" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.deliveriedQty" placeholder="请输入" />
|
<el-input v-model="scope.row.deliveriedQty" placeholder="请输入" @input="refreshTable" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="备注" prop="remark" width="150px">
|
<el-table-column label="备注" prop="remark" width="120px" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.remark" placeholder="文本-手动录入" />
|
<el-input v-model="scope.row.remark" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="80px">
|
<el-table-column label="操作" width="80px" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
link
|
link
|
||||||
@ -205,6 +208,9 @@
|
|||||||
|
|
||||||
<!-- 订单选择弹窗 -->
|
<!-- 订单选择弹窗 -->
|
||||||
<OrderSelectDialog ref="orderSelectRef" @select="handleOrderSelect" />
|
<OrderSelectDialog ref="orderSelectRef" @select="handleOrderSelect" />
|
||||||
|
|
||||||
|
<!-- 库存选择弹窗 -->
|
||||||
|
<ProStorageInventorySelectDialog ref="inventorySelectRef" @select="handleInventorySelect" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -214,6 +220,7 @@ import * as OrderApi from '@/api/biz/tsoorder'
|
|||||||
import * as CustomerApi from '@/api/biz/customer'
|
import * as CustomerApi from '@/api/biz/customer'
|
||||||
import { watch } from 'vue'
|
import { watch } from 'vue'
|
||||||
import OrderSelectDialog from './OrderSelectDialog.vue'
|
import OrderSelectDialog from './OrderSelectDialog.vue'
|
||||||
|
import ProStorageInventorySelectDialog from '../prostorageinventory/ProStorageInventorySelectDialog.vue'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
@ -229,6 +236,8 @@ const saleOrderOptions = ref([])
|
|||||||
// 订单选择弹窗
|
// 订单选择弹窗
|
||||||
const orderSelectRef = ref()
|
const orderSelectRef = ref()
|
||||||
|
|
||||||
|
// 库存选择弹窗
|
||||||
|
const inventorySelectRef = ref()
|
||||||
|
|
||||||
// 获取今日日期
|
// 获取今日日期
|
||||||
const getToday = () => {
|
const getToday = () => {
|
||||||
@ -267,19 +276,7 @@ const formData = reactive({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 产品列表
|
// 产品列表
|
||||||
const productList = ref([
|
const productList = ref([])
|
||||||
{
|
|
||||||
warehouse: '',
|
|
||||||
warehouseArea: '',
|
|
||||||
batchNo: '',
|
|
||||||
stockQty: '',
|
|
||||||
stockBag: '',
|
|
||||||
bagSpec: '',
|
|
||||||
deliveriedBag: '',
|
|
||||||
deliveriedQty: '',
|
|
||||||
remark: '',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// 合计
|
// 合计
|
||||||
const totalBag = computed(() => {
|
const totalBag = computed(() => {
|
||||||
@ -294,15 +291,32 @@ const totalQty = computed(() => {
|
|||||||
const getSummary = (param: any) => {
|
const getSummary = (param: any) => {
|
||||||
const { columns, data } = param
|
const { columns, data } = param
|
||||||
const sums: any[] = []
|
const sums: any[] = []
|
||||||
|
|
||||||
|
// 确保数据数组有效
|
||||||
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
|
columns.forEach((column: any, index: number) => {
|
||||||
|
sums[index] = index === 0 ? '合计' : ''
|
||||||
|
})
|
||||||
|
return sums
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算发货袋数合计
|
||||||
|
const totalDeliveriedBag = data.reduce((sum: number, item: any) => sum + (parseInt(item.deliveriedBag) || 0), 0)
|
||||||
|
|
||||||
|
// 计算发货数量合计
|
||||||
|
const totalDeliveriedQty = data.reduce((sum: number, item: any) => sum + (parseInt(item.deliveriedQty) || 0), 0)
|
||||||
|
|
||||||
|
// 使用列索引来匹配(更可靠)
|
||||||
|
// 列顺序:0-序号, 1-仓库, 2-库区, 3-批次号, 4-库存数量, 5-库存袋数, 6-单袋规格, 7-发货袋数, 8-发货数量, 9-备注, 10-操作
|
||||||
columns.forEach((column: any, index: number) => {
|
columns.forEach((column: any, index: number) => {
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
sums[index] = '合计'
|
sums[index] = '合计'
|
||||||
} else if (column.prop === 'deliveriedBag') {
|
} else if (index === 7) {
|
||||||
const total = data.reduce((sum: number, item: any) => sum + (parseInt(item.deliveriedBag) || 0), 0)
|
// 发货袋数列
|
||||||
sums[index] = total
|
sums[index] = totalDeliveriedBag
|
||||||
} else if (column.prop === 'deliveriedQty') {
|
} else if (index === 8) {
|
||||||
const total = data.reduce((sum: number, item: any) => sum + (parseInt(item.deliveriedQty) || 0), 0)
|
// 发货数量列
|
||||||
sums[index] = total
|
sums[index] = totalDeliveriedQty
|
||||||
} else {
|
} else {
|
||||||
sums[index] = ''
|
sums[index] = ''
|
||||||
}
|
}
|
||||||
@ -310,6 +324,14 @@ const getSummary = (param: any) => {
|
|||||||
return sums
|
return sums
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 强制刷新表格(用于实时更新合计) */
|
||||||
|
const refreshTable = () => {
|
||||||
|
// 通过修改key强制表格重新渲染
|
||||||
|
if (productList.value.length > 0) {
|
||||||
|
productList.value = [...productList.value]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 监听弹窗关闭
|
// 监听弹窗关闭
|
||||||
watch(dialogVisible, (val) => {
|
watch(dialogVisible, (val) => {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
@ -319,7 +341,7 @@ watch(dialogVisible, (val) => {
|
|||||||
|
|
||||||
const formRules = reactive({
|
const formRules = reactive({
|
||||||
saleOrdId: [{ required: true, message: '销售订单不能为空', trigger: 'change' }],
|
saleOrdId: [{ required: true, message: '销售订单不能为空', trigger: 'change' }],
|
||||||
deliveryDate: [{ required: true, message: '单据日期不能为空', trigger: 'change' }],
|
ordDate: [{ required: true, message: '单据日期不能为空', trigger: 'change' }],
|
||||||
deliveryStatus: [{ required: true, message: '单据状态不能为空', trigger: 'change' }],
|
deliveryStatus: [{ required: true, message: '单据状态不能为空', trigger: 'change' }],
|
||||||
deliveriedQty: [
|
deliveriedQty: [
|
||||||
{ required: true, message: '发货数量不能为空', trigger: 'change' },
|
{ required: true, message: '发货数量不能为空', trigger: 'change' },
|
||||||
@ -334,6 +356,13 @@ const formRules = reactive({
|
|||||||
trigger: ['change', 'blur']
|
trigger: ['change', 'blur']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
custName: [{ required: true, message: '客户名称不能为空', trigger: 'change' }],
|
||||||
|
materialName: [{ required: true, message: '产品名称不能为空', trigger: 'change' }],
|
||||||
|
spec: [{ required: true, message: '规格型号不能为空', trigger: 'change' }],
|
||||||
|
saleDeliveryNo: [{ required: true, message: '出库单号不能为空', trigger: 'change' }],
|
||||||
|
ordQty: [{ required: true, message: '订单数量不能为空', trigger: 'change' }],
|
||||||
|
remaimQty: [{ required: true, message: '剩余数量不能为空', trigger: 'change' }],
|
||||||
|
deliveriedQty: [{ required: true, message: '发货数量不能为空', trigger: 'change' }],
|
||||||
})
|
})
|
||||||
|
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
@ -487,16 +516,45 @@ const loadCustomerContact = async (custId: number) => {
|
|||||||
|
|
||||||
/** 添加产品项 */
|
/** 添加产品项 */
|
||||||
const addProductItem = () => {
|
const addProductItem = () => {
|
||||||
|
// 获取已选择的库存ID列表
|
||||||
|
const selectedIds = productList.value
|
||||||
|
.filter(item => item.inventoryId)
|
||||||
|
.map(item => item.inventoryId)
|
||||||
|
|
||||||
|
// 设置已选择的ID,以便弹窗打开时自动选中
|
||||||
|
inventorySelectRef.value.setSelectedIds(selectedIds)
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
inventorySelectRef.value.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理库存选择 */
|
||||||
|
const handleInventorySelect = (data: any[]) => {
|
||||||
|
// data 是选中的库存记录数组
|
||||||
|
if (!data || data.length === 0) return
|
||||||
|
|
||||||
|
// 遍历选中的库存记录,根据 id 判断是否已存在
|
||||||
|
data.forEach(inventory => {
|
||||||
|
const exists = productList.value.find(item => item.inventoryId === inventory.id)
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
// 不存在则新增
|
||||||
productList.value.push({
|
productList.value.push({
|
||||||
warehouse: '',
|
inventoryId: inventory.id, // 库存ID,用于判断是否已存在
|
||||||
warehouseArea: '',
|
storeHouseId: inventory.storeHouseId, // 仓储ID
|
||||||
batchNo: '',
|
storeAreaId: inventory.storeAreaId, // 库区ID
|
||||||
stockQty: '',
|
warehouse: inventory.storeHouseName, // 仓库名称
|
||||||
stockBag: '',
|
warehouseArea: inventory.storeAreaName, // 库区名称
|
||||||
bagSpec: '',
|
batchNo: inventory.lotNo, // 批次号
|
||||||
deliveriedBag: '',
|
stockQty: inventory.yardQty, // 库存数量
|
||||||
deliveriedQty: '',
|
stockBag: inventory.packQty, // 库存袋数
|
||||||
remark: '',
|
bagSpec: inventory.bagSpec, // 单袋规格
|
||||||
|
deliveriedBag: '', // 发货袋数
|
||||||
|
deliveriedQty: '', // 发货数量
|
||||||
|
remark: '', // 备注
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 如果已存在,不做处理
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -510,16 +568,50 @@ const removeProductItem = (index: number) => {
|
|||||||
/** 提交表单 */
|
/** 提交表单 */
|
||||||
const emit = defineEmits(['success', 'close'])
|
const emit = defineEmits(['success', 'close'])
|
||||||
|
|
||||||
|
/** 验证发货数量 */
|
||||||
|
const validateDeliveryQty = () => {
|
||||||
|
const remaimQty = parseInt(formData.remaimQty) || 0
|
||||||
|
const totalDeliveriedQty = totalQty.value
|
||||||
|
|
||||||
|
if (totalDeliveriedQty > remaimQty) {
|
||||||
|
message.warning(`发货总数量(${totalDeliveriedQty})不能超过剩余数量(${remaimQty})`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const submitForm = async () => {
|
const submitForm = async () => {
|
||||||
|
// 校验发货数量
|
||||||
|
if (!validateDeliveryQty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 校验表单
|
// 校验表单
|
||||||
await formRef.value.validate()
|
await formRef.value.validate()
|
||||||
|
|
||||||
// 提交请求
|
// 提交请求
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
|
// 将产品信息数据传输到 detailList
|
||||||
|
const detailList = productList.value.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
inventoryId: item.inventoryId,
|
||||||
|
storeHouseId: item.storeHouseId,
|
||||||
|
storeAreaId: item.storeAreaId,
|
||||||
|
warehouse: item.warehouse,
|
||||||
|
warehouseArea: item.warehouseArea,
|
||||||
|
batchNo: item.batchNo,
|
||||||
|
stockQty: item.stockQty,
|
||||||
|
stockBag: item.stockBag,
|
||||||
|
bagSpec: item.bagSpec,
|
||||||
|
deliveriedBag: item.deliveriedBag,
|
||||||
|
deliveriedQty: item.deliveriedQty,
|
||||||
|
remark: item.remark,
|
||||||
|
}))
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
...formData,
|
...formData,
|
||||||
items: productList.value,
|
detailList,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formType.value === 'create') {
|
if (formType.value === 'create') {
|
||||||
@ -538,16 +630,38 @@ const submitForm = async () => {
|
|||||||
|
|
||||||
/** 提交确认 */
|
/** 提交确认 */
|
||||||
const submitAudit = async () => {
|
const submitAudit = async () => {
|
||||||
|
// 校验发货数量
|
||||||
|
if (!validateDeliveryQty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 校验表单
|
// 校验表单
|
||||||
await formRef.value.validate()
|
await formRef.value.validate()
|
||||||
|
|
||||||
// 提交请求
|
// 提交请求
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
|
// 将产品信息数据传输到 detailList
|
||||||
|
const detailList = productList.value.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
inventoryId: item.inventoryId,
|
||||||
|
storeHouseId: item.storeHouseId,
|
||||||
|
storeAreaId: item.storeAreaId,
|
||||||
|
warehouse: item.warehouse,
|
||||||
|
warehouseArea: item.warehouseArea,
|
||||||
|
batchNo: item.batchNo,
|
||||||
|
stockQty: item.stockQty,
|
||||||
|
stockBag: item.stockBag,
|
||||||
|
bagSpec: item.bagSpec,
|
||||||
|
deliveriedBag: item.deliveriedBag,
|
||||||
|
deliveriedQty: item.deliveriedQty,
|
||||||
|
remark: item.remark,
|
||||||
|
}))
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
...formData,
|
...formData,
|
||||||
deliveryStatus: '2', // 已确认
|
deliveryStatus: '2', // 已确认
|
||||||
items: productList.value,
|
detailList,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formType.value === 'create') {
|
if (formType.value === 'create') {
|
||||||
@ -593,17 +707,7 @@ const resetForm = () => {
|
|||||||
ordDate: null,
|
ordDate: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
productList.value = [{
|
productList.value = []
|
||||||
warehouse: '',
|
|
||||||
warehouseArea: '',
|
|
||||||
batchNo: '',
|
|
||||||
stockQty: '',
|
|
||||||
stockBag: '',
|
|
||||||
bagSpec: '',
|
|
||||||
deliveriedBag: '',
|
|
||||||
deliveriedQty: '',
|
|
||||||
remark: '',
|
|
||||||
}]
|
|
||||||
|
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,15 +71,19 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 表格区域:主列表 + 明细表格上下各占一半 -->
|
||||||
|
<ContentWrap class="!p-15px">
|
||||||
|
<div style="display: flex; flex-direction: column; height: calc(100vh - 280px);">
|
||||||
<!-- 主列表 -->
|
<!-- 主列表 -->
|
||||||
<ContentWrap>
|
<div style="flex: 1; min-height: 0;">
|
||||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
<div style="font-weight: bold; margin-bottom: 8px;">出库单列表</div>
|
||||||
<el-table-column type="selection" width="55px" />
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" border @row-click="handleRowClick" style="height: calc(50vh - 220px);">
|
||||||
<el-table-column label="序号" align="center" type="index" width="60px"/>
|
<el-table-column type="selection" width="55px" align="right" fixed="left" />
|
||||||
<el-table-column label="出库单号" align="center" prop="saleDeliveryNo" width="140px" />
|
<el-table-column label="序号" align="center" type="index" width="60px" fixed="left" />
|
||||||
<el-table-column label="单据日期" align="center" prop="ordDate" width="120px" />
|
<el-table-column label="出库单号" align="center" prop="saleDeliveryNo" width="130px" fixed="left" />
|
||||||
<el-table-column label="客户名称" align="center" prop="custName" width="120px" />
|
<el-table-column label="单据日期" align="center" prop="deliveryDate" width="110px" fixed="left" />
|
||||||
<el-table-column label="产品名称" align="center" prop="materialName" width="120px" />
|
<el-table-column label="客户名称" align="center" prop="custName" width="250px" />
|
||||||
|
<el-table-column label="产品名称" align="center" prop="materialName" width="160px" />
|
||||||
<el-table-column label="产品规格" align="center" prop="spec" width="120px" />
|
<el-table-column label="产品规格" align="center" prop="spec" width="120px" />
|
||||||
<el-table-column label="销售订单号" align="center" prop="saleOrdNo" width="140px" />
|
<el-table-column label="销售订单号" align="center" prop="saleOrdNo" width="140px" />
|
||||||
<el-table-column label="订单数量" align="center" prop="ordQty" width="100px" />
|
<el-table-column label="订单数量" align="center" prop="ordQty" width="100px" />
|
||||||
@ -94,13 +98,13 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="联系人" align="center" prop="contact" width="100px" />
|
<el-table-column label="联系人" align="center" prop="contact" width="100px" />
|
||||||
<el-table-column label="联系电话" align="center" prop="conPhone" width="120px" />
|
<el-table-column label="联系电话" align="center" prop="conPhone" width="120px" />
|
||||||
<el-table-column label="联系地址" align="center" prop="conAddress" />
|
<el-table-column label="联系地址" align="center" prop="conAddress" width="180px" />
|
||||||
<el-table-column label="单据状态" align="center" prop="deliveryStatus" width="100px">
|
<el-table-column label="单据状态" align="center" prop="deliveryStatus" width="100px" fixed="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<dict-tag :type="DICT_TYPE.BILL_STATUS" :value="scope.row.deliveryStatus" />
|
<dict-tag :type="DICT_TYPE.BILL_STATUS" :value="scope.row.deliveryStatus" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" width="180px">
|
<el-table-column label="操作" align="center" width="170px" fixed="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
link
|
link
|
||||||
@ -129,18 +133,19 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<!-- 分页 -->
|
|
||||||
<Pagination
|
<Pagination
|
||||||
:total="total"
|
:total="total"
|
||||||
v-model:page="queryParams.pageNo"
|
v-model:page="queryParams.pageNo"
|
||||||
v-model:limit="queryParams.pageSize"
|
v-model:limit="queryParams.pageSize"
|
||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
|
style="margin-top: 8px;"
|
||||||
/>
|
/>
|
||||||
</ContentWrap>
|
</div>
|
||||||
|
|
||||||
<!-- 明细表格区域 -->
|
<!-- 明细表格 -->
|
||||||
<ContentWrap v-if="selectedRow">
|
<div style="flex: 1; min-height: 0;">
|
||||||
<el-table :data="selectedRow.items" :stripe="true" :show-overflow-tooltip="true">
|
<div style="font-weight: bold; margin-bottom: 8px;">出库单明细</div>
|
||||||
|
<el-table v-loading="detailLoading" :data="detailList" :stripe="true" :show-overflow-tooltip="true" border :summary-method="getDetailSummary" show-summary>
|
||||||
<el-table-column label="序号" align="center" type="index" width="60px"/>
|
<el-table-column label="序号" align="center" type="index" width="60px"/>
|
||||||
<el-table-column label="库区" align="center" prop="warehouseArea" width="100px" />
|
<el-table-column label="库区" align="center" prop="warehouseArea" width="100px" />
|
||||||
<el-table-column label="库位" align="center" prop="warehouseLoc" width="100px" />
|
<el-table-column label="库位" align="center" prop="warehouseLoc" width="100px" />
|
||||||
@ -151,10 +156,7 @@
|
|||||||
<el-table-column label="单位" align="center" prop="unit" width="80px" />
|
<el-table-column label="单位" align="center" prop="unit" width="80px" />
|
||||||
<el-table-column label="备注" align="center" prop="remark" />
|
<el-table-column label="备注" align="center" prop="remark" />
|
||||||
</el-table>
|
</el-table>
|
||||||
<div class="mt-2 flex justify-end">
|
</div>
|
||||||
<span class="font-bold">合计: </span>
|
|
||||||
<span>发货数量: {{ totalDeliveriedQty }}</span>
|
|
||||||
<span class="ml-4">发货袋数: {{ totalDeliveriedBag }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
@ -163,7 +165,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed } from 'vue'
|
import { ref, reactive } from 'vue'
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
import download from '@/utils/download'
|
import download from '@/utils/download'
|
||||||
import * as SaleDeliveryApi from '@/api/biz/saledelivery'
|
import * as SaleDeliveryApi from '@/api/biz/saledelivery'
|
||||||
@ -179,6 +181,8 @@ const loading = ref(false)
|
|||||||
const list = ref([])
|
const list = ref([])
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const selectedRow = ref(null)
|
const selectedRow = ref(null)
|
||||||
|
const detailLoading = ref(false)
|
||||||
|
const detailList = ref([])
|
||||||
|
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
@ -192,16 +196,31 @@ const queryParams = reactive({
|
|||||||
const queryFormRef = ref()
|
const queryFormRef = ref()
|
||||||
const exportLoading = ref(false)
|
const exportLoading = ref(false)
|
||||||
|
|
||||||
// 合计数量
|
/** 明细表格合计 */
|
||||||
const totalDeliveriedQty = computed(() => {
|
const getDetailSummary = (param: any) => {
|
||||||
if (!selectedRow.value?.items) return 0
|
const { columns, data } = param
|
||||||
return selectedRow.value.items.reduce((sum, item) => sum + (item.deliveriedQty || 0), 0)
|
const sums: any[] = []
|
||||||
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
|
columns.forEach((column: any, index: number) => {
|
||||||
|
sums[index] = index === 0 ? '合计' : ''
|
||||||
})
|
})
|
||||||
|
return sums
|
||||||
const totalDeliveriedBag = computed(() => {
|
}
|
||||||
if (!selectedRow.value?.items) return 0
|
const totalDeliveriedBag = data.reduce((sum: number, item: any) => sum + (parseInt(item.deliveriedBag) || 0), 0)
|
||||||
return selectedRow.value.items.reduce((sum, item) => sum + (item.deliveriedBag || 0), 0)
|
const totalDeliveriedQty = data.reduce((sum: number, item: any) => sum + (parseInt(item.deliveriedQty) || 0), 0)
|
||||||
|
columns.forEach((column: any, index: number) => {
|
||||||
|
if (index === 0) {
|
||||||
|
sums[index] = '合计'
|
||||||
|
} else if (index === 4) {
|
||||||
|
sums[index] = totalDeliveriedQty
|
||||||
|
} else if (index === 5) {
|
||||||
|
sums[index] = totalDeliveriedBag
|
||||||
|
} else {
|
||||||
|
sums[index] = ''
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
return sums
|
||||||
|
}
|
||||||
|
|
||||||
/** 查询列表 */
|
/** 查询列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
@ -210,6 +229,15 @@ const getList = async () => {
|
|||||||
const data = await SaleDeliveryApi.getSaleDeliveryPage(queryParams)
|
const data = await SaleDeliveryApi.getSaleDeliveryPage(queryParams)
|
||||||
list.value = data.list
|
list.value = data.list
|
||||||
total.value = data.total
|
total.value = data.total
|
||||||
|
|
||||||
|
// 自动加载第一条数据的详情
|
||||||
|
if (list.value.length > 0) {
|
||||||
|
await viewDetail(list.value[0])
|
||||||
|
} else {
|
||||||
|
// 没有数据时清空明细
|
||||||
|
detailList.value = []
|
||||||
|
selectedRow.value = null
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@ -244,13 +272,24 @@ const handleDelete = async (id: number) => {
|
|||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 行点击事件 */
|
||||||
|
const handleRowClick = async (row: any) => {
|
||||||
|
await viewDetail(row)
|
||||||
|
}
|
||||||
|
|
||||||
/** 查看详情 */
|
/** 查看详情 */
|
||||||
const viewDetail = async (row: any) => {
|
const viewDetail = async (row: any) => {
|
||||||
|
detailLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = await SaleDeliveryApi.getSaleDelivery(row.id)
|
const data = await SaleDeliveryApi.getSaleDelivery(row.id)
|
||||||
selectedRow.value = data
|
selectedRow.value = data
|
||||||
|
// 使用 detailList 显示明细数据
|
||||||
|
detailList.value = data.items || data.detailList || []
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取详情失败', e)
|
console.error('获取详情失败', e)
|
||||||
|
detailList.value = []
|
||||||
|
} finally {
|
||||||
|
detailLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,16 +62,16 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="业务部门" prop="saleDeptId" >
|
<el-form-item label="业务部门" prop="saleDeptId" >
|
||||||
<el-input
|
<el-tree-select
|
||||||
v-model="formData.saleDeptName"
|
v-model="formData.saleDeptId"
|
||||||
|
:data="deptList"
|
||||||
|
:props="{ label: 'name', value: 'id', children: 'children' }"
|
||||||
placeholder="请选择业务部门"
|
placeholder="请选择业务部门"
|
||||||
readonly
|
filterable
|
||||||
@click="openDeptSelect"
|
check-strictly
|
||||||
>
|
class="w-full"
|
||||||
<template #suffix>
|
@change="handleDeptChange"
|
||||||
<Icon icon="ep:caret-bottom" />
|
/>
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
@ -181,12 +181,18 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="税率" prop="taxRate" >
|
<el-form-item label="税率" prop="taxRate" >
|
||||||
<el-input v-model="formData.taxRate" placeholder="请输入税率" class="text-right" />
|
<MoneyInput v-model="formData.taxRate" :decimal-places="2" suffix="%" :show-suffix="true" :show-prefix="false" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="含税总金额" prop="totalAmount" >
|
<el-form-item label="含税总金额" prop="totalAmount" >
|
||||||
<el-input v-model="formData.totalAmount" placeholder="请输入含税总金额" class="text-right" />
|
<MoneyInput
|
||||||
|
v-model="formData.totalAmount"
|
||||||
|
:decimal-places="2"
|
||||||
|
:allow-negative="false"
|
||||||
|
:show-prefix="false"
|
||||||
|
placeholder="请输入"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
@ -245,12 +251,23 @@
|
|||||||
|
|
||||||
<el-table-column label="订单数量(*)" align="center" prop="ordQty">
|
<el-table-column label="订单数量(*)" align="center" prop="ordQty">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.ordQty" placeholder="请输入" />
|
<MoneyInput
|
||||||
|
v-model="scope.row.ordQty"
|
||||||
|
:decimal-places="2"
|
||||||
|
:allow-negative="false"
|
||||||
|
:show-prefix="false"
|
||||||
|
placeholder="请输入"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="含税单价(*)" align="center" prop="priceTax">
|
<el-table-column label="含税单价(*)" align="center" prop="priceTax">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.priceTax" placeholder="请输入" />
|
<MoneyInput
|
||||||
|
v-model="scope.row.priceTax"
|
||||||
|
:decimal-places="2"
|
||||||
|
:allow-negative="false"
|
||||||
|
placeholder="请输入"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="用途(*)" align="center" width="120px">
|
<el-table-column label="用途(*)" align="center" width="120px">
|
||||||
@ -358,11 +375,14 @@ import { ref, reactive, nextTick } from 'vue'
|
|||||||
import * as OrderApi from '@/api/biz/tsoorder/'
|
import * as OrderApi from '@/api/biz/tsoorder/'
|
||||||
import * as CustomerApi from '@/api/biz/customer'
|
import * as CustomerApi from '@/api/biz/customer'
|
||||||
import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict'
|
import { getIntDictOptions, getStrDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||||
import { getDeptSimpleName } from '@/api/system/dept'
|
import { getDeptSimpleName, getSimpleDeptList } from '@/api/system/dept'
|
||||||
import * as UserApi from '@/api/system/user'
|
import * as UserApi from '@/api/system/user'
|
||||||
|
import * as DeptApi from '@/api/system/dept'
|
||||||
|
import { handleTree } from '@/utils/tree'
|
||||||
import { Icon } from '@/components/Icon'
|
import { Icon } from '@/components/Icon'
|
||||||
import MaterialSelect from '@/views/biz/material/MaterialSelect.vue'
|
import MaterialSelect from '@/views/biz/material/MaterialSelect.vue'
|
||||||
import DeptTree from '@/views/system/user/DeptTree.vue'
|
import DeptTree from '@/views/system/user/DeptTree.vue'
|
||||||
|
import MoneyInput from '@/views/biz/components/MoneyInput.vue'
|
||||||
|
|
||||||
// 获取当日日期
|
// 获取当日日期
|
||||||
const getToday = () => {
|
const getToday = () => {
|
||||||
@ -388,6 +408,7 @@ const deptSelectVisible = ref(false)
|
|||||||
const deptTreeRef = ref()
|
const deptTreeRef = ref()
|
||||||
const userList = ref<UserApi.UserVO[]>([])
|
const userList = ref<UserApi.UserVO[]>([])
|
||||||
const userSelectLoading = ref(false)
|
const userSelectLoading = ref(false)
|
||||||
|
const deptList = ref<DeptApi.DeptVO[]>([])
|
||||||
|
|
||||||
// 文件上传相关
|
// 文件上传相关
|
||||||
const uploadRef = ref()
|
const uploadRef = ref()
|
||||||
@ -486,10 +507,21 @@ const unwrapOrderDetail = (raw: Record<string, any>) => {
|
|||||||
return raw
|
return raw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取业务部门列表 */
|
||||||
|
const loadDeptList = async () => {
|
||||||
|
try {
|
||||||
|
const list = await getSimpleDeptList()
|
||||||
|
deptList.value = handleTree(list) as DeptApi.DeptVO[]
|
||||||
|
} catch (e) {
|
||||||
|
console.error('获取业务部门列表失败', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const open = async (type: string, id?: number) => {
|
const open = async (type: string, id?: number) => {
|
||||||
formType.value = type
|
formType.value = type
|
||||||
dialogTitle.value = t('action.' + type)
|
dialogTitle.value = t('action.' + type)
|
||||||
resetForm()
|
resetForm()
|
||||||
|
await loadDeptList()
|
||||||
if (id) {
|
if (id) {
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
@ -752,6 +784,18 @@ const openMaterialSelect = async () => {
|
|||||||
materialSelectRef.value?.open(selectedIds)
|
materialSelectRef.value?.open(selectedIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 部门选择变更处理
|
||||||
|
const handleDeptChange = (deptId: number) => {
|
||||||
|
const dept = deptList.value.find(d => d.id === deptId)
|
||||||
|
if (dept) {
|
||||||
|
formData.saleDeptName = dept.name
|
||||||
|
}
|
||||||
|
// 选择部门后,清空并重新加载该部门下的用户
|
||||||
|
formData.saleMan = undefined
|
||||||
|
formData.saleManName = undefined
|
||||||
|
searchUsers('')
|
||||||
|
}
|
||||||
|
|
||||||
// 打开部门选择弹窗
|
// 打开部门选择弹窗
|
||||||
const openDeptSelect = () => {
|
const openDeptSelect = () => {
|
||||||
deptSelectVisible.value = true
|
deptSelectVisible.value = true
|
||||||
|
|||||||
@ -156,6 +156,15 @@
|
|||||||
v-hasPermi="['biz:order:update']"
|
v-hasPermi="['biz:order:update']"
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
v-if="scope.row.ordStatus === '2'"
|
||||||
|
@click="openForm('update', scope.row.id)"
|
||||||
|
v-hasPermi="['biz:order:update']"
|
||||||
|
>
|
||||||
|
订单变更
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
link
|
link
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user