新增 下单分不同类型添加sku,下单预览图设置背景

This commit is contained in:
YuanFeng 2024-11-16 14:04:38 +08:00
parent 1b221bca72
commit ad175a2393
44 changed files with 1852 additions and 723 deletions

View File

@ -597,7 +597,7 @@ public class FuncUtil extends StringUtils {
return requestAttributes == null ? null : ((ServletRequestAttributes) requestAttributes).getRequest();
}
public static LocalDateTime parseDate(Long timestamp) {
public static LocalDateTime timeToLocalDate(Long timestamp) {
if(isEmpty(timestamp)){
return null;
}
@ -605,6 +605,13 @@ public class FuncUtil extends StringUtils {
// Instant 转换为 LocalDateTime
return LocalDateTime.ofInstant(instant, java.time.ZoneId.systemDefault());
}
public static Long localDateToLong(LocalDateTime date) {
if(isEmpty(date)){
return null;
}
Instant instant = date.atZone(TimeZone.getDefault().toZoneId()).toInstant();
return instant.getEpochSecond();
}
private static final Snowflake snowflake = IdUtil.getSnowflake();
public static long getNextId() {
return snowflake.nextId();

View File

@ -1,5 +1,6 @@
package cn.hangtag.framework.common.util.validation;
import cn.hangtag.framework.common.exception.ErrorCode;
import cn.hangtag.framework.common.exception.ServiceException;
import cn.hangtag.framework.common.util.FuncUtil;
import org.springframework.util.Assert;
@ -28,4 +29,22 @@ public class AssertUtil {
}
}
public static void isEmpty(Object field, ErrorCode errorCode) {
if (FuncUtil.isEmpty(field)) {
throw new ServiceException(errorCode);
}
}
public static void isNull(Object field, String errorMessage) {
if (field == null) {
throw new ServiceException(600,errorMessage);
}
}
public static void isNull(Object field, ErrorCode errorCode) {
if (field == null){
throw new ServiceException(errorCode);
}
}
}

View File

@ -3,6 +3,9 @@ package cn.hangtag.module.oms.enums.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
/**
* 单据状态
* @author wwb
@ -29,4 +32,30 @@ public enum BillStatusEnum {
*/
private final String name;
public static BillStatusEnum getByValue(String value) {
for (BillStatusEnum item : BillStatusEnum.values()) {
if (item.getValue().equals(value)) {
return item;
}
}
return null;
}
private static final List<String> CAN_STATUS = new ArrayList<>();;
static {
CAN_STATUS.add(BillStatusEnum.REJECT.getValue());
CAN_STATUS.add(BillStatusEnum.SAVE.getValue());
CAN_STATUS.add(BillStatusEnum.SUBMIT.getValue());
}
/**
* 是否允许 编辑订单状态
*
* @return {@link List }<{@link String }>
*/
public static boolean isCanEditOrder(String status){
return CAN_STATUS.contains(status);
}
}

View File

@ -10,18 +10,12 @@ import cn.hangtag.framework.common.util.object.BeanUtils;
import cn.hangtag.framework.excel.core.util.ExcelUtils;
import cn.hangtag.framework.security.core.LoginUser;
import cn.hangtag.framework.security.core.util.SecurityFrameworkUtils;
import cn.hangtag.module.oms.controller.admin.productinfo.vo.ProductInfoPageReqVO;
import cn.hangtag.module.oms.controller.admin.productinfo.vo.ProductInfoRespVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderPageReqVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderRemarkReqVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderRespVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderSaveReqVO;
import cn.hangtag.module.oms.controller.admin.saleorder.front.vo.SaleOrderFollowerUserVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.*;
import cn.hangtag.module.oms.convert.saleorder.SaleOrderConvert;
import cn.hangtag.module.oms.dal.dataobject.customer.CustomerDO;
import cn.hangtag.module.oms.dal.dataobject.productinfo.ProductInfoDO;
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
import cn.hangtag.module.oms.service.customer.CustomerService;
import cn.hangtag.module.oms.service.productinfo.ProductInfoService;
import cn.hangtag.module.oms.service.saleorder.SaleOrderService;
import cn.hangtag.module.system.api.user.AdminUserApi;
import cn.hangtag.module.system.api.user.dto.AdminUserRespDTO;
@ -30,12 +24,10 @@ import com.alibaba.fastjson.JSONObject;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
@ -189,5 +181,18 @@ public class AppSaleOrderController{
}
@GetMapping("/follower-user/{brandId}")
@Operation(summary = "获取指定品牌跟单员")
public CommonResult<List<SaleOrderFollowerUserVO>> getFollowerUserList(@PathVariable("brandId") String brandId) {
List<SaleOrderFollowerUserVO> users = saleOrderService.queryFollowerUserListByBrandId(brandId);
return success(users);
}
@GetMapping("/contract-code")
@Operation(summary = "获取销售合同号")
public CommonResult<String> getContractCode() {
String code = saleOrderService.getNewContractCode2();
return success(code);
}
}

View File

@ -98,6 +98,11 @@ public class ProductInfoRespVO {
@ExcelProperty("规格材质")
private String specMaterial;
/**
* 模板类型-字典 product_draft_template_type 1有模板 2无模板 3尺码唛
*/
private String templateType;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;

View File

@ -73,6 +73,12 @@ public class ProductInfoSaveReqVO {
@Schema(description = "规格材质")
private String specMaterial;
/**
* 模板类型-字典 product_draft_template_type 1有模板 2无模板 3尺码唛
*/
@Schema(description = "模板类型")
private String templateType;
@Schema(description = "产品信息工艺列表")
private List<ProductProcessSaveReqVO> productProcessList;

View File

@ -34,5 +34,17 @@ public class SaleOrderFrontController {
public CommonResult<Long> placeOrder(@Valid @RequestBody CreateSaleOrderDTO dto) {
return success(saleOrderService.placeOrder(dto));
}
@PostMapping("/editOrder/{id}")
@Operation(summary = "修改订单")
public CommonResult<Long> editOrder(@PathVariable Long id,@Valid @RequestBody CreateSaleOrderDTO dto) {
return success(saleOrderService.editOrder(id,dto));
}
@GetMapping("/details/{id}")
@Operation(summary = "编辑订单")
public CommonResult<CreateSaleOrderDTO> placeOrder(@PathVariable Long id) {
CreateSaleOrderDTO dto = saleOrderService.queryEditOrder(id);
return success(dto);
}
}

View File

@ -9,6 +9,8 @@ import java.util.List;
@Data
public class CreateSaleOrderDTO implements Serializable {
Long id;
/**
* 联系人信息
*/
@ -89,6 +91,10 @@ public class CreateSaleOrderDTO implements Serializable {
*/
private String deliveryRemark;
/**
* 跟单员id system_user.id
*/
private String orderFollowerUser;
/**
* 是否分批交货
@ -96,7 +102,6 @@ public class CreateSaleOrderDTO implements Serializable {
private Boolean isBatch;
private List<SaleOrderEntryItemDTO> saleOrderEntry;
}

View File

@ -1,6 +1,10 @@
package cn.hangtag.module.oms.controller.admin.saleorder.front.dto;
import cn.hangtag.framework.common.util.FuncUtil;
import cn.hangtag.module.oms.dal.dataobject.saleorderentry.SaleOrderEntryDO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
@ -12,6 +16,8 @@ import java.util.List;
* @date 2024/10/02
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SaleOrderEntryItemDTO implements Serializable {
/**
@ -43,10 +49,17 @@ public class SaleOrderEntryItemDTO implements Serializable {
private String cover;
/**
* 订货数量
*/
private Integer orderQty;
/**
* 计量单位
*/
private String unit;
/**
* 总数量
*/
@ -56,11 +69,18 @@ public class SaleOrderEntryItemDTO implements Serializable {
*/
private Long deliveryDate;
/**
* 产品详情
*/
private String details;
/**
* 产品sku
*/
List<SaleOrderSkuDTO> productSkuList;
public SaleOrderEntryItemDTO(SaleOrderEntryDO entryDO) {
this.productId = entryDO.getMaterialId();
this.orderQty = entryDO.getQty();
this.deliveryDate = FuncUtil.localDateToLong(entryDO.getDeliveryDate());
}
}

View File

@ -1,6 +1,10 @@
package cn.hangtag.module.oms.controller.admin.saleorder.front.dto;
import cn.hangtag.module.oms.dal.dataobject.saleordersku.SaleOrderSkuDO;
import cn.hutool.json.JSONUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
@ -12,6 +16,8 @@ import java.util.List;
* @date 2024/10/02
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SaleOrderSkuDTO implements Serializable {
private String itemKey;
@ -24,7 +30,7 @@ public class SaleOrderSkuDTO implements Serializable {
/**
* 订货数量
*/
private Long orderQty;
private Integer orderQty;
/**
* 草图设计id
@ -47,11 +53,21 @@ public class SaleOrderSkuDTO implements Serializable {
*/
private String previewImage;
/**
* 模板类型-字典 product_draft_template_type 1有模板 2无模板 3尺码唛
*/
private String productTemplateType;
/**
* 动态属性json配置信息
*/
private String propInfo;
/**
* 尺码信息
*/
private String labelSize;
/**
* 动态属性 排序信息
*/
@ -63,5 +79,20 @@ public class SaleOrderSkuDTO implements Serializable {
private SpecInfoDTO specInfo;
public SaleOrderSkuDTO(SaleOrderSkuDO saleOrderSkuDO) {
this.itemKey = saleOrderSkuDO.getItemKey();
this.productId = saleOrderSkuDO.getProductId();
this.orderQty = saleOrderSkuDO.getOrderQty();
this.draftDesignId = saleOrderSkuDO.getDraftDesignId();
this.width = saleOrderSkuDO.getWidth().doubleValue();
this.height = saleOrderSkuDO.getHeight().doubleValue();
this.previewImage = saleOrderSkuDO.getPreviewImage();
this.productTemplateType = saleOrderSkuDO.getProductTemplateType();
this.propInfo = saleOrderSkuDO.getPropInfo();
this.labelSize = saleOrderSkuDO.getLabelSize();
String specInfo1 = saleOrderSkuDO.getSpecInfo();
if(specInfo1 != null){
this.specInfo = JSONUtil.toBean(specInfo1, SpecInfoDTO.class);
}
}
}

View File

@ -0,0 +1,28 @@
package cn.hangtag.module.oms.controller.admin.saleorder.front.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 销售订单跟单员vo
*
* @author YuanFeng
* @date 2024/11/09
*/
@Data
public class SaleOrderFollowerUserVO {
/**
* system_users id
*/
@Schema(description = "用户Id")
private Long id;
/**
* 跟单员名称
* system_users username
*/
@Schema(description = "跟单员名称")
private String username;
}

View File

@ -46,6 +46,9 @@ public class SaleOrderSkuSaveReqVO {
@Schema(description = "规格 json完整信息")
private String specInfo;
@Schema(description = "产品尺码")
private String labelSize;
@Schema(description = "宽(mm)")
private BigDecimal specSizeWidth;

View File

@ -97,6 +97,11 @@ public class ProductInfoDO extends BaseDO {
@TableField("spec_material")
private String specMaterial;
/**
* 模板类型-字典 product_draft_template_type 1有模板 2无模板 3尺码唛
*/
@TableField("template_type")
private String templateType;
/**

View File

@ -159,6 +159,11 @@ public class SaleOrderDO extends BaseDO {
*/
private String deliveryRemark;
/**
* 跟单员id system_user.id
*/
private String orderFollowerUser;
public SaleOrderDO(CreateSaleOrderDTO dto) {
BeanUtil.copyProperties(dto, this,"bizdate","plansenddate");
}

View File

@ -55,6 +55,12 @@ public class SaleOrderEntryDO extends BaseDO {
* 数量
*/
private Integer qty;
/**
* 计量单位
*/
private String unit;
/**
* 金额
*/

View File

@ -72,6 +72,17 @@ public class SaleOrderSkuDO extends BaseDO {
* 动态属性 json动态属性
*/
private String propInfo;
/**
* 模板类型-字典 product_draft_template_type 1有模板 2无模板 3尺码唛
*/
private String productTemplateType;
/**
* 尺码唛的 尺码信息
*/
private String labelSize;
/**
* 规格 json完整信息
*/

View File

@ -7,6 +7,7 @@ import javax.validation.*;
import cn.hangtag.module.oms.controller.admin.common.vo.DataComparisonRespVO;
import cn.hangtag.module.oms.controller.admin.saleorder.front.dto.CreateSaleOrderDTO;
import cn.hangtag.module.oms.controller.admin.saleorder.front.vo.SaleOrderFollowerUserVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.*;
import cn.hangtag.module.oms.controller.admin.trade.vo.TradeOrderSummaryRespVO;
import cn.hangtag.module.oms.controller.admin.trade.vo.TradeOrderTrendReqVO;
@ -107,9 +108,32 @@ public interface SaleOrderService {
* @return {@link Long }
*/
Long placeOrder(CreateSaleOrderDTO dto);
Long editOrder(Long id,CreateSaleOrderDTO dto);
public String getNewOrderCode();
public String getNewOrderCode2();
/**
* 获取销售订单编码
*
* @return {@link String }
*/
String getNewOrderCode();
/**
* 获取销售合同编码
*
* @return {@link String }
*/
String getNewContractCode2();
List<SaleOrderSkuDO> getSaleOrderSkuEntryListByEntryId(Long entryId);
List<SaleOrderFollowerUserVO> queryFollowerUserListByBrandId(String brandId);
/**
* 查询编辑订单
*
* @param id ID
* @return {@link CreateSaleOrderDTO }
*/
CreateSaleOrderDTO queryEditOrder(Long id);
}

View File

@ -4,14 +4,12 @@ import cn.hangtag.framework.common.exception.ServiceException;
import cn.hangtag.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.util.FuncUtil;
import cn.hangtag.framework.common.util.http.HttpUtils;
import cn.hangtag.framework.common.util.object.BeanUtils;
import cn.hangtag.framework.common.util.validation.AssertUtil;
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
import cn.hangtag.framework.security.core.LoginUser;
import cn.hangtag.framework.security.core.util.SecurityFrameworkUtils;
import cn.hangtag.module.oms.common.utils.NumberChineseFormatterUtils;
import cn.hangtag.module.oms.common.utils.WKHtmlToPdfUtil;
import cn.hangtag.module.oms.controller.admin.common.vo.DataComparisonRespVO;
import cn.hangtag.module.oms.controller.admin.produceorder.vo.ProduceOrderSaveReqVO;
import cn.hangtag.module.oms.controller.admin.product.vo.ProductPriceSaveReqVO;
@ -19,6 +17,7 @@ import cn.hangtag.module.oms.controller.admin.salecontract.vo.SaleContractSaveRe
import cn.hangtag.module.oms.controller.admin.saleorder.front.dto.CreateSaleOrderDTO;
import cn.hangtag.module.oms.controller.admin.saleorder.front.dto.SaleOrderEntryItemDTO;
import cn.hangtag.module.oms.controller.admin.saleorder.front.dto.SaleOrderSkuDTO;
import cn.hangtag.module.oms.controller.admin.saleorder.front.vo.SaleOrderFollowerUserVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderPageReqVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderRemarkReqVO;
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderSaveReqVO;
@ -50,6 +49,7 @@ import cn.hangtag.module.oms.service.produceorder.ProduceOrderService;
import cn.hangtag.module.oms.service.product.ProductPriceService;
import cn.hangtag.module.oms.service.productinfo.ProductInfoService;
import cn.hangtag.module.oms.service.salecontract.SaleContractService;
import cn.hangtag.module.oms.service.saleordersku.SaleOrderSkuService;
import cn.hangtag.module.system.mq.message.mail.MailSendMessage;
import cn.hangtag.module.system.service.mail.MailSendService;
import cn.hutool.core.bean.BeanUtil;
@ -62,6 +62,7 @@ import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Maps;
@ -75,7 +76,6 @@ import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.*;
@ -83,10 +83,7 @@ import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@ -112,6 +109,9 @@ public class SaleOrderServiceImpl implements SaleOrderService {
private SaleOrderEntryMapper saleOrderEntryMapper;
@Resource
private SaleOrderSkuMapper skuOrderSkuMapper;
@Resource
private SaleOrderSkuService saleOrderSkuService;
@Resource
private ProduceOrderService produceOrderService;
@Resource
@ -638,18 +638,19 @@ public class SaleOrderServiceImpl implements SaleOrderService {
.materialId(itemDTO.getProductId())
.materialName(itemDTO.getProductName())
.qty(itemDTO.getOrderQty())
.deliveryDate(FuncUtil.parseDate(itemDTO.getDeliveryDate())).build();
.deliveryDate(FuncUtil.timeToLocalDate(itemDTO.getDeliveryDate())).build();
entryList.add(entry);
List<SaleOrderSkuDTO> productSkuList = itemDTO.getProductSkuList();
// sku
for (SaleOrderSkuDTO saleOrderSkuDTO : productSkuList) {
SaleOrderSkuDO saleOrderSkuDO = new SaleOrderSkuDO();
BeanUtil.copyProperties(saleOrderSkuDTO,saleOrderSkuDO);
BeanUtil.copyProperties(saleOrderSkuDTO,saleOrderSkuDO,"specInfo");
saleOrderSkuDO.setSaleOrderId(order.getId());
saleOrderSkuDO.setEntryId(entry.getId());
saleOrderSkuDO.setProductTemplateType(saleOrderSkuDTO.getProductTemplateType());
saleOrderSkuDO.setSpecInfo(JSONUtil.toJsonStr(saleOrderSkuDTO.getSpecInfo()));
saleOrderSkuDO.setId(FuncUtil.getNextId());
skuList.add(saleOrderSkuDO);
}
@ -662,6 +663,60 @@ public class SaleOrderServiceImpl implements SaleOrderService {
System.out.println(order);
return order.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long editOrder(Long id,CreateSaleOrderDTO dto) {
SaleOrderDO saleOrderDO = checkEditOrder(id);
// 校验订单
SaleOrderDO order = new SaleOrderDO(dto);
order.setId(saleOrderDO.getId());
order = wrapperEntity(order, dto);
// 不进行修改
order.setOrderCode(saleOrderDO.getOrderCode());
order.setContractCode(saleOrderDO.getContractCode());
order.setBillno(saleOrderDO.getBillno());
order.setCustomerId(saleOrderDO.getCustomerId());
order.setBizdate(saleOrderDO.getBizdate());
List<SaleOrderEntryItemDTO> saleOrderEntry = dto.getSaleOrderEntry();
List<SaleOrderEntryDO> entryList = new ArrayList<>();
List<SaleOrderSkuDO> skuList = new ArrayList<>();
// 删除之前的的订单条目
deleteSaleOrderEntryByParentId(order.getId());
saleOrderSkuService.delSkuByOrderId(order.getId());
// 转成订单条目
for (SaleOrderEntryItemDTO itemDTO : saleOrderEntry) {
SaleOrderEntryDO entry = SaleOrderEntryDO.builder()
.id(FuncUtil.getNextId())
.parentId(order.getId())
.materialId(itemDTO.getProductId())
.materialName(itemDTO.getProductName())
.qty(itemDTO.getOrderQty())
.deliveryDate(FuncUtil.timeToLocalDate(itemDTO.getDeliveryDate())).build();
entryList.add(entry);
List<SaleOrderSkuDTO> productSkuList = itemDTO.getProductSkuList();
// sku
for (SaleOrderSkuDTO saleOrderSkuDTO : productSkuList) {
SaleOrderSkuDO saleOrderSkuDO = new SaleOrderSkuDO();
BeanUtil.copyProperties(saleOrderSkuDTO,saleOrderSkuDO,"specInfo");
saleOrderSkuDO.setSaleOrderId(order.getId());
saleOrderSkuDO.setEntryId(entry.getId());
saleOrderSkuDO.setProductTemplateType(saleOrderSkuDTO.getProductTemplateType());
saleOrderSkuDO.setSpecInfo(JSONUtil.toJsonStr(saleOrderSkuDTO.getSpecInfo()));
saleOrderSkuDO.setId(FuncUtil.getNextId());
skuList.add(saleOrderSkuDO);
}
}
saleOrderMapper.updateById(order);
saleOrderEntryMapper.insertBatch(entryList);
skuOrderSkuMapper.insertBatch(skuList);
updateCustomerInvoiceData(order);
return order.getId();
}
private static final long codeId = 6L;
private static final long saleContractCodeId = 7L;
@Override
@ -684,14 +739,14 @@ public class SaleOrderServiceImpl implements SaleOrderService {
}
}
@Override
public String getNewOrderCode2() {
public String getNewContractCode2() {
String s = "";
int count = 10;
while (true){
count --;
try {
s = CodingRulesUtils.generateCode(saleContractCodeId, false);
checkCode2(null,s);
checkSaleContractCode2(null,s);
return s;
}catch (ServiceException e){
log.warn("重复或者下一个编码");
@ -724,7 +779,7 @@ public class SaleOrderServiceImpl implements SaleOrderService {
}
private void checkCode2(Long id,String code){
private void checkSaleContractCode2(Long id, String code){
if(FuncUtil.isNotEmpty(code)){
LambdaQueryWrapper<SaleContractDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.select(SaleContractDO::getId,SaleContractDO::getBillno, BaseDO::getDeleted);
@ -756,7 +811,7 @@ public class SaleOrderServiceImpl implements SaleOrderService {
order.setId(FuncUtil.getNextId());
}
order.setBizdate(LocalDateTime.now());
order.setPlansenddate( FuncUtil.parseDate(dto.getPlansenddate()));
order.setPlansenddate( FuncUtil.timeToLocalDate(dto.getPlansenddate()));
// 设置订单状态
order.setOrderStatus(SaleOrderStatusEnum.YXD.getValue());
order.setBillStatus(BillStatusEnum.SUBMIT.getValue());
@ -859,7 +914,12 @@ public class SaleOrderServiceImpl implements SaleOrderService {
private SaleContractSaveReqVO toSaleContractVO(SaleOrderDO saleOrder, List<SaleOrderEntryDO> entrys){
CustomerDO customer = customerService.getCustomer(saleOrder.getCustomerId());
SaleContractSaveReqVO saveReqVO = new SaleContractSaveReqVO();
saveReqVO.setBillno(getNewOrderCode2());
// 优先使用销售订单合同号
String billno = saleOrder.getContractCode();
if(FuncUtil.isEmpty(billno)){
billno = getNewContractCode2();
}
saveReqVO.setBillno(billno);
saveReqVO.setCustomerId(saleOrder.getCustomerId());
saveReqVO.setCustomerName(customer.getName());
saveReqVO.setCustomerBuyNo(saleOrder.getBillno());
@ -896,6 +956,65 @@ public class SaleOrderServiceImpl implements SaleOrderService {
return skuOrderSkuMapper.selectListByEntryId(entryId);
}
@Override
public List<SaleOrderFollowerUserVO> queryFollowerUserListByBrandId(String brandId) {
// TODO 查询品牌下的所有跟单人员
List<SaleOrderFollowerUserVO> res = new ArrayList<>();
// 测试数据
res.add(new SaleOrderFollowerUserVO().setId(1L).setUsername("admin"));
return res;
}
@Override
public CreateSaleOrderDTO queryEditOrder(Long id) {
CreateSaleOrderDTO res = new CreateSaleOrderDTO();
SaleOrderDO saleOrderDO = checkEditOrder(id);
BeanUtil.copyProperties(saleOrderDO, res);
LambdaQueryWrapper<SaleOrderEntryDO> entryQueryWrapper = new LambdaQueryWrapper<>();
entryQueryWrapper.eq(SaleOrderEntryDO::getParentId, saleOrderDO.getId());
entryQueryWrapper.eq(SaleOrderEntryDO::getDeleted, false);
List<SaleOrderEntryDO> entryDOS = saleOrderEntryMapper.selectList(entryQueryWrapper);
List<SaleOrderEntryItemDTO> saleOrderEntry = new ArrayList<>();
for (SaleOrderEntryDO entryDO : entryDOS) {
SaleOrderEntryItemDTO dto = new SaleOrderEntryItemDTO(entryDO);
// 获取产品信息
Long productId = dto.getProductId();
ProductInfoDO productInfo = productInfoService.getProductInfo(productId);
dto.setCover(productInfo.getCover());
dto.setProductCode(productInfo.getCode());
dto.setProductName(entryDO.getMaterialName());
dto.setProducer(productInfo.getBrandName());
dto.setCover(productInfo.getCover());
dto.setDetails(productInfo.getDetails());
dto.setUnit(entryDO.getUnit());
// productId: row.id,
// productCode: row.code,
// productName: row.name,
// producer: row.producer,
// cover: row.cover,
// unit: row.unit || 'pcs',
// sku list
LambdaQueryWrapper<SaleOrderSkuDO> skuWrapper = new LambdaQueryWrapper<>();
skuWrapper.eq(SaleOrderSkuDO::getSaleOrderId, saleOrderDO.getId());
skuWrapper.eq(SaleOrderSkuDO::getDeleted, false);
List<SaleOrderSkuDO> sukList = skuOrderSkuMapper.selectList(skuWrapper);
List<SaleOrderSkuDTO> skus = new ArrayList<>();
for (SaleOrderSkuDO saleOrderSkuDO : sukList) {
SaleOrderSkuDTO toSaleOrderSkuDTO = new SaleOrderSkuDTO(saleOrderSkuDO);
skus.add(toSaleOrderSkuDTO);
}
dto.setProductSkuList(skus);
saleOrderEntry.add(dto);
}
res.setSaleOrderEntry(saleOrderEntry);
return res;
}
/**
* 下载ZIP压缩包(会对下载后的压缩包进行删除)
*
@ -933,4 +1052,16 @@ public class SaleOrderServiceImpl implements SaleOrderService {
}
}
private SaleOrderDO checkEditOrder(Long id){
SaleOrderDO saleOrderDO = saleOrderMapper.selectById(id);
AssertUtil.isEmpty(saleOrderDO, "订单不存在");
String billStatus = saleOrderDO.getBillStatus();
boolean order1 = BillStatusEnum.isCanEditOrder(billStatus);
BillStatusEnum byValue = BillStatusEnum.getByValue(billStatus);
AssertUtil.isEmpty(byValue, "订单状态异常");
AssertUtil.isTrue(!order1,byValue.getName()+"状态不允许修改" );
return saleOrderDO;
}
}

View File

@ -1,11 +1,9 @@
package cn.hangtag.module.oms.service.saleordersku;
import java.util.*;
import javax.validation.*;
import cn.hangtag.module.oms.controller.admin.saleordersku.vo.*;
import cn.hangtag.module.oms.dal.dataobject.saleordersku.SaleOrderSkuDO;
import cn.hangtag.framework.common.pojo.PageResult;
import cn.hangtag.framework.common.pojo.PageParam;
/**
* 销售订单产品属性表 Service 接口
@ -36,6 +34,13 @@ public interface SaleOrderSkuService {
*/
void deleteSaleOrderSku(Long id);
/**
* 通过销售订单ID删除销售订单SKU
* oms_saleorder id
* @param id ID
*/
int delSkuByOrderId(Long id);
/**
* 获得销售订单产品属性表
*

View File

@ -1,5 +1,6 @@
package cn.hangtag.module.oms.service.saleordersku;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
@ -55,6 +56,20 @@ public class SaleOrderSkuServiceImpl implements SaleOrderSkuService {
saleOrderSkuMapper.deleteById(id);
}
@Override
public int delSkuByOrderId(Long orderId) {
LambdaQueryWrapper<SaleOrderSkuDO> skuWrapper = new LambdaQueryWrapper<>();
skuWrapper.eq(SaleOrderSkuDO::getSaleOrderId, orderId);
skuWrapper.eq(SaleOrderSkuDO::getDeleted, false);
List<SaleOrderSkuDO> sukList = saleOrderSkuMapper.selectList(skuWrapper);
List<Long> skus = new ArrayList<>();
for (SaleOrderSkuDO saleOrderSkuDO : sukList) {
skus.add(saleOrderSkuDO.getId());
}
return saleOrderSkuMapper.deleteBatchIds(skus);
}
private void validateSaleOrderSkuExists(Long id) {
if (saleOrderSkuMapper.selectById(id) == null) {
throw exception(SALE_ORDER_SKU_NOT_EXISTS);

View File

@ -12,8 +12,8 @@
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="updateVisible(false)"> </el-button>
<el-button type="primary" @click="submit">批稿</el-button>
<el-button @click="updateVisible(false)">{{t("designInfo.cancelText")}}</el-button>
<el-button type="primary" @click="submit">{{ t("designInfo.auditSubmitText") }}</el-button>
</span>
</template>
</Dialog>
@ -24,7 +24,8 @@ import {ElAlert} from "element-plus";
import {reactive, computed, watch} from 'vue'
import {calculateVectorDifference} from "@/components/DraftDesign/utils/FuncUtil";
import {useMessage} from "@/hooks/web/useMessage";
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const emit = defineEmits(['update:visible', 'submit'])
const designPropEditRef = ref();
const that = reactive({
@ -61,7 +62,7 @@ const previewByDraftDesignId = (id: string | number,prop={})=>{
},100)
}
const submit = ()=>{
useMessage().confirm("是否提交?").then(async (r) => {
useMessage().confirm(t('designInfo.auditTips')).then(async (r) => {
const res = await designPropEditRef.value.getPropInfo()
emit("submit",res);
updateVisible(false)

View File

@ -1,88 +1,82 @@
<!-- eslint-disable vue/this-in-template -->
<template>
<div>
<div
:class ="{ 'hidden-div': that.hideCreate}"
:id="that.svgId"
:style="{
width: `${imageSize.width * 6}px`,
height: `${imageSize.height * 6}px`,
}">
<div v-if="that.previewUrl" v-html="that.previewUrl" >
</div>
</div>
<div style="max-height: 90vh; overflow:auto;" v-loading="loading">
<div>
<div style="max-height: 90vh; overflow:auto;" v-loading="loading">
<span>
仅供参考(for reference only)
</span>
<div style="padding-bottom: 4px">
<el-checkbox style="margin: 8px" v-model="that.reView">实时预览</el-checkbox>
<el-button
v-show="!that.reView"
style="margin: 8px"
type="primary"
size="small"
@click="updateDesign">
<span>更新</span>
<span
style="color: #ff0000;font-size: 20px; padding-left: 4px">{{
that.changeCount > 0 ? '*' : ''
}}</span>
</el-button>
<div style="padding-bottom: 4px">
<el-checkbox style="margin: 8px" v-model="that.reView">实时预览</el-checkbox>
<el-button
v-show="!that.reView"
style="margin: 8px"
type="primary"
size="small"
@click="updateDesign">
<span>更新</span>
<span
style="color: #ff0000;font-size: 20px; padding-left: 4px">{{
that.changeCount > 0 ? '*' : ''
}}</span>
</el-button>
</div>
<div>
<el-alert
title="按住鼠标左键进行拖动按住Ctrl 滚动鼠标进行缩小放放大"
type="info" effect="dark"/>
</div>
<div v-if="that.sizeInfo" :title="`${that.sizeInfo.width}mm`">
</div>
<div>
<el-alert
title="按住鼠标左键进行拖动按住Ctrl 滚动鼠标进行缩小放放大"
type="info" effect="dark"/>
</div>
<div v-if="that.sizeInfo" :title="`${that.sizeInfo.width}mm`">
<span>
:{{ that.sizeInfo.width }}mm {{ that.sizeInfo.height }}
</span>
</div>
</div>
<el-row>
<el-col :span="11">
<div
:class ="{ 'hidden-div': false }" style="width:100%;height: 580px; border: 1px solid #090805">
<DraftDesign ref="draftDesignEditRef" @init-succeed="initSucceed"/>
</div>
</el-col>
<el-col :span="12">
<div style="display: flex;">
<el-row>
<el-col :span="11">
<div
:class="{ 'hidden-div': false }"
style="width:100%;height: 580px; border: 1px solid #090805">
<DraftDesign ref="draftDesignEditRef" @init-succeed="initSucceed"/>
</div>
</el-col>
<el-col :span="12">
<div style="display: flex;">
<div class="flex flex-col">
<div>
<el-form label-width="180px">
<el-form-item label="风格样式" v-show="that.draftDesignList.length > 1">
<div class="flex ml-3">
<div>
<el-select class="min-w-100" v-model="that.draftDesignId" @change="changeType">
<el-option
v-for="(item) in that.draftDesignList"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
</div>
</div>
</el-form-item>
</el-form>
</div>
<div style="width: 100%" v-if="that.propOrderByList && that.propOrderByList.length > 0">
<el-scrollbar height="600px">
<el-form label-width="180px">
<el-form-item
v-for="(tmp) in that.propOrderByList"
:key="tmp.key"
:label="getLabelName(that.propInfo[tmp.key])">
<div
v-if="that.propInfo[tmp.key].multiLanguage && that.propInfo[tmp.key].shape === ShapeType.vueTextCell">
<div
style="padding: 4px">
<div style="display: flex;align-items: center;">
<div class="flex flex-col">
<div>
<el-form label-width="180px">
<el-form-item label="风格样式" v-show="that.draftDesignList.length > 1">
<div class="flex ml-3">
<div>
<el-select class="min-w-100" v-model="that.draftDesignId"
@change="changeType">
<el-option
v-for="(item) in that.draftDesignList"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
</div>
</div>
</el-form-item>
</el-form>
</div>
<div style="width: 100%"
v-if="that.propOrderByList && that.propOrderByList.length > 0">
<el-scrollbar height="600px">
<el-form label-width="180px">
<el-form-item
v-for="(tmp) in that.propOrderByList"
:key="tmp.key"
:label="getLabelName(that.propInfo[tmp.key])">
<div
v-if="that.propInfo[tmp.key].multiLanguage && that.propInfo[tmp.key].shape === ShapeType.vueTextCell">
<div
style="padding: 4px">
<div style="display: flex;align-items: center;">
<span>
<i
v-if="that.propInfo[tmp.key].canInput"
@ -91,56 +85,56 @@
<i v-else class="icon-lk_cell_add" style="font-size: 20px"> </i>
</span>
<el-select-v2
v-model="that.propInfo[tmp.key].dataInfo[0].showLabel"
filterable
:options="getIngredientInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeData(-1,tmp.key)"
size="large"
style="min-width: 280px;width: 100%"
/>
<div v-if="that.propInfo[tmp.key].groupType === '1'">
<div
style="display: flex;align-items: center; margin-left: 10px; width: 220px">
<el-row>
<el-col span="4">
<div>占比</div>
</el-col>
<el-col span="14">
<el-input-number
:min="-1" :max="100"
v-model="that.propInfo[tmp.key].dataInfo[0].ratio"
@change="changeData(0,tmp.key)"
/>
</el-col>
<el-col span="4">
<div> %</div>
</el-col>
</el-row>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
</div>
<el-select-v2
v-model="that.propInfo[tmp.key].dataInfo[0].showLabel"
filterable
:options="getIngredientInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeData(-1,tmp.key)"
size="large"
style="min-width: 280px;width: 100%"
/>
<div v-if="that.propInfo[tmp.key].groupType === '1'">
<div
style="display: flex;align-items: center; margin-left: 10px; width: 220px">
<el-row>
<el-col span="4">
<div>占比</div>
</el-col>
<el-col span="14">
<el-input-number
:min="-1" :max="100"
v-model="that.propInfo[tmp.key].dataInfo[0].ratio"
@change="changeData(0,tmp.key)"
/>
</el-col>
<el-col span="4">
<div> %</div>
</el-col>
</el-row>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
<div v-else-if="!that.propInfo[tmp.key].isCombo">
<div
v-for="(text,index) in that.propInfo[tmp.key].dataInfo"
:key="index"
style="padding: 4px">
<div style="display: flex;align-items: center;">
<div v-else-if="!that.propInfo[tmp.key].isCombo">
<div
v-for="(text,index) in that.propInfo[tmp.key].dataInfo"
:key="index"
style="padding: 4px">
<div style="display: flex;align-items: center;">
<span>
<i
v-if="that.propInfo[tmp.key].canInput"
@ -148,119 +142,154 @@
style="font-size: 20px"> </i>
<i v-else class="icon-lk_cell_add" style="font-size: 20px"> </i>
</span>
<el-autocomplete
v-if="that.propInfo[tmp.key].canInput"
v-model="that.propInfo[tmp.key].dataInfo[index].showLabel"
:fetch-suggestions="querySearch"
clearable
class="inline-input w-50"
placeholder="Please Input"
@change="changeData"
@select="changeData"
/>
<el-autocomplete
v-if="that.propInfo[tmp.key].canInput"
v-model="that.propInfo[tmp.key].dataInfo[index].showLabel"
:fetch-suggestions="querySearch"
clearable
class="inline-input w-50"
placeholder="Please Input"
@change="changeData"
@select="changeData"
/>
<el-select-v2
v-else
v-model="that.propInfo[tmp.key].dataInfo[index].showLabel"
filterable
:options="getIngredientInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeData(index,tmp.key)"
size="large"
style="min-width: 280px;width: 100%"
/>
<div v-if="that.propInfo[tmp.key].groupType === '1'">
<el-select-v2
v-else
v-model="that.propInfo[tmp.key].dataInfo[index].showLabel"
filterable
:options="getIngredientInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeData(index,tmp.key)"
size="large"
style="min-width: 280px;width: 100%"
/>
<div v-if="that.propInfo[tmp.key].groupType === '1'">
<div
style="display: flex;align-items: center; margin-left: 10px; width: 220px">
<el-row>
<el-col span="4">
<div>占比</div>
</el-col>
<el-col span="14">
<el-input-number
:min="-1" :max="100"
v-model="that.propInfo[tmp.key].dataInfo[index].ratio"
@change="changeData(index,tmp.key)"
/>
</el-col>
<el-col span="4">
<div> %</div>
</el-col>
</el-row>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
</div>
<div
style="display: flex;align-items: center; margin-left: 10px; width: 220px">
<el-row>
<el-col span="4">
<div>占比</div>
</el-col>
<el-col span="14">
<el-input-number
:min="-1" :max="100"
v-model="that.propInfo[tmp.key].dataInfo[index].ratio"
@change="changeData(index,tmp.key)"
/>
</el-col>
<el-col span="4">
<div> %</div>
</el-col>
</el-row>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
<div v-else-if="that.propInfo[tmp.key].shape === ShapeType.vueShapeImage">
<div
v-for="(img,index) in that.propInfo[tmp.key].dataInfo"
:key="index" style="display: flex">
<div class="img-box" v-if="img.url">
<div style="display: flex">
<img :src="img.url" width="60px" height="60px"/>
<el-select-v2
v-model="that.propInfo[tmp.key].dataInfo[0].showLabel"
filterable
:options="washingInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeComboData(that.propInfo[tmp.key],that.propInfo[tmp.key].dataInfo[0].showLabel)"
size="large"
style="min-width: 280px;width: 100%"
>
<template #default="{ item }">
<div style="display: flex;">
<img :src="item.url" width="30px" height="30px"/>
<span>
<div v-else-if="that.propInfo[tmp.key].shape === ShapeType.vueShapeImage">
<div
v-for="(img,index) in that.propInfo[tmp.key].dataInfo"
:key="index" style="display: flex">
<div class="img-box" v-if="img.url">
<div style="display: flex">
<img :src="img.url" width="60px" height="60px"/>
<el-select-v2
v-model="that.propInfo[tmp.key].dataInfo[0].showLabel"
filterable
:options="washingInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeComboData(that.propInfo[tmp.key],that.propInfo[tmp.key].dataInfo[0].showLabel)"
size="large"
style="min-width: 280px;width: 100%"
>
<template #default="{ item }">
<div style="display: flex;">
<img :src="item.url" width="30px" height="30px"/>
<span>
{{ item.label }}
</span>
</div>
</template>
</el-select-v2>
</div>
</div>
<div v-else-if="img.label">
<el-input v-model="img.label" disabled/>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
</template>
</el-select-v2>
</div>
</div>
<div v-else-if="img.label">
<el-input v-model="img.label" disabled/>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
</div>
</el-form-item>
</el-form>
</el-scrollbar>
</div>
</div>
</el-form-item>
</el-form>
</el-scrollbar>
</div>
</div>
</div>
</el-col>
</div>
</el-col>
</el-row>
</div>
</div>
</el-row>
</div>
<div
:class ="{ 'hidden-div': that.hideCreate}"
:id="that.svgId"
:style="{
position: that.hideCreate ? 'fixed' : 'relative',
display: `flex`,
flexDirection: `column`,
width: `${previewSize.width}px`,
height: `${previewSize.height}px`,
}">
<div v-if="that.previewUrl"
:style="{
width: `${that.svgWidth+2}px`,
height: `${that.svgHeight+2}px`,
backgroundColor: (that.pageConfig.background && that.pageConfig.background.areaColor) || '',
border: '1px solid #00ff00',
position: `absolute`,
top: `0`,
left: `0`,
}"
>
<div>
<div v-if="false" style="color: #ff0000;">w:{{imageSize.width}} h:{{imageSize.height}}</div>
<span style="color: #ff0000;writing-mode: vertical-rl; text-orientation: upright;">
*仅供参考
</span>
</div>
</div>
<div
v-if="that.previewUrl" v-html="that.previewUrl" >
</div>
</div>
</div>
</template>
<script lang="ts" setup name="DynamicPropConfig">
@ -278,13 +307,14 @@ import {useLocaleStore} from "@/store/modules/locale";
import {ProductInfoApi} from "@/api/oms/productinfo";
import domtoimage from 'dom-to-image';
import {usePageLoading} from "@/hooks/web/usePageLoading";
const { loadStart, loadDone } = usePageLoading()
const {loadStart, loadDone} = usePageLoading()
//
const localeStore = useLocaleStore()
const emit = defineEmits(['change', 'initSucceed'])
const draftDesignEditRef = ref()
const that = reactive({
svgId: "svg_"+Math.random().toString(36).substring(2),
svgId: "svg_" + Math.random().toString(36).substring(2),
hideCreate: true,
pageLoading: {},
propInfo: {
@ -305,7 +335,7 @@ const that = reactive({
draftDesignList: [],
draftDesignId: '',
reView: false,
currentZoom: 6,
currentZoom: 10,
changeCount: 0,
previewUrl: "",
previewData: '',
@ -318,8 +348,16 @@ const that = reactive({
},
data: {},
show: false,
svgHeight: 0,
svgWidth: 0,
})
const previewSize = computed(()=>{
return {
width: imageSize.value.width * 10,
height: imageSize.value.height * 10
}
})
const currentLocale = computed(() => localeStore.getCurrentLocale)
watch(() => that.currentZoom, () => {
showPng();
@ -331,8 +369,8 @@ const querySearch = (queryString: string, cb: any) => {
}) : that.ingredientInfoList
cb(results)
}
const imageSize =computed(()=>{
if(that.sizeInfo){
const imageSize = computed(() => {
if (that.sizeInfo) {
return {
width: that.sizeInfo.width,
height: that.sizeInfo.height
@ -383,7 +421,6 @@ const changeData = (index: number, key: string) => {
str = ''
}
const value = `${mapping[j].value}`.replaceAll('${r}', str)
console.log("value",value)
that.propInfo[key].dataInfo[i].label = value
break;
}
@ -447,27 +484,33 @@ const getIngredientInfoListByType = (type) => {
return that.ingredientInfoList.filter(item => item.type === type)
}
const uploadFile = async (fileName) => {
return new Promise((resolve, reject)=>{
return new Promise((resolve, reject) => {
let svgElement = document.getElementById(that.svgId);
domtoimage.toBlob(svgElement).then((blob) => {
FileApi.updateFile({ file: blob })
.then((res) => {
if (res.code === 0) {
resolve({
...res,
filename: fileName
})
} else {
reject(res)
}
}) .catch((res) => {
reject(res)
})
}).catch(function (error) {
console.error('转换或上传过程中出现错误:', error);
});
});
let svgElement = document.getElementById(that.svgId);
domtoimage.toBlob(svgElement,{
width: that.svgWidth+5,
height: that.svgHeight+5,
type: 'image/jpeg',
quality: 1
}).then((blob) => {
const file = new File([blob], fileName, { type: 'image/jpeg' });
FileApi.updateFile({file: file})
.then((res) => {
if (res.code === 0) {
resolve({
...res,
filename: fileName
})
} else {
reject(res)
}
}).catch((res) => {
reject(res)
})
}).catch(function (error) {
console.error('转换或上传过程中出现错误:', error);
});
});
}
const deleteList = (item, index) => {
@ -629,7 +672,6 @@ const updateDesign = () => {
const changeType = () => {
previewByDraftDesignId(that.draftDesignId, that.propInfo)
}
const getPropInfo = () => {
that.pageLoading = ElLoading.service({
@ -643,26 +685,42 @@ const getPropInfo = () => {
draftDesignEditRef.value.toSVGData((url) => {
that.previewUrl = url
that.hideCreate = false;
//
const date = new Date();
const randChar = Math.random().toString(36).slice(-6);
const fileName = `${date.getFullYear()}${ (date.getMonth() + 1)}${ date.getDate()}_${that.draftDesignId }_${randChar}_稿.png`;
//
uploadFile(fileName).then(res =>{
that.previewData = res.data;
that.hideCreate = true;
that.previewUrl =""
const info = draftDesignEditRef.value.getPropInfo();
that.pageLoading.close()
resolve({
draftDesignData: draftDesignEditRef.value.getJson(),
propOrderByList: info.propOrderByList,
// 稿id
draftDesignId: that.draftDesignId,
previewImage: that.previewData,
propInfo: that.propInfo
setTimeout(()=>{
const el = document.querySelector(`#${that.svgId} g`);
el.setAttribute("transform",'matrix(1,0,0,1,0,0)');
document.querySelector(`#${that.svgId} svg`).style.position = 'absolute';
document.querySelector(`#${that.svgId} svg`).style.top = '-40%';
const dom = document.querySelector(`#${that.svgId} svg .x6-graph-svg-viewport`).getBoundingClientRect()
const svgDom = document.querySelector(`#${that.svgId} svg`).getBoundingClientRect()
that.svgHeight = dom.height;
that.svgWidth = dom.width;
document.querySelector(`#${that.svgId} svg`).style.left = `${svgDom.left - dom.left}px`;
document.querySelector(`#${that.svgId} svg`).style.top = `${svgDom.top - dom.top}px`;
// that.pageLoading.close()
//
const date = new Date();
const randChar = Math.random().toString(36).slice(-6);
const fileName = `${date.getFullYear()}${(date.getMonth() + 1)}${date.getDate()}_${that.draftDesignId}_${randChar}_稿.png`;
//
uploadFile(fileName).then(res => {
that.previewData = res.data;
that.hideCreate = true;
that.previewUrl = ""
const info = draftDesignEditRef.value.getPropInfo();
that.pageLoading.close()
resolve({
draftDesignData: draftDesignEditRef.value.getJson(),
propOrderByList: info.propOrderByList,
// 稿id
draftDesignId: that.draftDesignId,
previewImage: that.previewData,
propInfo: that.propInfo
})
})
})
}, {
widthScale: 6,
heightScale: 6

View File

@ -727,6 +727,7 @@ const toSVGData = (callback: (url: string) => void, options?: {
height: number,
widthScale: number,
heightScale: number,
backgroundColor: String,
padding: {
top: number,
left: number,
@ -735,23 +736,23 @@ const toSVGData = (callback: (url: string) => void, options?: {
}
}) => {
let widthScale = options && options.widthScale ? options.widthScale : 5
let heightScale = options && options.heightScale ? options.heightScale : 5
let widthScale = 20 // options && options.widthScale ? options.widthScale : 5
let heightScale = 20 // options && options.heightScale ? options.heightScale : 5
graph.toSVG((dataUri) => {
that.previewUrl = dataUri;
callback(that.previewUrl)
}, {
copyStyles: true,
viewBox: {
x: 0,
y: 0,
width: that.pageConfig.width * (widthScale/2),
height: that.pageConfig.height * (heightScale/2),
},
preserveDimensions: {
width: that.pageConfig.width * (widthScale),
height: that.pageConfig.height * (heightScale),
},
preserveDimensions:{
width: that.pageConfig.width * (widthScale),
height: that.pageConfig.height * (heightScale),
},
}
})
}
const setPropList = (propDataInfo) => {

View File

@ -105,6 +105,7 @@ export enum DICT_TYPE {
COMMON_STATUS = 'common_status',
TERMINAL = 'terminal', // 终端
LANGUAGE_LOCALE = 'language_locale', // 地区语言标识
PRODUCT_DRAFT_TEMPLATE_TYPE = 'product_draft_template_type', // 地区语言标识
BRAND_INDUSTRY_FIELD = 'brand_industry_field',
DATE_INTERVAL = 'date_interval', // 数据间隔
// ========== SYSTEM 模块 ==========

View File

@ -90,7 +90,22 @@
</el-form-item>
</el-col>
</el-row>
<el-form-item label="关联设计稿" prop="draftDesignDataId">
<el-row>
<el-col :span="12" :xs="24">
<el-form-item label="模板类型" prop="templateType">
<el-select v-model="formData.templateType" placeholder="模板类型">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.PRODUCT_DRAFT_TEMPLATE_TYPE)"
:key="`${dict.value}`"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="关联设计稿" v-if="formData.templateType === '1'" prop="draftDesignDataId">
<div style="width: calc(100% - 20px);">
<el-button @click="addRow">添加</el-button>
<el-scrollbar max-height="400px">
@ -98,6 +113,7 @@
<el-table
:data="that.draftDesignList"
:stripe="true"
empty-text="无设计稿,点击添加进行关联设计稿"
:show-overflow-tooltip="true">
<el-table-column width="55">
<template #header>
@ -247,6 +263,7 @@ import {ProductInfoApi, ProductInfoVO} from '@/api/oms/productinfo'
import {ProductProcessApi} from '@/api/oms/productprocess'
import DraftDesignDataListDialog from "@/components/Dialog/src/DraftDesignDataListDialog/index.vue";
import BrandDataListDialog from "@/components/Dialog/src/BrandDataListDialog/index.vue";
import {DICT_TYPE, getStrDictOptions} from "@/utils/dict";
/** 产品资料 表单 */
defineOptions({name: 'ProductInfoForm'})
@ -275,13 +292,14 @@ const formData = ref({
specSizeHeight: 0,
specSizeThk: 0,
specMaterial: '',
templateType: '1',
})
const that = reactive({
brandId: undefined,
draftDesignList: [{
remark: '',
label: '',
label: '默认',
id: ''
}],
updateProcess: false,
@ -404,10 +422,6 @@ const submitForm = async () => {
//
formLoading.value = true
try {
if (that.draftDesignList.length === 0) {
message.error('请选择关联的设计稿')
return;
}
// 稿
let countInfo = {};
let ids = [];
@ -458,12 +472,14 @@ const submitForm = async () => {
data.draftDesignList = JSON.stringify(that.draftDesignList)
// 稿
data.draftDesignDataId = ids.join(",");
if(ids.length > 0){
data.draftDesignDataId = ids.join(",");
}
if (formType.value === 'create') {
await ProductInfoApi.createProductInfo(data as ProductInfoVO)
await ProductInfoApi.createProductInfo(data)
message.success(t('common.createSuccess'))
} else {
await ProductInfoApi.updateProductInfo(data as ProductInfoVO)
await ProductInfoApi.updateProductInfo(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
@ -510,12 +526,9 @@ const resetForm = () => {
specSizeHeight: 0,
specSizeThk: 0,
specMaterial: '',
templateType: '1',
}
that.draftDesignList = [{
remark: '',
label: '默认',
id: ''
}]
that.draftDesignList = []
that.processInfoList =[{
key: Math.random().toString(36).substring(2, 6),
id: null, // id

View File

@ -151,7 +151,7 @@
v-hasPermi="['oms:product-info:update']"
:disabled="!scope.row.draftDesignDataId"
>
预览稿件
{{scope.row.draftDesignDataId ? '预览稿件' : '未使用稿件'}}
</el-button>
<el-button
link

View File

@ -25,6 +25,7 @@ export const SaleOrderApi = {
return await request.get({ url: `/oms/app/sale-order/page`, params })
},
// 查询销售订单详情
getSaleOrder: async (id: number) => {
return await request.get({ url: `/oms/app/sale-order/get?id=` + id })
@ -50,6 +51,15 @@ export const SaleOrderApi = {
return await request.download({ url: `/oms/app/sale-order/export-excel`, params })
},
// 获取品牌下 所有跟单员
getFollowerUser: async (brandId:string) => {
return await request.get({ url: `/oms/app/sale-order/follower-user/${brandId}` })
},
// 获取销售合同号
getContractCode: async () => {
return await request.get({ url: `/oms/app/sale-order/contract-code` })
},
// ==================== 子表(销售订单明细) ====================
// 获得销售订单明细列表
@ -109,4 +119,12 @@ export const SaleOrderApi = {
placeOrder: async (data: any) => {
return await request.post({ url: `/front/oms/sale-order/placeOrder`, data })
},
// 查询编辑订单
queryEditById: async (id: string) => {
return await request.get({ url: `/front/oms/sale-order/details/${id}` })
},
// 修改订单
editOrder: async (id: string,data: any) => {
return await request.post({ url: `/front/oms/sale-order/editOrder/${id}`,data})
},
}

View File

@ -29,7 +29,7 @@
import { AddressApi } from '@/api/oms/customer/address'
import { rules, allSchemas } from './config.data'
import {defaultProps} from "@/utils/tree";
import {UPDATE_ADDRESS} from "@/constants/EmitEventName";
import {UPDATE_ADDRESS_EVENT} from "@/constants/EmitEventName";
import {useEmitt} from "@/hooks/web/useEmitt";
const { t } = useI18n() //
const message = useMessage() //
@ -85,7 +85,7 @@ const submitForm = async () => {
dialogVisible.value = false
//
emit('success')
emitter.emit(UPDATE_ADDRESS,{
emitter.emit(UPDATE_ADDRESS_EVENT,{
...data
})
} finally {

View File

@ -126,7 +126,7 @@ import DataForm from './DataForm.vue'
import {AddressApi} from "@/api/oms/customer/address";
import {usePageLoading} from "@/hooks/web/usePageLoading";
import {useEmitt} from "@/hooks/web/useEmitt";
import {UPDATE_ADDRESS} from "@/constants/EmitEventName";
import {UPDATE_ADDRESS_EVENT} from "@/constants/EmitEventName";
/** 稿件图片库 */
defineOptions({name: 'AddressListDialog'})
@ -187,7 +187,7 @@ const setDefault = (id: number)=>{
pageLoading.loadStart();
AddressApi.setDefaultAddress(id).then(res=>{
useMessage().success(t('common.success'))
emitter.emit(UPDATE_ADDRESS,{
emitter.emit(UPDATE_ADDRESS_EVENT,{
id: id
})
getList();

View File

@ -12,8 +12,8 @@
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="updateVisible(false)"> </el-button>
<el-button type="primary" @click="submit">批稿</el-button>
<el-button @click="updateVisible(false)">{{t("designInfo.cancelText")}}</el-button>
<el-button type="primary" @click="submit">{{ t("designInfo.auditSubmitText") }}</el-button>
</span>
</template>
</Dialog>
@ -24,7 +24,8 @@ import {ElAlert} from "element-plus";
import {reactive, computed, watch} from 'vue'
import {calculateVectorDifference} from "@/components/DraftDesign/utils/FuncUtil";
import {useMessage} from "@/hooks/web/useMessage";
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const emit = defineEmits(['update:visible', 'submit'])
const designPropEditRef = ref();
const that = reactive({
@ -61,7 +62,7 @@ const previewByDraftDesignId = (id: string | number,prop={})=>{
},100)
}
const submit = ()=>{
useMessage().confirm("是否提交?").then(async (r) => {
useMessage().confirm(t('designInfo.auditTips')).then(async (r) => {
const res = await designPropEditRef.value.getPropInfo()
emit("submit",res);
updateVisible(false)

View File

@ -1,88 +1,82 @@
<!-- eslint-disable vue/this-in-template -->
<template>
<div>
<div
:class ="{ 'hidden-div': that.hideCreate}"
:id="that.svgId"
:style="{
width: `${imageSize.width * 6}px`,
height: `${imageSize.height * 6}px`,
}">
<div v-if="that.previewUrl" v-html="that.previewUrl" >
</div>
</div>
<div style="max-height: 90vh; overflow:auto;" v-loading="loading">
<div>
<div style="max-height: 90vh; overflow:auto;" v-loading="loading">
<span>
仅供参考(for reference only)
</span>
<div style="padding-bottom: 4px">
<el-checkbox style="margin: 8px" v-model="that.reView">实时预览</el-checkbox>
<el-button
v-show="!that.reView"
style="margin: 8px"
type="primary"
size="small"
@click="updateDesign">
<span>更新</span>
<span
style="color: #ff0000;font-size: 20px; padding-left: 4px">{{
that.changeCount > 0 ? '*' : ''
}}</span>
</el-button>
<div style="padding-bottom: 4px">
<el-checkbox style="margin: 8px" v-model="that.reView">实时预览</el-checkbox>
<el-button
v-show="!that.reView"
style="margin: 8px"
type="primary"
size="small"
@click="updateDesign">
<span>更新</span>
<span
style="color: #ff0000;font-size: 20px; padding-left: 4px">{{
that.changeCount > 0 ? '*' : ''
}}</span>
</el-button>
</div>
<div>
<el-alert
title="按住鼠标左键进行拖动按住Ctrl 滚动鼠标进行缩小放放大"
type="info" effect="dark"/>
</div>
<div v-if="that.sizeInfo" :title="`${that.sizeInfo.width}mm`">
</div>
<div>
<el-alert
title="按住鼠标左键进行拖动按住Ctrl 滚动鼠标进行缩小放放大"
type="info" effect="dark"/>
</div>
<div v-if="that.sizeInfo" :title="`${that.sizeInfo.width}mm`">
<span>
:{{ that.sizeInfo.width }}mm {{ that.sizeInfo.height }}
</span>
</div>
</div>
<el-row>
<el-col :span="11">
<div
:class ="{ 'hidden-div': false }" style="width:100%;height: 580px; border: 1px solid #090805">
<DraftDesign ref="draftDesignEditRef" @init-succeed="initSucceed"/>
</div>
</el-col>
<el-col :span="12">
<div style="display: flex;">
<el-row>
<el-col :span="11">
<div
:class="{ 'hidden-div': false }"
style="width:100%;height: 580px; border: 1px solid #090805">
<DraftDesign ref="draftDesignEditRef" @init-succeed="initSucceed"/>
</div>
</el-col>
<el-col :span="12">
<div style="display: flex;">
<div class="flex flex-col">
<div>
<el-form label-width="180px">
<el-form-item label="风格样式" v-show="that.draftDesignList.length > 1">
<div class="flex ml-3">
<div>
<el-select class="min-w-100" v-model="that.draftDesignId" @change="changeType">
<el-option
v-for="(item) in that.draftDesignList"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
</div>
</div>
</el-form-item>
</el-form>
</div>
<div style="width: 100%" v-if="that.propOrderByList && that.propOrderByList.length > 0">
<el-scrollbar height="600px">
<el-form label-width="180px">
<el-form-item
v-for="(tmp) in that.propOrderByList"
:key="tmp.key"
:label="getLabelName(that.propInfo[tmp.key])">
<div
v-if="that.propInfo[tmp.key].multiLanguage && that.propInfo[tmp.key].shape === ShapeType.vueTextCell">
<div
style="padding: 4px">
<div style="display: flex;align-items: center;">
<div class="flex flex-col">
<div>
<el-form label-width="180px">
<el-form-item label="风格样式" v-show="that.draftDesignList.length > 1">
<div class="flex ml-3">
<div>
<el-select class="min-w-100" v-model="that.draftDesignId"
@change="changeType">
<el-option
v-for="(item) in that.draftDesignList"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
</div>
</div>
</el-form-item>
</el-form>
</div>
<div style="width: 100%"
v-if="that.propOrderByList && that.propOrderByList.length > 0">
<el-scrollbar height="600px">
<el-form label-width="180px">
<el-form-item
v-for="(tmp) in that.propOrderByList"
:key="tmp.key"
:label="getLabelName(that.propInfo[tmp.key])">
<div
v-if="that.propInfo[tmp.key].multiLanguage && that.propInfo[tmp.key].shape === ShapeType.vueTextCell">
<div
style="padding: 4px">
<div style="display: flex;align-items: center;">
<span>
<i
v-if="that.propInfo[tmp.key].canInput"
@ -91,56 +85,56 @@
<i v-else class="icon-lk_cell_add" style="font-size: 20px"> </i>
</span>
<el-select-v2
v-model="that.propInfo[tmp.key].dataInfo[0].showLabel"
filterable
:options="getIngredientInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeData(-1,tmp.key)"
size="large"
style="min-width: 280px;width: 100%"
/>
<div v-if="that.propInfo[tmp.key].groupType === '1'">
<div
style="display: flex;align-items: center; margin-left: 10px; width: 220px">
<el-row>
<el-col span="4">
<div>占比</div>
</el-col>
<el-col span="14">
<el-input-number
:min="-1" :max="100"
v-model="that.propInfo[tmp.key].dataInfo[0].ratio"
@change="changeData(0,tmp.key)"
/>
</el-col>
<el-col span="4">
<div> %</div>
</el-col>
</el-row>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
</div>
<el-select-v2
v-model="that.propInfo[tmp.key].dataInfo[0].showLabel"
filterable
:options="getIngredientInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeData(-1,tmp.key)"
size="large"
style="min-width: 280px;width: 100%"
/>
<div v-if="that.propInfo[tmp.key].groupType === '1'">
<div
style="display: flex;align-items: center; margin-left: 10px; width: 220px">
<el-row>
<el-col span="4">
<div>占比</div>
</el-col>
<el-col span="14">
<el-input-number
:min="-1" :max="100"
v-model="that.propInfo[tmp.key].dataInfo[0].ratio"
@change="changeData(0,tmp.key)"
/>
</el-col>
<el-col span="4">
<div> %</div>
</el-col>
</el-row>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
<div v-else-if="!that.propInfo[tmp.key].isCombo">
<div
v-for="(text,index) in that.propInfo[tmp.key].dataInfo"
:key="index"
style="padding: 4px">
<div style="display: flex;align-items: center;">
<div v-else-if="!that.propInfo[tmp.key].isCombo">
<div
v-for="(text,index) in that.propInfo[tmp.key].dataInfo"
:key="index"
style="padding: 4px">
<div style="display: flex;align-items: center;">
<span>
<i
v-if="that.propInfo[tmp.key].canInput"
@ -148,119 +142,154 @@
style="font-size: 20px"> </i>
<i v-else class="icon-lk_cell_add" style="font-size: 20px"> </i>
</span>
<el-autocomplete
v-if="that.propInfo[tmp.key].canInput"
v-model="that.propInfo[tmp.key].dataInfo[index].showLabel"
:fetch-suggestions="querySearch"
clearable
class="inline-input w-50"
placeholder="Please Input"
@change="changeData"
@select="changeData"
/>
<el-autocomplete
v-if="that.propInfo[tmp.key].canInput"
v-model="that.propInfo[tmp.key].dataInfo[index].showLabel"
:fetch-suggestions="querySearch"
clearable
class="inline-input w-50"
placeholder="Please Input"
@change="changeData"
@select="changeData"
/>
<el-select-v2
v-else
v-model="that.propInfo[tmp.key].dataInfo[index].showLabel"
filterable
:options="getIngredientInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeData(index,tmp.key)"
size="large"
style="min-width: 280px;width: 100%"
/>
<div v-if="that.propInfo[tmp.key].groupType === '1'">
<el-select-v2
v-else
v-model="that.propInfo[tmp.key].dataInfo[index].showLabel"
filterable
:options="getIngredientInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeData(index,tmp.key)"
size="large"
style="min-width: 280px;width: 100%"
/>
<div v-if="that.propInfo[tmp.key].groupType === '1'">
<div
style="display: flex;align-items: center; margin-left: 10px; width: 220px">
<el-row>
<el-col span="4">
<div>占比</div>
</el-col>
<el-col span="14">
<el-input-number
:min="-1" :max="100"
v-model="that.propInfo[tmp.key].dataInfo[index].ratio"
@change="changeData(index,tmp.key)"
/>
</el-col>
<el-col span="4">
<div> %</div>
</el-col>
</el-row>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
</div>
<div
style="display: flex;align-items: center; margin-left: 10px; width: 220px">
<el-row>
<el-col span="4">
<div>占比</div>
</el-col>
<el-col span="14">
<el-input-number
:min="-1" :max="100"
v-model="that.propInfo[tmp.key].dataInfo[index].ratio"
@change="changeData(index,tmp.key)"
/>
</el-col>
<el-col span="4">
<div> %</div>
</el-col>
</el-row>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
<div v-else-if="that.propInfo[tmp.key].shape === ShapeType.vueShapeImage">
<div
v-for="(img,index) in that.propInfo[tmp.key].dataInfo"
:key="index" style="display: flex">
<div class="img-box" v-if="img.url">
<div style="display: flex">
<img :src="img.url" width="60px" height="60px"/>
<el-select-v2
v-model="that.propInfo[tmp.key].dataInfo[0].showLabel"
filterable
:options="washingInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeComboData(that.propInfo[tmp.key],that.propInfo[tmp.key].dataInfo[0].showLabel)"
size="large"
style="min-width: 280px;width: 100%"
>
<template #default="{ item }">
<div style="display: flex;">
<img :src="item.url" width="30px" height="30px"/>
<span>
<div v-else-if="that.propInfo[tmp.key].shape === ShapeType.vueShapeImage">
<div
v-for="(img,index) in that.propInfo[tmp.key].dataInfo"
:key="index" style="display: flex">
<div class="img-box" v-if="img.url">
<div style="display: flex">
<img :src="img.url" width="60px" height="60px"/>
<el-select-v2
v-model="that.propInfo[tmp.key].dataInfo[0].showLabel"
filterable
:options="washingInfoListByType(that.propInfo[tmp.key].groupType)"
placeholder="Please select"
@change="changeComboData(that.propInfo[tmp.key],that.propInfo[tmp.key].dataInfo[0].showLabel)"
size="large"
style="min-width: 280px;width: 100%"
>
<template #default="{ item }">
<div style="display: flex;">
<img :src="item.url" width="30px" height="30px"/>
<span>
{{ item.label }}
</span>
</div>
</template>
</el-select-v2>
</div>
</div>
<div v-else-if="img.label">
<el-input v-model="img.label" disabled/>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
</template>
</el-select-v2>
</div>
</div>
<div v-else-if="img.label">
<el-input v-model="img.label" disabled/>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(that.propInfo[tmp.key],index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="appendList(that.propInfo[tmp.key])">
添加
</el-button>
</div>
</div>
</el-form-item>
</el-form>
</el-scrollbar>
</div>
</div>
</el-form-item>
</el-form>
</el-scrollbar>
</div>
</div>
</div>
</el-col>
</div>
</el-col>
</el-row>
</div>
</div>
</el-row>
</div>
<div
:class ="{ 'hidden-div': that.hideCreate}"
:id="that.svgId"
:style="{
position: that.hideCreate ? 'fixed' : 'relative',
display: `flex`,
flexDirection: `column`,
width: `${previewSize.width}px`,
height: `${previewSize.height}px`,
}">
<div v-if="that.previewUrl"
:style="{
width: `${that.svgWidth+2}px`,
height: `${that.svgHeight+2}px`,
backgroundColor: (that.pageConfig.background && that.pageConfig.background.areaColor) || '',
border: '1px solid #00ff00',
position: `absolute`,
top: `0`,
left: `0`,
}"
>
<div>
<div v-if="false" style="color: #ff0000;">w:{{imageSize.width}} h:{{imageSize.height}}</div>
<span style="color: #ff0000;writing-mode: vertical-rl; text-orientation: upright;">
*仅供参考
</span>
</div>
</div>
<div
v-if="that.previewUrl" v-html="that.previewUrl" >
</div>
</div>
</div>
</template>
<script lang="ts" setup name="DynamicPropConfig">
@ -278,13 +307,14 @@ import {useLocaleStore} from "@/store/modules/locale";
import {ProductInfoApi} from "@/api/oms/productinfo";
import domtoimage from 'dom-to-image';
import {usePageLoading} from "@/hooks/web/usePageLoading";
const { loadStart, loadDone } = usePageLoading()
const {loadStart, loadDone} = usePageLoading()
//
const localeStore = useLocaleStore()
const emit = defineEmits(['change', 'initSucceed'])
const draftDesignEditRef = ref()
const that = reactive({
svgId: "svg_"+Math.random().toString(36).substring(2),
svgId: "svg_" + Math.random().toString(36).substring(2),
hideCreate: true,
pageLoading: {},
propInfo: {
@ -305,7 +335,7 @@ const that = reactive({
draftDesignList: [],
draftDesignId: '',
reView: false,
currentZoom: 6,
currentZoom: 10,
changeCount: 0,
previewUrl: "",
previewData: '',
@ -318,8 +348,16 @@ const that = reactive({
},
data: {},
show: false,
svgHeight: 0,
svgWidth: 0,
})
const previewSize = computed(()=>{
return {
width: imageSize.value.width * 10,
height: imageSize.value.height * 10
}
})
const currentLocale = computed(() => localeStore.getCurrentLocale)
watch(() => that.currentZoom, () => {
showPng();
@ -331,8 +369,8 @@ const querySearch = (queryString: string, cb: any) => {
}) : that.ingredientInfoList
cb(results)
}
const imageSize =computed(()=>{
if(that.sizeInfo){
const imageSize = computed(() => {
if (that.sizeInfo) {
return {
width: that.sizeInfo.width,
height: that.sizeInfo.height
@ -383,7 +421,6 @@ const changeData = (index: number, key: string) => {
str = ''
}
const value = `${mapping[j].value}`.replaceAll('${r}', str)
console.log("value",value)
that.propInfo[key].dataInfo[i].label = value
break;
}
@ -447,27 +484,33 @@ const getIngredientInfoListByType = (type) => {
return that.ingredientInfoList.filter(item => item.type === type)
}
const uploadFile = async (fileName) => {
return new Promise((resolve, reject)=>{
return new Promise((resolve, reject) => {
let svgElement = document.getElementById(that.svgId);
domtoimage.toBlob(svgElement).then((blob) => {
FileApi.updateFile({ file: blob })
.then((res) => {
if (res.code === 0) {
resolve({
...res,
filename: fileName
})
} else {
reject(res)
}
}) .catch((res) => {
reject(res)
})
}).catch(function (error) {
console.error('转换或上传过程中出现错误:', error);
});
});
let svgElement = document.getElementById(that.svgId);
domtoimage.toBlob(svgElement,{
width: that.svgWidth+5,
height: that.svgHeight+5,
type: 'image/jpeg',
quality: 1
}).then((blob) => {
const file = new File([blob], fileName, { type: 'image/jpeg' });
FileApi.updateFile({file: file})
.then((res) => {
if (res.code === 0) {
resolve({
...res,
filename: fileName
})
} else {
reject(res)
}
}).catch((res) => {
reject(res)
})
}).catch(function (error) {
console.error('转换或上传过程中出现错误:', error);
});
});
}
const deleteList = (item, index) => {
@ -629,7 +672,6 @@ const updateDesign = () => {
const changeType = () => {
previewByDraftDesignId(that.draftDesignId, that.propInfo)
}
const getPropInfo = () => {
that.pageLoading = ElLoading.service({
@ -643,26 +685,42 @@ const getPropInfo = () => {
draftDesignEditRef.value.toSVGData((url) => {
that.previewUrl = url
that.hideCreate = false;
//
const date = new Date();
const randChar = Math.random().toString(36).slice(-6);
const fileName = `${date.getFullYear()}${ (date.getMonth() + 1)}${ date.getDate()}_${that.draftDesignId }_${randChar}_稿.png`;
//
uploadFile(fileName).then(res =>{
that.previewData = res.data;
that.hideCreate = true;
that.previewUrl =""
const info = draftDesignEditRef.value.getPropInfo();
that.pageLoading.close()
resolve({
draftDesignData: draftDesignEditRef.value.getJson(),
propOrderByList: info.propOrderByList,
// 稿id
draftDesignId: that.draftDesignId,
previewImage: that.previewData,
propInfo: that.propInfo
setTimeout(()=>{
const el = document.querySelector(`#${that.svgId} g`);
el.setAttribute("transform",'matrix(1,0,0,1,0,0)');
document.querySelector(`#${that.svgId} svg`).style.position = 'absolute';
document.querySelector(`#${that.svgId} svg`).style.top = '-40%';
const dom = document.querySelector(`#${that.svgId} svg .x6-graph-svg-viewport`).getBoundingClientRect()
const svgDom = document.querySelector(`#${that.svgId} svg`).getBoundingClientRect()
that.svgHeight = dom.height;
that.svgWidth = dom.width;
document.querySelector(`#${that.svgId} svg`).style.left = `${svgDom.left - dom.left}px`;
document.querySelector(`#${that.svgId} svg`).style.top = `${svgDom.top - dom.top}px`;
// that.pageLoading.close()
//
const date = new Date();
const randChar = Math.random().toString(36).slice(-6);
const fileName = `${date.getFullYear()}${(date.getMonth() + 1)}${date.getDate()}_${that.draftDesignId}_${randChar}_稿.png`;
//
uploadFile(fileName).then(res => {
that.previewData = res.data;
that.hideCreate = true;
that.previewUrl = ""
const info = draftDesignEditRef.value.getPropInfo();
that.pageLoading.close()
resolve({
draftDesignData: draftDesignEditRef.value.getJson(),
propOrderByList: info.propOrderByList,
// 稿id
draftDesignId: that.draftDesignId,
previewImage: that.previewData,
propInfo: that.propInfo
})
})
})
}, {
widthScale: 6,
heightScale: 6

View File

@ -727,6 +727,7 @@ const toSVGData = (callback: (url: string) => void, options?: {
height: number,
widthScale: number,
heightScale: number,
backgroundColor: String,
padding: {
top: number,
left: number,
@ -735,23 +736,23 @@ const toSVGData = (callback: (url: string) => void, options?: {
}
}) => {
let widthScale = options && options.widthScale ? options.widthScale : 5
let heightScale = options && options.heightScale ? options.heightScale : 5
let widthScale = 20 // options && options.widthScale ? options.widthScale : 5
let heightScale = 20 // options && options.heightScale ? options.heightScale : 5
graph.toSVG((dataUri) => {
that.previewUrl = dataUri;
callback(that.previewUrl)
}, {
copyStyles: true,
viewBox: {
x: 0,
y: 0,
width: that.pageConfig.width * (widthScale/2),
height: that.pageConfig.height * (heightScale/2),
},
preserveDimensions: {
width: that.pageConfig.width * (widthScale),
height: that.pageConfig.height * (heightScale),
},
preserveDimensions:{
width: that.pageConfig.width * (widthScale),
height: that.pageConfig.height * (heightScale),
},
}
})
}
const setPropList = (propDataInfo) => {

View File

@ -21,8 +21,10 @@
<!-- 新增等操作按钮 -->
<template #productTypeId="{data}">
<div>
<el-select class="w-full min-w-[200px]" v-model="data.productTypeId" clearable placeholder="请选择产品类型">
<el-option v-for="item in that.typeList" :key="item.id" :label="item.label" :value="item.id" />
<el-select class="w-full min-w-[200px]" v-model="data.productTypeId" clearable
placeholder="请选择产品类型">
<el-option v-for="item in that.typeList" :key="item.id" :label="item.label"
:value="item.id"/>
</el-select>
</div>
</template>
@ -45,9 +47,18 @@
<template #productTypeId="{row}">
<div>{{ getLabel(row.productTypeId) }}</div>
</template>
<template #details="{row}">
<template #details="{row}">
<div v-html="row.details"></div>
</template>
<template #cover="{row}">
<div v-if="row.cover">
<el-image
:src="row.cover"
style="width: 38px; height: 38px"
:preview-src-list="[row.cover]"/>
</div>
<div v-else>-</div>
</template>
</Table>
</ContentWrap>
@ -126,16 +137,16 @@ const that = reactive({
visible: false,
keyword: '',
typeList: [],
selectRow:[],
queryInfo:{
selectRow: [],
queryInfo: {
productTypeId: null,
}
})
const searchRef = ref(null);
const getLabel = (id)=>{
const res = that.typeList.find(item=>item.id === id);
const getLabel = (id) => {
const res = that.typeList.find(item => item.id === id);
return res ? res.label : id
}
const openDialog = (filter = {}) => {
@ -144,11 +155,11 @@ const openDialog = (filter = {}) => {
...filter
}
updateVisible(true);
if(that.typeList.length === 0 ){
if (that.typeList.length === 0) {
ProductTypeApi.getProductTypePage({
pageNo: 1,
pageSize: 100,
}).then(res=>{
}).then(res => {
that.typeList = res.list
})
}
@ -156,15 +167,15 @@ const openDialog = (filter = {}) => {
}
//
const tmp = computed(()=>{
setTimeout(()=>{
that.inputVal = toStr(props.modelValue,that.inputVal)
const tmp = computed(() => {
setTimeout(() => {
that.inputVal = toStr(props.modelValue, that.inputVal)
if (that.inputVal) {
initInput();
}
},100)
}, 100)
return ''
},{
}, {
deep: true
})
@ -209,13 +220,13 @@ watch(() => props.visible, (newVal) => {
const updateVisible = (visible: boolean, clearInput = false) => {
that.visible = visible;
emit("update:visible", visible)
if(that.visible){
setTimeout(()=>{
searchRef.value.search()
},100)
if (that.visible) {
setTimeout(() => {
searchRef.value.search()
}, 100)
}
}
defineExpose({ openDialog})
defineExpose({openDialog})
const submit = () => {
updateValue();

View File

@ -1,3 +1,13 @@
/**
*
*/
export const STEP0_FINISH_EVENT:string = 'step0finish'
/**
*
*/
export const UPDATE_ADDRESS_EVENT:string = 'update_address'
export const STEP0_FINISH:string = 'step0finish'
export const UPDATE_ADDRESS:string = 'update_address'
/**
*
*/
export const PRODUCT_DEL_ROW_EVENT:string = 'product_del_row_event'

View File

@ -0,0 +1,13 @@
function defaultCloneObj(obj: any) {
if (obj === null || typeof obj !== 'object') return obj;
return JSON.parse(JSON.stringify(obj))
}
export function useResetFields<T extends object>(value: T, clone = defaultCloneObj) {
const state = reactive(clone(value)) as T;
const reset = ():T => {
Object.keys(state).forEach(key => delete state[key]);
return Object.assign(state, clone(value))
}
return [ state,reset] as const;
}

View File

@ -448,5 +448,10 @@ export default {
btn_zoom_out: '缩小',
preview: '预览'
},
designInfo: {
auditSubmitText: '批稿',
auditTips: '此稿件仅仅作为核对内容使用用,不作为最终印刷稿件;',
cancelText: '取消',
},
'OAuth 2.0': 'OAuth 2.0' // 避免菜单名是 OAuth 2.0 时,一直 warn 报错
}

View File

@ -2,7 +2,7 @@
<div class="card-with-badge">
<DesignPreviewDialog ref="designPreviewDialogRef" @submit="submit"/>
<el-card class="el-card">
<el-card class="el-card" >
<div class="badge" v-if="badge">{{ badge }}</div>
<div class="edit-btn">
<el-button link title="Edit" type="primary" @click.stop="openItem">
@ -83,7 +83,10 @@ const designPreviewDialogRef = ref(null);
const props = defineProps({
productInfo: {
type: Object,
required: true
required: true,
default: () => ({
previewImage: ''
})
},
badge: {
type: String,
@ -145,6 +148,11 @@ const submit = (data:any) => {
...formData.value.specInfo,
mainColor: formData.value.draftDesignInfo.label || "默认"
};
// 使
if(!formData.value.specInfo.specSizeWidth){
formData.value.specInfo.specSizeWidth = data.draftDesignData.pageConfig.editArea.width;
formData.value.specInfo.specSizeHeight = data.draftDesignData.pageConfig.editArea.height;
}
change();
}
const change = () => {

View File

@ -0,0 +1,355 @@
<template>
<div class="card-with-badge">
<el-card class="el-card">
<el-table :data="that.tableList" class="w-full" border height="400px" @selection-change="selectionChange">
<el-table-column type="selection" width="55" :selectable="selectable" />
<el-table-column width="80px" label="#">
<template #default="scope">
<div>
{{scope.$index + 1}}
</div>
</template>
</el-table-column>
<el-table-column width="84px" label="主图">
<template #default="scope">
<div class="flex items-center">
<el-image
fit="contain"
style="width: 80px;height: 80px;"
:preview-src-list="[scope.row.previewImage]"
:src="scope.row.previewImage"/>
</div>
</template>
</el-table-column>
<el-table-column width="300px" label="尺码">
<template #header>
<div class="flex">
<div>尺码</div>
<div>
<el-input
v-model="that.fullSize"
v-show="that.tableList.length > 1"
placeholder="多个使用空格间隔"
@change="fullChangeSize"/>
</div>
</div>
</template>
<template #default="scope">
<el-autocomplete
v-model="scope.row.labelSize"
:fetch-suggestions="querySearch"
clearable
class="inline-input w-50"
placeholder="输入码数"
@select="handleSelect"
/>
</template>
</el-table-column>
<el-table-column width="300px" label="数量">
<template #header>
<div class="flex">
<div>数量<span class="text-xs">({{totalCount}})</span></div>
<div><el-input-number v-model="that.fullCount" v-show="that.tableList.length > 1" @change="fullQty"/> </div>
</div>
</template>
<template #default="scope">
<el-input-number v-model="scope.row.orderQty" min="1" />
</template>
</el-table-column>
</el-table>
<div class="badge" >{{ that.tableList.length }}</div>
<div class="edit-btn">
<el-button link title="Add" type="primary" @click.stop="addItem">
<Icon size="24" icon="ep:plus"/>
</el-button>
</div>
<div class="del-btn" title="Delete">
<el-button link type="danger" @click.stop="delItem">
<Icon size="24" icon="ep:remove"/>
</el-button>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup name="ProductItem1">
//@ts-nocheck
import {reactive, useModel, watch} from 'vue'
import DesignPreviewDialog from "@/components/DraftDesign/components/DesignPreviewDialog.vue";
import {ShapeType} from "@/components/DraftDesign/config";
import {createImageViewer} from "@/components/ImageViewer";
import {useI18n} from "@/hooks/web/useI18n";
import {ElOption, ElSelect} from "element-plus";
import {useEmitt} from "@/hooks/web/useEmitt";
import {PRODUCT_DEL_ROW_EVENT, STEP0_FINISH_EVENT} from "@/constants/EmitEventName";
import {useResetFields} from "@/hooks/ext";
const emit = defineEmits(['update:productInfo', 'delItem', 'change'])
const { t } = useI18n()
const props = defineProps({
productInfo: {
type: Object,
required: true
},
badge: {
type: String,
required: false,
default: ''
}
})
const [that, resetFields] = useResetFields({
tableList: [],
selectRow: [],
fullCount: 0,
itemList:[ 'M','L','XL','XXL','XXXL'],
fullSize:'',
})
const totalCount = computed(()=>{
let count = 0;
that.tableList.forEach(item=>{
count += item.orderQty
})
return count;
})
watch(that.tableList, (newVal)=>{
changeData();
})
const formData = useModel(props, 'productInfo', emit)
const delItem = () => {
if(that.selectRow.length === 0){
useMessage().warning("请选择要删除的项");
return;
}
useMessage().confirm('确定删除吗?').then(() => {
let key = [];
for (let i = 0; i < that.selectRow.length; i++) {
key.push(that.selectRow[i].key)
}
that.tableList = that.tableList.filter(item => !key.includes(item.key))
changeData();
})
}
const fullChangeSize = ()=>{
const selectedValue = ref<string | number>(1); //
ElMessageBox({
title: '确定批量设置吗?',
confirmButtonText: t('common.ok'),
cancelButtonText: t('common.cancel'),
type: 'warning',
message: () =>
h(ElSelect, {
modelValue: selectedValue.value,
placeholder: '请选择',
style: { width: '300px' },
'onUpdate:modelValue': (value: string | number) => {
selectedValue.value = value;
},
}, [
h(ElOption, { label: '填充空值行', value: 1 }),
h(ElOption, { label: '追加', value: 2 }),
h(ElOption, { label: '覆盖', value: 3 }),
//
]),
}).then(() => {
const val = that.fullSize.split(" ")
let index= 0;
if(selectedValue.value === 2){
for (let i = 0; i < val.length; i++) {
addRow({
labelSize: val[index],
})
}
}else {
that.tableList.forEach(item => {
if(index < val.length){
if(selectedValue.value === 1){
if(!item.labelSize){
item.labelSize = val[index]
index++;
}
}else if(selectedValue.value === 3){
item.labelSize = val[index]
index++;
}
}
});
}
}).catch(() => {
//
});
}
const fullQty = (val) => {
const selectedValue = ref<string | number>(1); //
ElMessageBox({
title: '确定批量设置数量 ' + val + ' 吗?',
confirmButtonText: t('common.ok'),
cancelButtonText: t('common.cancel'),
type: 'warning',
message: () =>
h(ElSelect, {
modelValue: selectedValue.value,
placeholder: '请选择',
style: { width: '300px' },
'onUpdate:modelValue': (value: string | number) => {
selectedValue.value = value;
},
}, [
h(ElOption, { label: '填充全部', value: 1 }),
h(ElOption, { label: '追加', value: 2 }),
h(ElOption, { label: '减少', value: 3 }),
//
]),
}).then(() => {
that.tableList.forEach(item => {
if(selectedValue.value === 1){
item.orderQty = val
}else if(selectedValue.value === 2){
item.orderQty += val
}else if(selectedValue.value === 3){
item.orderQty -= val
if( item.orderQty < 0){
item.orderQty = 1
}
}
});
}).catch(() => {
//
});
}
const addItem = () => {
addRow();
}
const addRow=(row={})=>{
if(row.labelSize){
for (let i = 0; i < that.tableList.length; i++) {
if(that.tableList[i].labelSize === row.labelSize){
//
useMessage().notifyWarning(`${i}行,已跳过重复尺码${row.labelSize} `)
return
}
}
}
that.tableList.push({
orderQty: 1,
labelSize: "",
//@ts-ignore
previewImage: formData.value.previewImage,
draftDesignId: "",
...formData.value,
...row,
key: Math.random().toString(36).substring(2),
})
}
useEmitt({
name: PRODUCT_DEL_ROW_EVENT,
callback: (row)=>{
if(formData.value && that.tableList && that.tableList.length > 0
&& formData.value.productId === that.tableList[0].productId){
that = resetFields();
}
},
});
const init = (list = [])=>{
if(list){
for (let i = 0; i < list.length; i++) {
addRow(list[i])
}
}
}
const querySearch = (queryString: string, cb: any) => {
const results = queryString ? that.itemList.filter(t => t.toString()
.toLocaleLowerCase()
.indexOf(queryString.toLocaleLowerCase()) != -1) : that.itemList
let res = [];
for (let i = 0; i < results.length; i++) {
res.push({
value: results[i],
label: results[i],
})
}
cb(res)
}
const handleSelect = (item: RestaurantItem) => {
console.log(item)
}
const selectable = (row:any) => {
return true;
}
const selectionChange = (arr)=>{
that.selectRow = arr;
}
const changeData = () => {
emit("change", that.tableList)
}
defineExpose({
init
})
</script>
<style lang="scss" scoped>
:deep(.el-form-item) {
margin-bottom: 0;
margin-top: 0 !important;
}
.card-with-badge {
position: relative;
display: inline-block;
}
.el-card {
padding-top: 20px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.badge {
position: absolute;
top: -4px;
left: 0;
background-color: #6bb5ff;
color: white;
padding: 5px 10px;
border-radius: 50%;
font-size: 12px;
font-weight: bold;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
text-align: center;
}
.del-btn {
position: absolute;
top: 0;
right: 0;
}
.edit-btn {
position: absolute;
top: 0;
right: 30px;
}
.el-form-item {
margin-top: 15px;
}
</style>

View File

@ -10,6 +10,7 @@
highlight-current-row
stripe
:data="that.tableData"
border
@row-click="rowChange"
style="width: 100%">
<el-table-column prop="productCode" label="产品编码"/>
@ -57,9 +58,10 @@
<script lang="ts" setup name="ProductList">
// @ts-nocheck
import {useEmitt} from "@/hooks/web/useEmitt";
import {STEP0_FINISH} from "@/constants/EmitEventName";
import {PRODUCT_DEL_ROW_EVENT, STEP0_FINISH_EVENT} from "@/constants/EmitEventName";
import {formatDate} from "@/utils/formatTime";
const emit = defineEmits(['rowClick'])
const tableRef = ref();
const that = reactive({
@ -74,20 +76,19 @@ const that = reactive({
})
const handleEvent = (data:any) => {
console.log("获取过滤条件",data);
if(data){
that.filterParam.brandId = data.brandId;
that.isBatch = data.isBatch;
that.plansenddate = data.plansenddate;
that.planDate = formatDate(that.plansenddate);
}
};
const {emitter} = useEmitt();
useEmitt({
name: STEP0_FINISH,
name: STEP0_FINISH_EVENT,
callback: handleEvent,
});
const productInfoListRef = ref(null);
const addProduct = () => {
productInfoListRef.value.openDialog(that.filterParam)
@ -96,9 +97,13 @@ const delProduct = () => {
useMessage().confirm('确定删除吗?').then(() => {
that.tableData = that.tableData.filter(item => item.productId !== that.selectRow.productId)
useMessage().notifySuccess('删除成功');
rowChange(that.tableData[that.tableData.length - 1])
})
emit('delRow', that.selectRow);
emitter.emit(PRODUCT_DEL_ROW_EVENT, that.selectRow);
if(that.tableData.length > 0){
rowChange(that.tableData[that.tableData.length - 1]);
}
})
}
const submit = (data) => {
for (let i = 0; i < data.length; i++) {
@ -128,6 +133,7 @@ const addRow = (row) => {
producer: row.producer,
cover: row.cover,
unit: row.unit || 'pcs',
details: row.details || '',
orderQty: row.orderQty || 0,
totalQty: row.totalQty || 0,
deliveryDate: row.deliveryDate,
@ -175,9 +181,17 @@ const setSelectRow = (row) => {
const getTableData = () => {
return that.tableData
}
const init = (data) => {
if(data && data.length > 0){
that.tableData = data
rowChange(data[0])
}
}
defineExpose({
setSelectRow,
getTableData
getTableData,
init
})
</script>

View File

@ -2,32 +2,47 @@
<div>
<div>
<el-button type="primary" v-show="that.tableData.length > 0" @click="addSku">添加SKU</el-button>
<el-button type="primary" v-show="that.templateType === '1' && that.tableData.length > 0" @click="addSku">添加SKU</el-button>
</div>
<div class="flex flex-wrap">
<div class="flex flex-wrap" v-if="that.templateType === '1'">
<template v-for="(item,index) in that.tableData" :key="item.key">
<ProductItem1
@change="changeItem"
class="m-2"
@del-item="delItem"
:badge="`${index+1}`"
v-model:product-info="that.tableData[index]"/>
v-model:product-info="that.tableData[index]" />
</template>
</div>
<div class="flex" v-show="that.templateType === '2' && that.tableData.length > 0">
<ProductItem2
@change="changeList"
ref="productItem2Ref"
class="m-2"
@del-item="delItem"
:badge="`${1}`"
v-model:product-info="that.tableData[0]" />
</div>
</div>
</template>
<script lang="ts" setup name="ProductSkuList">
import ProductItem1 from "./ProductItem1.vue"
import ProductItem2 from "./ProductItem2.vue"
import {ProductInfoApi} from "@/api/oms/productinfo";
import {useResetFields} from "@/hooks/ext";
const emit = defineEmits(['change'])
const that = reactive({
const productItem2Ref = ref();
const [that, resetFields] = useResetFields({
tableData: [],
productId: '',
listKey: '',
cover: '',
templateType: '2', // 1 2 3
specInfo: {
specSizeWidth: 0,
specSizeHeight: 0,
@ -43,7 +58,11 @@ const addSku = () => {
listKey: that.listKey,
orderQty: 1,
productId: that.productId,
previewImage: that.cover,
draftDesignId: '',
labelSize: '',
// - product_draft_template_type 1 2 3
productTemplateType: that.templateType,
draftDesignInfo: {},
width: 0, // 稿
height: 0, // 稿
@ -51,30 +70,54 @@ const addSku = () => {
specInfo: JSON.parse(JSON.stringify(that.specInfo)),
})
}
const itemType =()=>{
//
if(that.cover){
//
}
}
const init = (row) => {
that.tableData = row.productSkuList;
that.productId = row.productId
that.listKey = row.key
//@ts-ignore
ProductInfoApi.getProductInfo(that.productId).then(res=>{
that.specInfo = {
specSizeWidth: res.specSizeWidth,
specSizeHeight: res.specSizeHeight,
specSizeThk: res.specSizeThk,
specMaterial: res.specMaterial,
mainColor: '',
}
if (that.tableData.length === 0) {
addSku();
}
})
if(that.productId){
//@ts-ignore
ProductInfoApi.getProductInfo(that.productId).then(res=>{
console.log("res",res)
that.specInfo = {
specSizeWidth: res.specSizeWidth,
specSizeHeight: res.specSizeHeight,
specSizeThk: res.specSizeThk,
specMaterial: res.specMaterial,
mainColor: '',
}
that.templateType = res.templateType;
// 稿
if(res.templateType !== '1'){
that.cover = res.cover
}
if (that.tableData.length === 0) {
addSku();
}
if(res.templateType === '2'){
//
setTimeout(()=>{
productItem2Ref.value.init(that.tableData);
},100)
}
})
}
}
const changeItem = (item) => {
const changeItem = () => {
emit("change", that.tableData)
}
const changeList = (tableData)=>{
that.tableData = tableData
changeItem();
}
const delItem = (item) => {
let index = -1
for (let i = 0; i < that.tableData.length; i++) {
@ -92,7 +135,8 @@ const delItem = (item) => {
emit("change", that.tableData)
}
defineExpose({
init
init,
resetFields
})
</script>

View File

@ -4,7 +4,7 @@
<el-scrollbar height="38vh">
<div>
<div class="title-border">订单产品列表</div>
<ProductList ref="listRef" @row-click="rowClick" />
<ProductList ref="listRef" @row-click="rowClick" @del-row="delRow"/>
</div>
</el-scrollbar>
@ -22,7 +22,7 @@
import ProductList from "./ProductList.vue";
import ProductSkuList from "./ProductSkuList.vue";
import {useEmitt} from "@/hooks/web/useEmitt";
import {STEP0_FINISH} from "@/constants/EmitEventName";
import {STEP0_FINISH_EVENT} from "@/constants/EmitEventName";
const skuListRef = ref(null);
const listRef = ref(null);
@ -30,6 +30,7 @@ const that = reactive({
selectRow: null,
})
const rowClick = (row) => {
console.log("init",row)
that.selectRow = {
productSkuList: [],
...row,
@ -37,7 +38,9 @@ const rowClick = (row) => {
//@ts-nocheck
skuListRef.value.init(that.selectRow)
}
const delRow = (row)=>{
skuListRef.value.resetFields();
}
const changeDetails = (tableData)=>{
that.selectRow.productSkuList = [];
@ -48,10 +51,10 @@ const changeDetails = (tableData)=>{
})
}
listRef.value.setSelectRow(that.selectRow)
}
const getTableData = () => {
const list = [];
for (let i = 0; i < listRef.value.getTableData().length; i++) {
list.push({
@ -61,9 +64,12 @@ const getTableData = () => {
}
return list
}
const init = (saleOrderEntry)=>{
listRef.value.init(saleOrderEntry)
}
defineExpose({
getTableData
getTableData,
init
})
</script>

View File

@ -40,8 +40,8 @@
disabled
prefix-icon=""
style="width: 760px"
type="date"
value-format="YYYY-MM-DD"
type="datetime"
value-format="YYYY-MM-DD hh:mm:ss"
/>
</el-form-item>
</el-col>
@ -62,7 +62,7 @@
<el-row>
<el-col :span="12">
<el-form-item label="品牌" prop="brandId">
<el-select v-model="formData.brandId" filterable placeholder="请选择品牌">
<el-select :disabled="changeBrand" v-model="formData.brandId" filterable placeholder="请选择品牌">
<el-option
v-for="brand in brandList"
:key="brand.id"
@ -79,8 +79,8 @@
:disabledDate="(time)=> { return time.getTime() < (Date.now() + 8 * 60 * 60 * 1000); }"
v-model="that.tmpFormData.planDate"
style="width: 760px"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
type="datetime"
/>
</el-form-item>
@ -149,9 +149,10 @@
<div class="flex w-full">
<el-input
clearable
:disabled="isEditState"
v-model="formData.contractCode"
placeholder="请填写合同号"/>
<el-button type="primary" @click="generateContractCode">生成</el-button>
<el-button v-show="!formData.contractCode && !isEditState" type="primary" @click="generateContractCode">获取</el-button>
</div>
</el-form-item>
</el-col>
@ -166,6 +167,20 @@
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="业务员" prop="orderFollowerUser">
<el-select v-model="formData.orderFollowerUser" filterable placeholder="业务员">
<el-option
v-for="user in that.followerUserList"
:key="user.id"
:label="user.username"
:value="`${user.id}`"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>
<el-form-item label="备注" prop="remarks">
@ -391,21 +406,23 @@ import * as BrandApi from "@/api/oms/brand";
import {AddressApi} from "@/api/oms/customer/address";
import {CustomerApi} from "@/api/oms/customer";
import {SaleOrderApi} from "@/api/oms/saleorder";
const { push } = useRouter()
import OrderAddProductStep from "./components/OrderAddProductStep/index.vue";
import {useEmitt} from "@/hooks/web/useEmitt";
import {STEP0_FINISH, UPDATE_ADDRESS} from "@/constants/EmitEventName";
import {STEP0_FINISH_EVENT, UPDATE_ADDRESS_EVENT} from "@/constants/EmitEventName";
import {formatDate} from "@/utils/formatTime";
import {ElLoading} from "element-plus";
const route = useRoute() //
//
const calculateDateAfterDays = (days) => {
return formatDate(new Date(Date.now() + ((24 * 60 * 60 * 1000) * days)));
}
const {emitter} = useEmitt();
const loading = ref(false) //
const list = ref([]) //
const total = ref(0) //
const queryParams = reactive({
id: null,
pageNo: 1,
pageSize: 10,
name: null,
@ -423,13 +440,14 @@ const editAddress = () => {
addressRef.value.openDialog()
}
useEmitt({
name: UPDATE_ADDRESS,
name: UPDATE_ADDRESS_EVENT,
callback: ()=>{
getCustomerAddressList();
},
});
const that = reactive({
formLoading: false,
pageLoading: {},
addressList: [],
addressId: null,
tmpFormData: {
@ -437,6 +455,8 @@ const that = reactive({
emailList: [],
planDate: calculateDateAfterDays(7),
},
followerUserList:[],
subListSize: 0,
})
const formData = ref({
contactName: '',
@ -463,12 +483,15 @@ const formData = ref({
deliveryAddress: '',
deliveryRemark: '',
saleOrderEntry: [],
orderFollowerUser: '',
})
const isEditState = computed(()=>{
return route.query.id != undefined
})
const formRules = reactive({
contactName: [{required: true, message: '联系人不能为空', trigger: 'blur'}],
brandId: [{required: true, message: '品牌不能为空', trigger: 'blur'}],
contractCode: [{required: true, message: '品牌不能为空', trigger: 'blur'}],
contractCode: [{required: true, message: '合同号不能为空', trigger: 'blur'}],
phone: [{required: true, message: '联系人手机号不能为空', trigger: 'blur'},
{
pattern: /^(?:(?:\+|00)86)?1(?:3[\d]|4[5-79]|5[0-35-9]|6[5-7]|7[0-8]|8[\d]|9[189])\d{8}$/,
@ -479,13 +502,8 @@ const formRules = reactive({
})
const activeNames = ref(['1', '2', '3'])
const onAddEmail = () => {
if (!that.tmpFormData.inputEmail || that.tmpFormData.inputEmail.trim() === '') {
useMessage().warning('请输入邮箱');
return;
}
const tmp = that.tmpFormData.inputEmail.trim();
const pushEmail = (input:string)=>{
const tmp = input.trim();
const index = that.tmpFormData.emailList.indexOf(tmp);
if (index != -1) {
that.tmpFormData.emailList.splice(index, 1);
@ -494,8 +512,16 @@ const onAddEmail = () => {
} else {
that.tmpFormData.emailList.push(tmp);
}
}
const onAddEmail = () => {
if (!that.tmpFormData.inputEmail || that.tmpFormData.inputEmail.trim() === '') {
useMessage().warning('请输入邮箱');
return;
}
pushEmail(that.tmpFormData.inputEmail);
that.tmpFormData.inputEmail = ''; //
}
const onDelEmail = () => {
if (selectedIndex.value >= 0) {
that.tmpFormData.emailList.splice(selectedIndex.value, 1); //
@ -509,16 +535,24 @@ const onDelEmail = () => {
const selectItem = async (index: number) => {
selectedIndex.value = index;
}
const changeBrand = computed(()=>{
return that.subListSize > 0;
})
const generateContractCode = () => {
const code = `CT-${Math.floor(Date.now() / 1000) + '-' + Math.random().toString(10).substring(2, 6).padEnd(4, '0')}`;
formData.value.contractCode = code;
return code;
//
SaleOrderApi.getContractCode().then(res =>{
formData.value.contractCode = res;
})
}
const backStep = async () => {
stepIndex.value = stepIndex.value - 1;
that.subListSize = 0;
if(stepRef.value){
that.subListSize = stepRef.value.getTableData().length
}
}
const {emitter} = useEmitt();
const nextStep = async () => {
if (stepIndex.value === 0) {
formRef.value.validate().then(() => {
@ -530,7 +564,7 @@ const nextStep = async () => {
if (!formData.isBatch && that.tmpFormData.planDate) {
formData.value.plansenddate = new Date(that.tmpFormData.planDate).getTime()
}
emitter.emit(STEP0_FINISH, {
emitter.emit(STEP0_FINISH_EVENT, {
...formData.value
})
}).catch(() => {
@ -539,7 +573,6 @@ const nextStep = async () => {
} else {
stepIndex.value = stepIndex.value + 1;
}
}
const onChangeAddress = (id)=>{
console.log(id)
@ -549,28 +582,78 @@ const onChangeAddress = (id)=>{
}
}
}
const addNewBill = () => {
console.log("stepRef.value.getTableData()", stepRef.value.getTableData())
// todo
const tableData = stepRef.value.getTableData();
if (tableData.length === 0) {
useMessage().warning("请添加产品")
return;
}
/**
* 提交前处理
*/
const submitPreHandler = (showMsg = true) => {
return new Promise((resolve, reject) => {
// todo
const tableData = stepRef.value.getTableData();
if (tableData.length === 0) {
if(showMsg){
useMessage().warning("请添加产品")
}
reject("请添加产品")
}
if (that.tmpFormData.emailList) {
formData.value.emails = that.tmpFormData.emailList.join(";")
}
if (!formData.isBatch && that.tmpFormData.planDate) {
formData.value.plansenddate = new Date(that.tmpFormData.planDate).getTime()
}
formData.value.saleOrderEntry = tableData
for (let i = 0; i < tableData.length; i++) {
}
formData.value.saleOrderEntry = tableData
console.log("333", formData.value)
SaleOrderApi.placeOrder({
...formData.value
}).then(res => {
useMessage().success("下单成功")
resFrom();
stepIndex.value = 0
if ( formData.value.bizdate) {
formData.value.bizdate = new Date(formData.value.bizdate).getTime()
}
resolve(true);
})
}
const addNewBill = () => {
console.log("stepRef.value.getTableData()", stepRef.value.getTableData())
submitPreHandler().then(res=>{
if(formData.value.id){
SaleOrderApi.editOrder(formData.value.id,{
...formData.value
}).then(res => {
that.pageLoading = ElLoading.service({
lock: true,
text: '修改成功...',
background: 'rgba(0,0,0,0.5)'
})
setTimeout(()=>{
that.pageLoading.close()
push("/")
},800)
})
}else {
SaleOrderApi.placeOrder({
...formData.value
}).then(res => {
that.pageLoading = ElLoading.service({
lock: true,
text: '下单成功...',
background: 'rgba(0,0,0,0.5)'
})
setTimeout(()=>{
that.pageLoading.close()
push("/")
},800)
})
}
});
}
/** 初始化 **/
onMounted(() => {
queryParams.id = '';
if(route.query){
queryParams.id = route.query.id ?? ''
}
})
const hasLastIndex = computed(() => {
return stepIndex.value >= 1;
@ -578,9 +661,26 @@ const hasLastIndex = computed(() => {
/** 初始化 **/
const brandList = ref<any[]>([]) //
watch(() => formData.value.brandId, async (val)=>{
if(val){
const data = await SaleOrderApi.getFollowerUser(val)
that.followerUserList = data
if(that.followerUserList.length > 0 && !formData.value.orderFollowerUser){
formData.value.orderFollowerUser = that.followerUserList[0].id
}
}
})
onMounted(async () => {
//
brandList.value = await BrandApi.getSimpleBrandList()
BrandApi.getSimpleBrandList().then(res =>{
brandList.value = res
if(res.length > 0 && !formData.value.brandId){
formData.value.brandId = brandList.value[0].id
}
})
const customerData = await CustomerApi.getCustomerInfo()
if (customerData) {
that.addressList = customerData.addressList;
@ -589,6 +689,9 @@ onMounted(async () => {
formData.value.company = customerData.company;
formData.value.contactName = customerData.contacts;
formData.value.phone = customerData.phone;
if(customerData.email){
pushEmail(customerData.email);
}
formData.value.invoiceCode = customerData.invoiceCode;
formData.value.invoiceName = customerData.invoiceName;
formData.value.invoiceAddress = customerData.invoiceAddress;
@ -597,6 +700,19 @@ onMounted(async () => {
that.addressId = that.addressList[0].id;
}
}
//
if(queryParams.id){
const res = await SaleOrderApi.queryEditById(queryParams.id);
formData.value = res
formData.value.bizdate = formatDate( new Date(res.bizdate), 'YYYY-MM-DD hh:mm:ss');
that.tmpFormData.planDate = formatDate( new Date(res.plansenddate), 'YYYY-MM-DD');
that.tmpFormData.emailList = res.emails.split(";")
formData.value = {
...formData.value,
id: queryParams.id
}
stepRef.value.init(res.saleOrderEntry ?? []);
}
})
const getCustomerAddressList = async () => {
@ -621,7 +737,7 @@ const resFrom = (init = {}) => {
invoiceName: '',
invoiceCode: '',
invoiceRemarks: '',
contractCode: '',
contractCode: 'tmp1',
retailerCode: '',
remarks: '',
isBatch: false,

View File

@ -236,6 +236,16 @@
/>
<el-table-column label="操作" align="center" width="150" fixed="right">
<template #default="scope">
<router-link :to="'/order/createorder?id='+scope.row.id+'&_t=2'">
<el-button
link
type="primary">
<Icon icon="ep:edit" />
修改
</el-button>
</router-link>
<el-button
link
type="primary"

View File

@ -0,0 +1,13 @@
ALTER TABLE oms_saleorder ADD COLUMN `order_follower_user` VARCHAR(128) DEFAULT NULL COMMENT '跟单员id system_user.id';
ALTER TABLE oms_product_info ADD COLUMN `template_type` VARCHAR(10) DEFAULT 1 COMMENT '模板类型-字典product_draft_template_type 1有模板 2无模板 3尺码唛';
ALTER TABLE oms_sale_order_sku ADD COLUMN `product_template_type` VARCHAR(10) DEFAULT 1 COMMENT '模板类型-字典product_draft_template_type 1有模板 2无模板 3尺码唛';
ALTER TABLE oms_sale_order_sku ADD COLUMN `label_size` VARCHAR(64) DEFAULT NULL COMMENT '尺码唛的尺码';
ALTER TABLE oms_saleorder_entry
ADD COLUMN `unit` VARCHAR(64) DEFAULT NULL COMMENT '计量单位'
AFTER `qty`;