Compare commits
2 Commits
c94727c72e
...
c5a8f8f032
| Author | SHA1 | Date |
|---|---|---|
|
|
c5a8f8f032 | |
|
|
b153c22261 |
|
|
@ -25,6 +25,7 @@ public interface GlobalErrorCodeConstants {
|
|||
ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确");
|
||||
ErrorCode LOCKED = new ErrorCode(423, "请求失败,请稍后重试"); // 并发请求,不允许
|
||||
ErrorCode TOO_MANY_REQUESTS = new ErrorCode(429, "请求过于频繁,请稍后重试");
|
||||
ErrorCode NOT_SUPPORT_FILE_TYPE = new ErrorCode(430, "不支持上传该文件类型");
|
||||
|
||||
// ========== 服务端错误段 ==========
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
package cn.hangtag.framework.common.pojo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "文件信息")
|
||||
@Data
|
||||
public class FileInfoVO {
|
||||
|
||||
/**
|
||||
* infra_file id
|
||||
*/
|
||||
@Schema(description = "文件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* infra_file_config id
|
||||
*/
|
||||
@Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11")
|
||||
private Long configId;
|
||||
|
||||
@Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "hangtag.jpg")
|
||||
private String path;
|
||||
|
||||
@Schema(description = "原文件名", requiredMode = Schema.RequiredMode.REQUIRED, example = "hangtag.jpg")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "文件 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/hangtag.jpg")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "文件MIME类型", example = "application/octet-stream")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "文件大小", example = "2048", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer size;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import lombok.SneakyThrows;
|
|||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* 文件工具类
|
||||
|
|
@ -18,6 +19,49 @@ import java.io.File;
|
|||
*/
|
||||
public class FileUtils {
|
||||
|
||||
public static final HashSet<String> SUFFIXS = new HashSet<String>() {{
|
||||
// 常用的图片文件类型
|
||||
add("jpg");
|
||||
add("jpeg");
|
||||
add("png");
|
||||
add("gif");
|
||||
add("bmp");
|
||||
add("tiff");
|
||||
add("webp");
|
||||
add("ico");
|
||||
add("svg");
|
||||
|
||||
// 常用的文档文件类型
|
||||
add("pdf");
|
||||
add("doc");
|
||||
add("docx");
|
||||
add("xls");
|
||||
add("xlsx");
|
||||
add("ppt");
|
||||
add("pptx");
|
||||
add("txt");
|
||||
add("rtf");
|
||||
|
||||
// 常用的视频文件类型
|
||||
add("mp4");
|
||||
add("avi");
|
||||
add("mov");
|
||||
add("wmv");
|
||||
add("flv");
|
||||
add("mkv");
|
||||
|
||||
// 常用的音频文件类型
|
||||
add("mp3");
|
||||
add("wav");
|
||||
add("ogg");
|
||||
add("aac");
|
||||
|
||||
// 压缩文件类型
|
||||
add("zip");
|
||||
add("rar");
|
||||
add("7z");
|
||||
|
||||
}};
|
||||
/**
|
||||
* 创建临时文件
|
||||
* 该文件会在 JVM 退出时,进行删除
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
package cn.hangtag.module.infra.api.file;
|
||||
|
||||
import cn.hangtag.framework.common.pojo.FileInfoVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件 API 接口
|
||||
*
|
||||
|
|
@ -38,4 +42,31 @@ public interface FileApi {
|
|||
*/
|
||||
String createFile(String name, String path, byte[] content);
|
||||
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param originalFilename 原始文件名
|
||||
* @param path 路径
|
||||
* @param bytes 字节
|
||||
* @return {@link FileInfoVO }
|
||||
*/
|
||||
FileInfoVO uploadFile(String originalFilename, String path, byte[] bytes);
|
||||
|
||||
/**
|
||||
* 获取文件网址
|
||||
*
|
||||
* @param ids IDs
|
||||
* @return {@link List }<{@link String }>
|
||||
*/
|
||||
List<FileInfoVO> getFileInfo(String ids);
|
||||
|
||||
/**
|
||||
* 重命名
|
||||
*
|
||||
* @param id ID
|
||||
* @param name 名称
|
||||
* @return {@link FileInfoVO }
|
||||
*/
|
||||
FileInfoVO rename(String id, String name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
package cn.hangtag.module.infra.api.file;
|
||||
|
||||
import cn.hangtag.framework.common.pojo.FileInfoVO;
|
||||
import cn.hangtag.module.infra.controller.admin.file.vo.file.FileRespVO;
|
||||
import cn.hangtag.module.infra.service.file.FileService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件 API 实现类
|
||||
|
|
@ -23,4 +27,18 @@ public class FileApiImpl implements FileApi {
|
|||
return fileService.createFile(name, path, content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileInfoVO uploadFile(String originalFilename, String path, byte[] bytes) {
|
||||
return fileService.uploadFile(originalFilename, path, bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileInfoVO> getFileInfo(String ids) {
|
||||
return fileService.getFileInfo(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileInfoVO rename(String id, String name) {
|
||||
return fileService.rename(id, name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package cn.hangtag.module.infra.controller.admin.file;
|
||||
|
||||
import cn.hangtag.framework.common.pojo.FileInfoVO;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
|
|
@ -25,6 +26,8 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.hangtag.framework.common.pojo.CommonResult.success;
|
||||
import static cn.hangtag.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
|
||||
|
||||
|
|
@ -45,6 +48,26 @@ public class FileController {
|
|||
String path = uploadReqVO.getPath();
|
||||
return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
|
||||
}
|
||||
@PostMapping("/upload-plus")
|
||||
@Operation(summary = "上传文件", description = "后端上传文件并返回文件信息")
|
||||
public CommonResult<FileInfoVO> uploadPlus(FileUploadReqVO uploadReqVO) throws Exception {
|
||||
MultipartFile file = uploadReqVO.getFile();
|
||||
String path = uploadReqVO.getPath();
|
||||
FileInfoVO res = fileService.uploadFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream()));
|
||||
return success(res);
|
||||
}
|
||||
@GetMapping("/info/{ids}")
|
||||
@Operation(summary = "上传文件", description = "根据id查询文件信息")
|
||||
public CommonResult<List<FileInfoVO>> getUploadUrl(@PathVariable("ids") String ids) {
|
||||
List<FileInfoVO> res = fileService.getFileInfo(ids);
|
||||
return success(res);
|
||||
}
|
||||
@PostMapping("/rename/{id}")
|
||||
@Operation(summary = "上传文件", description = "修改文件名称")
|
||||
public CommonResult<FileInfoVO> getUploadUrl(@PathVariable("id") String ids,@Validated @RequestBody FileRenameReqVO renameReqVO) {
|
||||
FileInfoVO res = fileService.rename(ids,renameReqVO.getName());
|
||||
return success(res);
|
||||
}
|
||||
|
||||
@GetMapping("/presigned-url")
|
||||
@Operation(summary = "获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package cn.hangtag.module.infra.controller.admin.file.vo.file;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Schema(description = "管理后台 - 文件修改名称 Request VO")
|
||||
@Data
|
||||
public class FileRenameReqVO {
|
||||
|
||||
@NotNull(message = "文件名不能为空")
|
||||
@Schema(description = "文件名", requiredMode = Schema.RequiredMode.REQUIRED, example = "abc")
|
||||
private String name;
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
package cn.hangtag.module.infra.service.file;
|
||||
|
||||
import cn.hangtag.framework.common.pojo.FileInfoVO;
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
import cn.hangtag.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
|
||||
import cn.hangtag.module.infra.controller.admin.file.vo.file.FilePageReqVO;
|
||||
import cn.hangtag.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
|
||||
import cn.hangtag.module.infra.controller.admin.file.vo.file.FileRespVO;
|
||||
import cn.hangtag.module.infra.dal.dataobject.file.FileDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件 Service 接口
|
||||
*
|
||||
|
|
@ -69,4 +73,32 @@ public interface FileService {
|
|||
* @return {@link String}
|
||||
*/
|
||||
String getDomain();
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param originalFilename 原始文件名
|
||||
* @param path 路径
|
||||
* @param bytes 字节
|
||||
* @return {@link FileRespVO }
|
||||
*/
|
||||
FileInfoVO uploadFile(String originalFilename, String path, byte[] bytes);
|
||||
|
||||
/**
|
||||
* 获取文件网址
|
||||
*
|
||||
* @param ids ID infra_file id 多个使用 , 分割
|
||||
* @return {@link List }<{@link String }>
|
||||
*/
|
||||
List<FileInfoVO> getFileInfo(String ids);
|
||||
|
||||
/**
|
||||
* 重命名
|
||||
*
|
||||
* @param id ID infra_file id
|
||||
* @param name 名称 文件名称 不包含后缀名称
|
||||
* @return {@link FileInfoVO }
|
||||
*/
|
||||
FileInfoVO rename(String id, String name);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
package cn.hangtag.module.infra.service.file;
|
||||
|
||||
import cn.hangtag.framework.common.exception.ErrorCode;
|
||||
import cn.hangtag.framework.common.exception.ServiceException;
|
||||
import cn.hangtag.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.hangtag.framework.common.pojo.FileInfoVO;
|
||||
import cn.hangtag.framework.common.util.FuncUtil;
|
||||
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.hangtag.module.infra.controller.admin.file.vo.file.FileRespVO;
|
||||
import cn.hangtag.module.infra.service.config.YmlUtils;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
|
|
@ -15,6 +22,7 @@ import cn.hangtag.module.infra.controller.admin.file.vo.file.FilePageReqVO;
|
|||
import cn.hangtag.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
|
||||
import cn.hangtag.module.infra.dal.dataobject.file.FileDO;
|
||||
import cn.hangtag.module.infra.dal.mysql.file.FileMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -24,8 +32,12 @@ import org.springframework.web.context.request.ServletRequestAttributes;
|
|||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.hangtag.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.hangtag.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS;
|
||||
|
|
@ -145,6 +157,97 @@ public class FileServiceImpl implements FileService {
|
|||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public FileInfoVO uploadFile(String name, String path, byte[] content) {
|
||||
// 计算默认的 path 名
|
||||
String type = FileTypeUtils.getMineType(content, name);
|
||||
if (StrUtil.isEmpty(path)) {
|
||||
path = FileUtils.generatePath(content, name);
|
||||
}
|
||||
// 如果 name 为空,则使用 path 填充
|
||||
if (StrUtil.isEmpty(name)) {
|
||||
name = path;
|
||||
}
|
||||
String suffix1 = FileUtil.getSuffix(name);
|
||||
if(!FileUtils.SUFFIXS.contains(suffix1)){
|
||||
throw new ServiceException(GlobalErrorCodeConstants.NOT_SUPPORT_FILE_TYPE);
|
||||
}
|
||||
// 上传到文件存储器
|
||||
FileClient client = fileConfigService.getMasterFileClient();
|
||||
Assert.notNull(client, "客户端(master) 不能为空");
|
||||
String url = client.upload(content, path, type);
|
||||
|
||||
// 保存到数据库
|
||||
FileDO file = new FileDO();
|
||||
file.setConfigId(client.getId());
|
||||
file.setName(name);
|
||||
file.setPath(path);
|
||||
file.setUrl(url);
|
||||
file.setType(type);
|
||||
file.setSize(content.length);
|
||||
file.setCreateTime(LocalDateTime.now());
|
||||
fileMapper.insert(file);
|
||||
return new FileInfoVO()
|
||||
.setId(file.getId())
|
||||
.setConfigId(client.getId())
|
||||
.setName(name)
|
||||
.setPath(path)
|
||||
.setUrl(url)
|
||||
.setType(type)
|
||||
.setSize(content.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FileInfoVO> getFileInfo(String ids) {
|
||||
List<FileInfoVO> list = new ArrayList<>();
|
||||
if(FuncUtil.isNotEmpty(ids)){
|
||||
LambdaQueryWrapper<FileDO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.in(FileDO::getId, FuncUtil.toStrList(ids));
|
||||
wrapper.eq(BaseDO::getDeleted,false);
|
||||
wrapper.orderByDesc(BaseDO::getCreateTime);
|
||||
List<FileDO> fileDOS = fileMapper.selectList(wrapper);
|
||||
for (FileDO fileDO : fileDOS) {
|
||||
list.add(
|
||||
new FileInfoVO()
|
||||
.setId(fileDO.getId())
|
||||
.setConfigId(fileDO.getConfigId())
|
||||
.setName(fileDO.getName())
|
||||
.setPath(fileDO.getName())
|
||||
.setUrl(fileDO.getUrl())
|
||||
.setType(fileDO.getType())
|
||||
.setSize(fileDO.getSize())
|
||||
);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileInfoVO rename(String id, String name) {
|
||||
FileDO fileDO = fileMapper.selectById(id);
|
||||
if(FuncUtil.isNotEmpty(fileDO.getName())){
|
||||
FileDO newInfo = new FileDO();
|
||||
String name1 = fileDO.getName();
|
||||
// 获取后缀
|
||||
String suffix1 = FileUtil.getSuffix(name1);
|
||||
fileDO.setName(name+"."+suffix1);
|
||||
newInfo.setName(fileDO.getName());
|
||||
newInfo.setId(fileDO.getId());
|
||||
fileMapper.updateById(newInfo);
|
||||
return new FileInfoVO()
|
||||
.setId(fileDO.getId())
|
||||
.setConfigId(fileDO.getConfigId())
|
||||
.setName(newInfo.getName())
|
||||
.setPath(fileDO.getPath())
|
||||
.setUrl(fileDO.getUrl())
|
||||
.setType(fileDO.getType())
|
||||
.setSize(fileDO.getSize());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String domain(HttpServletRequest request) {
|
||||
String domain = YmlUtils.get("localupload.domain");
|
||||
if(FuncUtil.isNotEmpty(domain)){
|
||||
|
|
|
|||
|
|
@ -27,5 +27,5 @@ public interface ErrorCodeConstants extends cn.hangtag.module.system.enums.Erro
|
|||
ErrorCode PRODUCT_PRICE_NOT_EXISTS = new ErrorCode(600, "产品单价记录不存在");
|
||||
ErrorCode CUSTOMER_EMAIL_EXISTS = new ErrorCode(600, "已存在重复的客户邮箱号");
|
||||
ErrorCode SALE_ORDER_NOT_FILE_EXPORT = new ErrorCode(7000, "订单中没有可导出的稿件");
|
||||
|
||||
ErrorCode CUSTOMER_GROUP_NOT_EXISTS = new ErrorCode(7100, "客户组别 不存在");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,10 +102,6 @@
|
|||
<artifactId>x-easypdf</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hangtag</groupId>
|
||||
<artifactId>hangtag-spring-boot-starter-protection</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ public class AppMaterialController {
|
|||
@GetMapping("/page")
|
||||
@Operation(summary = "获得产品资料 分页")
|
||||
public CommonResult<PageResult<ProductInfoRespVO>> getProductInfoPage(@Valid ProductInfoPageReqVO pageReqVO) {
|
||||
PageResult<ProductInfoDO> pageResult = productInfoService.getProductInfoPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, ProductInfoRespVO.class));
|
||||
PageResult<ProductInfoRespVO> pageResult = productInfoService.getProductInfoPage(pageReqVO);
|
||||
return success(pageResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
package cn.hangtag.module.oms.controller.admin.customergroup;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import javax.validation.*;
|
||||
import javax.servlet.http.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
import cn.hangtag.framework.common.pojo.PageParam;
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
import cn.hangtag.framework.common.pojo.CommonResult;
|
||||
import cn.hangtag.framework.common.util.object.BeanUtils;
|
||||
import static cn.hangtag.framework.common.pojo.CommonResult.success;
|
||||
|
||||
import cn.hangtag.framework.excel.core.util.ExcelUtils;
|
||||
|
||||
import cn.hangtag.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import static cn.hangtag.framework.apilog.core.enums.OperateTypeEnum.*;
|
||||
|
||||
import cn.hangtag.module.oms.controller.admin.customergroup.vo.*;
|
||||
import cn.hangtag.module.oms.dal.dataobject.customergroup.CustomerGroupDO;
|
||||
import cn.hangtag.module.oms.service.customergroup.CustomerGroupService;
|
||||
|
||||
@Tag(name = "管理后台 - 客户组别 ")
|
||||
@RestController
|
||||
@RequestMapping("/oms/customer-group")
|
||||
@Validated
|
||||
public class CustomerGroupController {
|
||||
|
||||
@Resource
|
||||
private CustomerGroupService customerGroupService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建客户组别 ")
|
||||
@PreAuthorize("@ss.hasPermission('oms:customer-group:create')")
|
||||
public CommonResult<Long> createCustomerGroup(@Valid @RequestBody CustomerGroupSaveReqVO createReqVO) {
|
||||
return success(customerGroupService.createCustomerGroup(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新客户组别 ")
|
||||
@PreAuthorize("@ss.hasPermission('oms:customer-group:update')")
|
||||
public CommonResult<Boolean> updateCustomerGroup(@Valid @RequestBody CustomerGroupSaveReqVO updateReqVO) {
|
||||
customerGroupService.updateCustomerGroup(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除客户组别 ")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('oms:customer-group:delete')")
|
||||
public CommonResult<Boolean> deleteCustomerGroup(@RequestParam("id") Long id) {
|
||||
customerGroupService.deleteCustomerGroup(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得客户组别 ")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
// @PreAuthorize("@ss.hasPermission('oms:customer-group:query')")
|
||||
public CommonResult<CustomerGroupRespVO> getCustomerGroup(@RequestParam("id") Long id) {
|
||||
CustomerGroupDO customerGroup = customerGroupService.getCustomerGroup(id);
|
||||
return success(BeanUtils.toBean(customerGroup, CustomerGroupRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得客户组别 分页")
|
||||
// @PreAuthorize("@ss.hasPermission('oms:customer-group:query')")
|
||||
public CommonResult<PageResult<CustomerGroupRespVO>> getCustomerGroupPage(@Valid CustomerGroupPageReqVO pageReqVO) {
|
||||
PageResult<CustomerGroupDO> pageResult = customerGroupService.getCustomerGroupPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, CustomerGroupRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出客户组别 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('oms:customer-group:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportCustomerGroupExcel(@Valid CustomerGroupPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<CustomerGroupDO> list = customerGroupService.getCustomerGroupPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "客户组别 .xls", "数据", CustomerGroupRespVO.class,
|
||||
BeanUtils.toBean(list, CustomerGroupRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package cn.hangtag.module.oms.controller.admin.customergroup.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.hangtag.framework.common.pojo.PageParam;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.hangtag.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 客户组别 分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CustomerGroupPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "名称", example = "张三")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "排序号")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "备注", example = "随便")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package cn.hangtag.module.oms.controller.admin.customergroup.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import com.alibaba.excel.annotation.*;
|
||||
|
||||
@Schema(description = "管理后台 - 客户组别 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class CustomerGroupRespVO {
|
||||
|
||||
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "5719")
|
||||
@ExcelProperty("id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "编码")
|
||||
@ExcelProperty("编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
@ExcelProperty("名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "排序号")
|
||||
@ExcelProperty("排序号")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "备注", example = "随便")
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package cn.hangtag.module.oms.controller.admin.customergroup.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
@Schema(description = "管理后台 - 客户组别 新增/修改 Request VO")
|
||||
@Data
|
||||
public class CustomerGroupSaveReqVO {
|
||||
|
||||
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "5719")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "编码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
@NotEmpty(message = "名称不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "排序号")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "备注", example = "随便")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
|
|
@ -26,6 +26,9 @@ public class DraftDesignDataRespVO {
|
|||
@ExcelProperty("封面")
|
||||
private String cover;
|
||||
|
||||
@Schema(description = "底稿附件多个 infra_file 表id")
|
||||
private String fileIds;
|
||||
|
||||
@Schema(description = "设计稿名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
|
||||
@ExcelProperty("设计稿名称")
|
||||
private String name;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ public class DraftDesignDataSaveReqVO {
|
|||
@Schema(description = "封面")
|
||||
private String cover;
|
||||
|
||||
@Schema(description = "底稿附件多个 infra_file 表id")
|
||||
private String fileIds;
|
||||
|
||||
@Schema(description = "设计稿名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
|
||||
@NotEmpty(message = "设计稿名称不能为空")
|
||||
private String name;
|
||||
|
|
|
|||
|
|
@ -70,16 +70,16 @@ public class ProductInfoController {
|
|||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
// @PreAuthorize("@ss.hasPermission('oms:product-info:query')")
|
||||
public CommonResult<ProductInfoRespVO> getProductInfo(@RequestParam("id") Long id) {
|
||||
ProductInfoDO productInfo = productInfoService.getProductInfo(id);
|
||||
return success(BeanUtils.toBean(productInfo, ProductInfoRespVO.class));
|
||||
ProductInfoRespVO vo = productInfoService.getFrontProductInfo(id);
|
||||
return success(vo);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得产品资料 分页")
|
||||
// @PreAuthorize("@ss.hasPermission('oms:product-info:query')")
|
||||
public CommonResult<PageResult<ProductInfoRespVO>> getProductInfoPage(@Valid ProductInfoPageReqVO pageReqVO) {
|
||||
PageResult<ProductInfoDO> pageResult = productInfoService.getProductInfoPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, ProductInfoRespVO.class));
|
||||
PageResult<ProductInfoRespVO> pageResult = productInfoService.getProductInfoPage(pageReqVO);
|
||||
return success(pageResult);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -87,8 +87,8 @@ public class ProductInfoController {
|
|||
@Operation(summary = "获得产品资料 分页")
|
||||
// @PreAuthorize("@ss.hasPermission('oms:product-info:query')")
|
||||
public CommonResult<PageResult<ProductInfoRespVO>> queryPage(@RequestBody QueryFilterInfo<ProductInfoPageReqVO> queryFilterInfo) {
|
||||
PageResult<ProductInfoDO> pageResult = productInfoService.queryPage(queryFilterInfo);
|
||||
return success(BeanUtils.toBean(pageResult, ProductInfoRespVO.class));
|
||||
PageResult<ProductInfoRespVO> pageResult = productInfoService.queryPage(queryFilterInfo);
|
||||
return success(pageResult);
|
||||
}
|
||||
|
||||
@GetMapping("/excel-template")
|
||||
|
|
@ -114,10 +114,9 @@ public class ProductInfoController {
|
|||
public void exportProductInfoExcel(@Valid ProductInfoPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<ProductInfoDO> list = productInfoService.getProductInfoPage(pageReqVO).getList();
|
||||
List<ProductInfoRespVO> list = productInfoService.getProductInfoPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "产品资料 .xls", "数据", ProductInfoRespVO.class,
|
||||
BeanUtils.toBean(list, ProductInfoRespVO.class));
|
||||
ExcelUtils.write(response, "产品资料 .xls", "数据", ProductInfoRespVO.class,list);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -30,6 +30,13 @@ public class ProductInfoPageReqVO extends PageParam {
|
|||
@Schema(description = "品牌", example = "12523")
|
||||
private Long brandId;
|
||||
|
||||
@Schema(description = "客户组别id oms_customer_group", example = "12523,1233")
|
||||
private String customerGroupId;
|
||||
|
||||
@Schema(description = "客户组别 oms_customer_group name", example = "vip")
|
||||
private String customerGroupName;
|
||||
|
||||
|
||||
@Schema(description = "产品类型id", example = "26002")
|
||||
private Long productTypeId;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,13 @@ public class ProductInfoRespVO {
|
|||
@ExcelProperty("品牌")
|
||||
private Long brandId;
|
||||
|
||||
@Schema(description = "客户组别id oms_customer_group", example = "12523,1233")
|
||||
private String customerGroupId;
|
||||
|
||||
@Schema(description = "客户组别 oms_customer_group name", example = "vip")
|
||||
private String customerGroupName;
|
||||
|
||||
|
||||
@Schema(description = "产品类型id", example = "26002")
|
||||
@ExcelProperty("产品类型id")
|
||||
private Long productTypeId;
|
||||
|
|
@ -85,6 +92,11 @@ public class ProductInfoRespVO {
|
|||
*/
|
||||
private String currency;
|
||||
|
||||
/**
|
||||
* 底稿文件id
|
||||
*/
|
||||
private String fileIds;
|
||||
|
||||
/**
|
||||
* json数组数据 [{p:100.25,c:"RMB",d:true}]
|
||||
* 1.p:价格 2.c:币种 3.d:是否默认
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ public class ProductInfoSaveReqVO {
|
|||
@Schema(description = "品牌", example = "12523")
|
||||
private Long brandId;
|
||||
|
||||
@Schema(description = "客户组别id oms_customer_group", example = "12523,1233")
|
||||
private String customerGroupId;
|
||||
|
||||
@Schema(description = "产品类型id", example = "26002")
|
||||
private Long productTypeId;
|
||||
|
||||
|
|
|
|||
|
|
@ -120,6 +120,12 @@ public class CreateSaleOrderDTO implements Serializable {
|
|||
*/
|
||||
private String rejectReason;
|
||||
|
||||
/**
|
||||
* 下单客户上传的 附件文件 infra_file id
|
||||
* 允许多个
|
||||
*/
|
||||
private String accessoryFileIds;
|
||||
|
||||
private List<SaleOrderEntryItemDTO> saleOrderEntry;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
package cn.hangtag.module.oms.dal.dataobject.customergroup;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* 客户组别 DO
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
@TableName("oms_customer_group")
|
||||
@KeySequence("oms_customer_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class CustomerGroupDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
private Integer sort;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
|
|
@ -36,6 +36,14 @@ public class DraftDesignDataDO extends BaseDO {
|
|||
* 封面
|
||||
*/
|
||||
private String cover;
|
||||
|
||||
/**
|
||||
* 底稿附件多个 infra_file 表id
|
||||
* 多个用,连接
|
||||
*/
|
||||
@TableField("file_ids")
|
||||
private String fileIds;
|
||||
|
||||
/**
|
||||
* 设计稿名称
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -51,11 +51,13 @@ public class ProductInfoDO extends BaseDO {
|
|||
*/
|
||||
private Long productTypeId;
|
||||
/**
|
||||
* 设计稿id
|
||||
* 设计稿id 12,22
|
||||
* oms_draft_design_data id 多个用,连接
|
||||
*/
|
||||
private String draftDesignDataId;
|
||||
/**
|
||||
* 设计稿列表 json数据带名称
|
||||
* [{"remark":"","label":"默认jq4","id":16}]
|
||||
*/
|
||||
private String draftDesignList;
|
||||
|
||||
|
|
@ -124,6 +126,12 @@ public class ProductInfoDO extends BaseDO {
|
|||
@TableField("template_type")
|
||||
private String templateType;
|
||||
|
||||
@Schema(description = "客户组别id oms_customer_group", example = "12523,1233")
|
||||
@TableField("customer_group_id")
|
||||
private String customerGroupId;
|
||||
|
||||
@TableField(exist = false)
|
||||
private String customerGroupName;
|
||||
|
||||
/**
|
||||
* 品牌名称
|
||||
|
|
@ -137,4 +145,6 @@ public class ProductInfoDO extends BaseDO {
|
|||
@TableField(exist = false)
|
||||
private String productTypeName;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -178,6 +178,13 @@ public class SaleOrderDO extends BaseDO {
|
|||
*/
|
||||
private String currencyType;
|
||||
|
||||
/**
|
||||
* 下单客户上传的 附件文件 infra_file id
|
||||
* 允许多个
|
||||
*/
|
||||
@TableField("accessory_file_ids")
|
||||
private String accessoryFileIds;
|
||||
|
||||
public SaleOrderDO(CreateSaleOrderDTO dto) {
|
||||
BeanUtil.copyProperties(dto, this,"bizdate","plansenddate");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package cn.hangtag.module.oms.dal.mysql.customergroup;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
import cn.hangtag.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.hangtag.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.hangtag.module.oms.dal.dataobject.customergroup.CustomerGroupDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import cn.hangtag.module.oms.controller.admin.customergroup.vo.*;
|
||||
|
||||
/**
|
||||
* 客户组别 Mapper
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
@Mapper
|
||||
public interface CustomerGroupMapper extends BaseMapperX<CustomerGroupDO> {
|
||||
|
||||
default PageResult<CustomerGroupDO> selectPage(CustomerGroupPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<CustomerGroupDO>()
|
||||
.likeIfPresent(CustomerGroupDO::getCode, reqVO.getCode())
|
||||
.likeIfPresent(CustomerGroupDO::getName, reqVO.getName())
|
||||
.eqIfPresent(CustomerGroupDO::getSort, reqVO.getSort())
|
||||
.eqIfPresent(CustomerGroupDO::getRemark, reqVO.getRemark())
|
||||
.betweenIfPresent(CustomerGroupDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(CustomerGroupDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@ public interface ProductInfoMapper extends BaseMapperX<ProductInfoDO> {
|
|||
.eqIfPresent(ProductInfoDO::getDraftDesignList, reqVO.getDraftDesignList())
|
||||
.eqIfPresent(ProductInfoDO::getEnabled, reqVO.getEnabled())
|
||||
.eqIfPresent(ProductInfoDO::getRemark, reqVO.getRemark())
|
||||
.eqIfPresent(ProductInfoDO::getCustomerGroupId, reqVO.getCustomerGroupId())
|
||||
.eqIfPresent(ProductInfoDO::getDetails, reqVO.getDetails())
|
||||
.betweenIfPresent(ProductInfoDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(ProductInfoDO::getId));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
package cn.hangtag.module.oms.service.customergroup;
|
||||
|
||||
import java.util.*;
|
||||
import javax.validation.*;
|
||||
import cn.hangtag.module.oms.controller.admin.customergroup.vo.*;
|
||||
import cn.hangtag.module.oms.dal.dataobject.customergroup.CustomerGroupDO;
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
import cn.hangtag.framework.common.pojo.PageParam;
|
||||
|
||||
/**
|
||||
* 客户组别 Service 接口
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
public interface CustomerGroupService {
|
||||
|
||||
/**
|
||||
* 创建客户组别
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createCustomerGroup(@Valid CustomerGroupSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新客户组别
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateCustomerGroup(@Valid CustomerGroupSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除客户组别
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteCustomerGroup(Long id);
|
||||
|
||||
/**
|
||||
* 获得客户组别
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 客户组别
|
||||
*/
|
||||
CustomerGroupDO getCustomerGroup(Long id);
|
||||
|
||||
/**
|
||||
* 获得客户组别 分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 客户组别 分页
|
||||
*/
|
||||
PageResult<CustomerGroupDO> getCustomerGroupPage(CustomerGroupPageReqVO pageReqVO);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package cn.hangtag.module.oms.service.customergroup;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import cn.hangtag.module.oms.controller.admin.customergroup.vo.*;
|
||||
import cn.hangtag.module.oms.dal.dataobject.customergroup.CustomerGroupDO;
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
import cn.hangtag.framework.common.pojo.PageParam;
|
||||
import cn.hangtag.framework.common.util.object.BeanUtils;
|
||||
|
||||
import cn.hangtag.module.oms.dal.mysql.customergroup.CustomerGroupMapper;
|
||||
|
||||
import static cn.hangtag.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.hangtag.module.oms.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 客户组别 Service 实现类
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class CustomerGroupServiceImpl implements CustomerGroupService {
|
||||
|
||||
@Resource
|
||||
private CustomerGroupMapper customerGroupMapper;
|
||||
|
||||
@Override
|
||||
public Long createCustomerGroup(CustomerGroupSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
CustomerGroupDO customerGroup = BeanUtils.toBean(createReqVO, CustomerGroupDO.class);
|
||||
customerGroupMapper.insert(customerGroup);
|
||||
// 返回
|
||||
return customerGroup.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCustomerGroup(CustomerGroupSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateCustomerGroupExists(updateReqVO.getId());
|
||||
// 更新
|
||||
CustomerGroupDO updateObj = BeanUtils.toBean(updateReqVO, CustomerGroupDO.class);
|
||||
customerGroupMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteCustomerGroup(Long id) {
|
||||
// 校验存在
|
||||
validateCustomerGroupExists(id);
|
||||
// 删除
|
||||
customerGroupMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateCustomerGroupExists(Long id) {
|
||||
if (customerGroupMapper.selectById(id) == null) {
|
||||
throw exception(CUSTOMER_GROUP_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomerGroupDO getCustomerGroup(Long id) {
|
||||
return customerGroupMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<CustomerGroupDO> getCustomerGroupPage(CustomerGroupPageReqVO pageReqVO) {
|
||||
return customerGroupMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -48,15 +48,23 @@ public interface ProductInfoService {
|
|||
*/
|
||||
ProductInfoDO getProductInfo(Long id);
|
||||
|
||||
/**
|
||||
* 获取前台产品信息
|
||||
*
|
||||
* @param id ID
|
||||
* @return {@link ProductInfoRespVO }
|
||||
*/
|
||||
ProductInfoRespVO getFrontProductInfo(Long id);
|
||||
|
||||
/**
|
||||
* 获得产品资料 分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 产品资料 分页
|
||||
*/
|
||||
PageResult<ProductInfoDO> getProductInfoPage(ProductInfoPageReqVO pageReqVO);
|
||||
PageResult<ProductInfoRespVO> getProductInfoPage(ProductInfoPageReqVO pageReqVO);
|
||||
|
||||
PageResult<ProductInfoDO> queryPage(QueryFilterInfo<ProductInfoPageReqVO> queryFilterInfo);
|
||||
PageResult<ProductInfoRespVO> queryPage(QueryFilterInfo<ProductInfoPageReqVO> queryFilterInfo);
|
||||
|
||||
/**
|
||||
* 获取编码
|
||||
|
|
@ -75,4 +83,5 @@ public interface ProductInfoService {
|
|||
BigDecimal queryPriceByProductId(Long productId, String currency);
|
||||
|
||||
String importExcel(List<ProductInfoExcelVO> list);
|
||||
|
||||
}
|
||||
|
|
@ -13,9 +13,12 @@ import cn.hangtag.module.oms.controller.admin.productinfo.dto.PriceListItemDTO;
|
|||
import cn.hangtag.module.oms.controller.admin.productinfo.dto.ProductInfoPageDTO;
|
||||
import cn.hangtag.module.oms.controller.admin.productprocess.vo.ProductProcessSaveReqVO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.brand.BrandDO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.customergroup.CustomerGroupDO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.draftdesigndata.DraftDesignDataDO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.productprocess.ProductProcessDO;
|
||||
import cn.hangtag.module.oms.dal.mysql.brand.BrandMapper;
|
||||
import cn.hangtag.module.oms.dal.mysql.customergroup.CustomerGroupMapper;
|
||||
import cn.hangtag.module.oms.dal.mysql.draftdesigndata.DraftDesignDataMapper;
|
||||
import cn.hangtag.module.oms.dal.mysql.productprocess.ProductProcessMapper;
|
||||
import cn.hangtag.module.oms.serialnumber.CodingRulesUtils;
|
||||
import cn.hangtag.module.oms.service.brand.BrandService;
|
||||
|
|
@ -59,10 +62,11 @@ public class ProductInfoServiceImpl implements ProductInfoService {
|
|||
private final ProductInfoMapper productInfoMapper;
|
||||
private final BrandMapper brandMapper;
|
||||
private final ProductTypeMapper productTypeMapper;
|
||||
private final CustomerGroupMapper customerGroupMapper;
|
||||
private final ProductProcessMapper productProcessMapper;
|
||||
private final BrandService brandService;
|
||||
private final DraftDesignDataService draftDesignDataService;
|
||||
|
||||
private final DraftDesignDataMapper draftDesignDataMapper;
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createProductInfo(ProductInfoSaveReqVO createReqVO) {
|
||||
|
|
@ -169,50 +173,29 @@ public class ProductInfoServiceImpl implements ProductInfoService {
|
|||
public ProductInfoDO getProductInfo(Long id) {
|
||||
return productInfoMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<ProductInfoDO> getProductInfoPage(ProductInfoPageReqVO pageReqVO) {
|
||||
PageResult<ProductInfoDO> productInfoDOPageResult = productInfoMapper.selectPage(pageReqVO);
|
||||
List<ProductInfoDO> list = productInfoDOPageResult.getList();
|
||||
list.forEach(productInfoDO -> {
|
||||
if(FuncUtil.isNotEmpty(productInfoDO.getBrandId())){
|
||||
BrandDO brandDO = brandMapper.selectById(productInfoDO.getBrandId());
|
||||
if(FuncUtil.isNotEmpty(brandDO)){
|
||||
productInfoDO.setBrandName(brandDO.getName());
|
||||
}
|
||||
}
|
||||
if(FuncUtil.isNotEmpty(productInfoDO.getProductTypeId())){
|
||||
ProductTypeDO productTypeDO = productTypeMapper.selectById(productInfoDO.getProductTypeId());
|
||||
if(FuncUtil.isNotEmpty(productTypeDO)){
|
||||
productInfoDO.setProductTypeName(productTypeDO.getLabel());
|
||||
}
|
||||
}
|
||||
});
|
||||
productInfoDOPageResult.setList(list);
|
||||
return productInfoDOPageResult;
|
||||
public ProductInfoRespVO getFrontProductInfo(Long id) {
|
||||
ProductInfoDO infoDO = productInfoMapper.selectById(id);
|
||||
ProductInfoRespVO vo = BeanUtils.toBean(infoDO, ProductInfoRespVO.class);
|
||||
return wrapperFileIds(vo);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PageResult<ProductInfoDO> queryPage(QueryFilterInfo<ProductInfoPageReqVO> queryFilterInfo) {
|
||||
public PageResult<ProductInfoRespVO> getProductInfoPage(ProductInfoPageReqVO pageReqVO) {
|
||||
PageResult<ProductInfoDO> productInfoDOPageResult = productInfoMapper.selectPage(pageReqVO);
|
||||
List<ProductInfoDO> list = productInfoDOPageResult.getList();
|
||||
List<ProductInfoRespVO> resList = wrapperRespVO(list);
|
||||
return new PageResult<>(resList, productInfoDOPageResult.getTotal());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PageResult<ProductInfoRespVO> queryPage(QueryFilterInfo<ProductInfoPageReqVO> queryFilterInfo) {
|
||||
PageResult<ProductInfoDO> productInfoDOPageResult = productInfoMapper.selectPagePlus(queryFilterInfo);
|
||||
List<ProductInfoDO> list = productInfoDOPageResult.getList();
|
||||
list.forEach(productInfoDO -> {
|
||||
if(FuncUtil.isNotEmpty(productInfoDO.getBrandId())){
|
||||
BrandDO brandDO = brandMapper.selectById(productInfoDO.getBrandId());
|
||||
if(FuncUtil.isNotEmpty(brandDO)){
|
||||
productInfoDO.setBrandName(brandDO.getName());
|
||||
}
|
||||
}
|
||||
if(FuncUtil.isNotEmpty(productInfoDO.getProductTypeId())){
|
||||
ProductTypeDO productTypeDO = productTypeMapper.selectById(productInfoDO.getProductTypeId());
|
||||
if(FuncUtil.isNotEmpty(productTypeDO)){
|
||||
productInfoDO.setProductTypeName(productTypeDO.getLabel());
|
||||
}
|
||||
}
|
||||
});
|
||||
productInfoDOPageResult.setList(list);
|
||||
return productInfoDOPageResult;
|
||||
List<ProductInfoRespVO> resList = wrapperRespVO(list);
|
||||
return new PageResult<>(resList, productInfoDOPageResult.getTotal());
|
||||
}
|
||||
|
||||
private void checkCode(Long id, String code){
|
||||
|
|
@ -273,6 +256,7 @@ public class ProductInfoServiceImpl implements ProductInfoService {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String importExcel(List<ProductInfoExcelVO> list) {
|
||||
List<ProductInfoDO> newList = new ArrayList<>();
|
||||
|
|
@ -306,4 +290,87 @@ public class ProductInfoServiceImpl implements ProductInfoService {
|
|||
productInfoMapper.insertBatch(newList);
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
private List<ProductInfoRespVO> wrapperRespVO(List<ProductInfoDO> list){
|
||||
List<ProductInfoRespVO> resList = new ArrayList<>();
|
||||
list.forEach(productInfoDO -> {
|
||||
ProductInfoRespVO vo = BeanUtils.toBean(productInfoDO, ProductInfoRespVO.class);
|
||||
wrapperBrandName(vo);
|
||||
wrapperProductTypeName(vo);
|
||||
wrapperCustomerGroupName(vo);
|
||||
wrapperFileIds(vo);
|
||||
resList.add(vo);
|
||||
});
|
||||
return resList;
|
||||
}
|
||||
private ProductInfoRespVO wrapperFileIds(ProductInfoRespVO vo){
|
||||
if(FuncUtil.isNotEmpty(vo)){
|
||||
String draftDesignDataIds = vo.getDraftDesignDataId();
|
||||
if(FuncUtil.isNotEmpty(draftDesignDataIds)){
|
||||
LambdaQueryWrapper<DraftDesignDataDO> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.in(DraftDesignDataDO::getId, Arrays.asList(draftDesignDataIds.split(",")));
|
||||
queryWrapper.eq(DraftDesignDataDO::getDeleted, false);
|
||||
queryWrapper.isNotNull(DraftDesignDataDO::getFileIds);
|
||||
queryWrapper.orderByDesc(BaseDO::getCreateTime);
|
||||
List<DraftDesignDataDO> dos = draftDesignDataMapper.selectList(queryWrapper);
|
||||
List<String> fileIds = new ArrayList<>();
|
||||
if(FuncUtil.isNotEmpty(dos)){
|
||||
for (DraftDesignDataDO designDataDO : dos) {
|
||||
List<String> strList = FuncUtil.toStrList(designDataDO.getFileIds());
|
||||
fileIds.addAll(strList);
|
||||
}
|
||||
}
|
||||
if(FuncUtil.isNotEmpty(fileIds)){
|
||||
vo.setFileIds(String.join(",",fileIds));
|
||||
}
|
||||
}
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
private ProductInfoRespVO wrapperCustomerGroupName(ProductInfoRespVO vo){
|
||||
if(FuncUtil.isNotEmpty(vo)){
|
||||
if(FuncUtil.isNotEmpty(vo.getCustomerGroupId())){
|
||||
LambdaQueryWrapper<CustomerGroupDO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.in(CustomerGroupDO::getId, FuncUtil.toStrList(vo.getCustomerGroupId()));
|
||||
wrapper.eq(BaseDO::getDeleted,false);
|
||||
List<CustomerGroupDO> groupDOS = customerGroupMapper.selectList(wrapper);
|
||||
String name = null;
|
||||
if(FuncUtil.isNotEmpty(groupDOS)){
|
||||
List<String> names = new ArrayList<>();
|
||||
for (CustomerGroupDO groupDO : groupDOS) {
|
||||
if(names.contains(groupDO.getName())){
|
||||
continue;
|
||||
}
|
||||
names.add(groupDO.getName());
|
||||
}
|
||||
name = String.join(",", names);
|
||||
}
|
||||
vo.setCustomerGroupName(name);
|
||||
}
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
private ProductInfoRespVO wrapperProductTypeName(ProductInfoRespVO vo){
|
||||
if(FuncUtil.isNotEmpty(vo)){
|
||||
if(FuncUtil.isNotEmpty(vo.getProductTypeId())){
|
||||
ProductTypeDO productTypeDO = productTypeMapper.selectById(vo.getProductTypeId());
|
||||
if(FuncUtil.isNotEmpty(productTypeDO)){
|
||||
vo.setProductTypeName(productTypeDO.getLabel());
|
||||
}
|
||||
}
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
private ProductInfoRespVO wrapperBrandName(ProductInfoRespVO vo){
|
||||
if(FuncUtil.isNotEmpty(vo)){
|
||||
if(FuncUtil.isNotEmpty(vo.getBrandId())){
|
||||
BrandDO brandDO = brandMapper.selectById(vo.getBrandId());
|
||||
if(FuncUtil.isNotEmpty(brandDO)){
|
||||
vo.setBrandName(brandDO.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="cn.hangtag.module.oms.dal.mysql.customergroup.CustomerGroupMapper">
|
||||
|
||||
<!--
|
||||
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
|
||||
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
|
||||
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
|
||||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
||||
-->
|
||||
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
package cn.hangtag.module.oms.service.customergroup;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import cn.hangtag.framework.test.core.ut.BaseDbUnitTest;
|
||||
|
||||
import cn.hangtag.module.oms.controller.admin.customergroup.vo.*;
|
||||
import cn.hangtag.module.oms.dal.dataobject.customergroup.CustomerGroupDO;
|
||||
import cn.hangtag.module.oms.dal.mysql.customergroup.CustomerGroupMapper;
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.*;
|
||||
import static cn.hangtag.module.oms.enums.ErrorCodeConstants.*;
|
||||
import static cn.hangtag.framework.test.core.util.AssertUtils.*;
|
||||
import static cn.hangtag.framework.test.core.util.RandomUtils.*;
|
||||
import static cn.hangtag.framework.common.util.date.LocalDateTimeUtils.*;
|
||||
import static cn.hangtag.framework.common.util.object.ObjectUtils.*;
|
||||
import static cn.hangtag.framework.common.util.date.DateUtils.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link CustomerGroupServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 管理员
|
||||
*/
|
||||
@Import(CustomerGroupServiceImpl.class)
|
||||
public class CustomerGroupServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private CustomerGroupServiceImpl customerGroupService;
|
||||
|
||||
@Resource
|
||||
private CustomerGroupMapper customerGroupMapper;
|
||||
|
||||
@Test
|
||||
public void testCreateCustomerGroup_success() {
|
||||
// 准备参数
|
||||
CustomerGroupSaveReqVO createReqVO = randomPojo(CustomerGroupSaveReqVO.class).setId(null);
|
||||
|
||||
// 调用
|
||||
Long customerGroupId = customerGroupService.createCustomerGroup(createReqVO);
|
||||
// 断言
|
||||
assertNotNull(customerGroupId);
|
||||
// 校验记录的属性是否正确
|
||||
CustomerGroupDO customerGroup = customerGroupMapper.selectById(customerGroupId);
|
||||
assertPojoEquals(createReqVO, customerGroup, "id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCustomerGroup_success() {
|
||||
// mock 数据
|
||||
CustomerGroupDO dbCustomerGroup = randomPojo(CustomerGroupDO.class);
|
||||
customerGroupMapper.insert(dbCustomerGroup);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
CustomerGroupSaveReqVO updateReqVO = randomPojo(CustomerGroupSaveReqVO.class, o -> {
|
||||
o.setId(dbCustomerGroup.getId()); // 设置更新的 ID
|
||||
});
|
||||
|
||||
// 调用
|
||||
customerGroupService.updateCustomerGroup(updateReqVO);
|
||||
// 校验是否更新正确
|
||||
CustomerGroupDO customerGroup = customerGroupMapper.selectById(updateReqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(updateReqVO, customerGroup);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCustomerGroup_notExists() {
|
||||
// 准备参数
|
||||
CustomerGroupSaveReqVO updateReqVO = randomPojo(CustomerGroupSaveReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> customerGroupService.updateCustomerGroup(updateReqVO), CUSTOMER_GROUP_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteCustomerGroup_success() {
|
||||
// mock 数据
|
||||
CustomerGroupDO dbCustomerGroup = randomPojo(CustomerGroupDO.class);
|
||||
customerGroupMapper.insert(dbCustomerGroup);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbCustomerGroup.getId();
|
||||
|
||||
// 调用
|
||||
customerGroupService.deleteCustomerGroup(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(customerGroupMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteCustomerGroup_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> customerGroupService.deleteCustomerGroup(id), CUSTOMER_GROUP_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
|
||||
public void testGetCustomerGroupPage() {
|
||||
// mock 数据
|
||||
CustomerGroupDO dbCustomerGroup = randomPojo(CustomerGroupDO.class, o -> { // 等会查询到
|
||||
o.setCode(null);
|
||||
o.setName(null);
|
||||
o.setRemark(null);
|
||||
o.setCreateTime(null);
|
||||
});
|
||||
customerGroupMapper.insert(dbCustomerGroup);
|
||||
// 测试 code 不匹配
|
||||
customerGroupMapper.insert(cloneIgnoreId(dbCustomerGroup, o -> o.setCode(null)));
|
||||
// 测试 name 不匹配
|
||||
customerGroupMapper.insert(cloneIgnoreId(dbCustomerGroup, o -> o.setName(null)));
|
||||
// 测试 remark 不匹配
|
||||
customerGroupMapper.insert(cloneIgnoreId(dbCustomerGroup, o -> o.setRemark(null)));
|
||||
// 测试 createTime 不匹配
|
||||
customerGroupMapper.insert(cloneIgnoreId(dbCustomerGroup, o -> o.setCreateTime(null)));
|
||||
// 准备参数
|
||||
CustomerGroupPageReqVO reqVO = new CustomerGroupPageReqVO();
|
||||
reqVO.setCode(null);
|
||||
reqVO.setName(null);
|
||||
reqVO.setRemark(null);
|
||||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
|
||||
|
||||
// 调用
|
||||
PageResult<CustomerGroupDO> pageResult = customerGroupService.getCustomerGroupPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbCustomerGroup, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -160,11 +160,11 @@ public class ProductInfoServiceImplTest extends BaseDbUnitTest {
|
|||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
|
||||
|
||||
// 调用
|
||||
PageResult<ProductInfoDO> pageResult = productInfoService.getProductInfoPage(reqVO);
|
||||
// PageResult<ProductInfoDO> pageResult = productInfoService.getProductInfoPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbProductInfo, pageResult.getList().get(0));
|
||||
// assertEquals(1, pageResult.getTotal());
|
||||
// assertEquals(1, pageResult.getList().size());
|
||||
// assertPojoEquals(dbProductInfo, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -47,9 +47,9 @@ spring:
|
|||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://127.0.0.1:3306/hangtag-uat?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
|
||||
url: jdbc:mysql://202.74.40.60:33061/oms-uat?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
|
||||
username: root
|
||||
password: 123456
|
||||
password: qygo5gYNhivG
|
||||
# slave: # 模拟从库,可根据自己需要修改
|
||||
# lazy: true # 开启懒加载,保证启动速度
|
||||
# url: jdbc:mysql://43.136.71.164:3306/hangtag?allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&useUnicode=true&characterEncoding=UTF-8
|
||||
|
|
|
|||
|
|
@ -49,3 +49,21 @@ export const updateFile = (data: any) => {
|
|||
export const getDomain = () => {
|
||||
return request.get({ url: '/infra/file/domain'})
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件 返回文件信息
|
||||
* @param data
|
||||
*/
|
||||
export const updateFilePlus = (data: any) => {
|
||||
return request.upload({ url: '/infra/file/upload-plus', data })
|
||||
}
|
||||
/**
|
||||
* 根据id 获取文件信息
|
||||
* @param ids
|
||||
*/
|
||||
export const infoByIds = (ids: string) => {
|
||||
return request.get({ url: '/infra/file/info/'+ids })
|
||||
}
|
||||
export const renameById = (id: string, name: string) => {
|
||||
return request.post({ url:`/infra/file/rename/${id}`,data:{name} })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
// 客户组别 VO
|
||||
export interface CustomerGroupVO {
|
||||
id: number // id
|
||||
code: string // 编码
|
||||
name: string // 名称
|
||||
sort: number // 排序号
|
||||
remark: string // 备注
|
||||
}
|
||||
|
||||
// 客户组别 API
|
||||
export const CustomerGroupApi = {
|
||||
// 查询客户组别 分页
|
||||
getCustomerGroupPage: async (params: any) => {
|
||||
return await request.get({ url: `/oms/customer-group/page`, params })
|
||||
},
|
||||
|
||||
// 查询客户组别 详情
|
||||
getCustomerGroup: async (id: number) => {
|
||||
return await request.get({ url: `/oms/customer-group/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增客户组别
|
||||
createCustomerGroup: async (data: CustomerGroupVO) => {
|
||||
return await request.post({ url: `/oms/customer-group/create`, data })
|
||||
},
|
||||
|
||||
// 修改客户组别
|
||||
updateCustomerGroup: async (data: CustomerGroupVO) => {
|
||||
return await request.put({ url: `/oms/customer-group/update`, data })
|
||||
},
|
||||
|
||||
// 删除客户组别
|
||||
deleteCustomerGroup: async (id: number) => {
|
||||
return await request.delete({ url: `/oms/customer-group/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出客户组别 Excel
|
||||
exportCustomerGroup: async (params) => {
|
||||
return await request.download({ url: `/oms/customer-group/export-excel`, params })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="w-full">
|
||||
<el-button @click="dialogVisible = true" :size="size" type="primary">
|
||||
<Icon icon="ep:download"/>
|
||||
{{ t('common.fileDownloadBtnText') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<Dialog
|
||||
v-model="dialogVisible"
|
||||
:title="t('common.fileDownloadTitle')"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:show-close="false"
|
||||
:destroy-on-close="true"
|
||||
:close-on-hash-change="false"
|
||||
>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
stripe
|
||||
:data="fileList"
|
||||
border
|
||||
style="width: 100%">
|
||||
<el-table-column prop="name" :label="t('common.fileDownloadNameLabel')"/>
|
||||
<el-table-column prop="size" :label="t('common.fileDownloadSizeLabel')">
|
||||
<template #default="scope">
|
||||
{{ formatBytes(scope.row.size) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="url" :label="t('common.fileDownloadOptionsLabel')">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<el-link
|
||||
:href="scope.row.url"
|
||||
:underline="false"
|
||||
download
|
||||
target="_blank"
|
||||
type="primary"
|
||||
>
|
||||
<Icon icon="ep:download" :size="24"/>
|
||||
</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// @ts-nocheck
|
||||
import {propTypes} from '@/utils/propTypes'
|
||||
import type {UploadUserFile} from 'element-plus'
|
||||
import {infoByIds} from "@/api/infra/file";
|
||||
import {useMessage} from "@/hooks/web/useMessage";
|
||||
import {useI18n} from "@/hooks/web/useI18n";
|
||||
const {t} = useI18n()
|
||||
// 附件上传
|
||||
defineOptions({name: 'AccessoryDownload'})
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const text = t('fileUploadBtnText');
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
||||
size:{
|
||||
// "" | "default" | "small" | "large"
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const dialogVisible = ref(false);
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
const that = reactive({
|
||||
ids: [],
|
||||
})
|
||||
const formatBytes = (bytes: any, decimals = 2) =>{
|
||||
if (bytes === null || bytes === undefined) {
|
||||
return ''
|
||||
}
|
||||
if (bytes === 0) return '0 Bytes'
|
||||
|
||||
const k = 1024
|
||||
const dm = decimals < 0 ? 0 : decimals
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
const res = parseFloat((bytes / Math.pow(k, i)).toFixed(dm))
|
||||
if (isNaN(res)) {
|
||||
return '0 ' + sizes[0]
|
||||
}
|
||||
return res + ' ' + sizes[i]
|
||||
}
|
||||
// 监听模型绑定值变动
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: string | string[]) => {
|
||||
if (!val) {
|
||||
fileList.value = [] // fix:处理掉缓存,表单重置后上传组件的内容并没有重置
|
||||
return
|
||||
}
|
||||
const arr = `${val}`.split(",");
|
||||
const query = [];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const item = `${arr[i]}`
|
||||
if(!that.ids.includes(item)){
|
||||
query.push(item)
|
||||
}
|
||||
}
|
||||
if(query.length > 0){
|
||||
infoByIds(query.join(",")).then((res) => {
|
||||
console.log("init", res)
|
||||
if (res) {
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
const item = res[i];
|
||||
that.ids.push(`${item.id}`)
|
||||
fileList.value.push({
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
size: item.size,
|
||||
// 'ready' | 'uploading' | 'success' | 'fail'
|
||||
status: "success",
|
||||
response: item,
|
||||
uid: item.id
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
{immediate: true, deep: true}
|
||||
)
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
|
|
@ -0,0 +1,324 @@
|
|||
<template>
|
||||
<div class="upload-file w-full">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:file-list="fileList"
|
||||
:action="'#'"
|
||||
:auto-upload="autoUpload"
|
||||
:before-upload="beforeUpload"
|
||||
:drag="drag"
|
||||
:http-request="httpRequest"
|
||||
:limit="props.limit"
|
||||
:multiple="props.limit > 1"
|
||||
:on-error="excelUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
:on-preview="handlePreview"
|
||||
:on-remove="handleRemove"
|
||||
:on-success="handleFileSuccess"
|
||||
:show-file-list="true"
|
||||
:accept="accept"
|
||||
:disabled="disabled || fileList.length >= props.limit"
|
||||
class="upload-file-uploader"
|
||||
name="file"
|
||||
>
|
||||
<el-button v-if="!disabled" type="primary" :disabled="disabled || fileList.length >= props.limit">
|
||||
<Icon icon="ep:upload-filled"/>
|
||||
{{btnText}}
|
||||
<span>
|
||||
({{fileList.length}}/{{props.limit}})
|
||||
</span>
|
||||
</el-button>
|
||||
<template v-if="isShowTip && !disabled" #tip>
|
||||
<div style="font-size: 0.8rem">
|
||||
{{ t(`common.fileUploadMaxSize`) }} <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||
</div>
|
||||
<div style="font-size: 0.8rem">
|
||||
{{ t(`common.fileUploadFormat`) }} <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #file="row">
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
<!-- 'ready' | 'uploading' | 'success' | 'fail'-->
|
||||
<div v-if="row.file.status === 'ready'">
|
||||
<el-tag type="info">
|
||||
<Icon icon="ep:clock"/>
|
||||
</el-tag>
|
||||
|
||||
</div>
|
||||
<div v-if="row.file.status === 'uploading'">
|
||||
<el-tag type="warning" effect="dark">
|
||||
<Icon icon="ep:loading"/>
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-if="row.file.status === 'success'">
|
||||
<el-tag type="success" effect="dark">
|
||||
<Icon icon="ep:success-filled"/>
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-if="row.file.status === 'fail'" style="color: #ff0000">
|
||||
<el-tag type="danger" effect="dark">
|
||||
<Icon icon="ep:warning-filled"/>
|
||||
</el-tag>
|
||||
{{ row.file.error }}
|
||||
</div>
|
||||
</div>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="row.file.name"
|
||||
placement="top-start"
|
||||
>
|
||||
<div class="ml-2 line-clamp-1">{{ row.file.name }}</div>
|
||||
</el-tooltip>
|
||||
|
||||
<div class="flex">
|
||||
<div class="ml-1">
|
||||
<el-link
|
||||
:href="row.file.url"
|
||||
:underline="false"
|
||||
download
|
||||
target="_blank"
|
||||
type="primary"
|
||||
>
|
||||
<Icon icon="ep:download" :size="24"/>
|
||||
</el-link>
|
||||
</div>
|
||||
<div class="ml-1" v-if="!disabled">
|
||||
<el-button link type="primary" @click="reName(row.file)">
|
||||
<Icon icon="ep:edit" :size="18"/>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="ml-1" v-if="!disabled">
|
||||
<el-button link type="danger" @click="handleRemove(row.file)">
|
||||
<Icon icon="ep:delete" :size="18"/>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// @ts-nocheck
|
||||
import {propTypes} from '@/utils/propTypes'
|
||||
import type {UploadInstance, UploadProps, UploadRawFile, UploadUserFile} from 'element-plus'
|
||||
import {useUpload} from '@/components/UploadFile/src/useUploadlPlus'
|
||||
import {UploadFile} from 'element-plus/es/components/upload/src/upload'
|
||||
import {infoByIds, renameById} from "@/api/infra/file";
|
||||
import {useMessage} from "@/hooks/web/useMessage";
|
||||
import {useI18n} from "@/hooks/web/useI18n";
|
||||
const {t} = useI18n()
|
||||
// 附件上传
|
||||
defineOptions({name: 'AccessoryUpload'})
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const text = t('fileUploadBtnText');
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
||||
fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileSize: propTypes.number.def(5), // 大小限制(MB)
|
||||
limit: propTypes.number.def(5), // 数量限制
|
||||
autoUpload: propTypes.bool.def(true), // 自动上传
|
||||
drag: propTypes.bool.def(false), // 拖拽上传
|
||||
isShowTip: propTypes.bool.def(true), // 是否显示提示
|
||||
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
|
||||
buttonText: propTypes.string.def(''),
|
||||
})
|
||||
|
||||
const btnText = computed(() => {
|
||||
if(props.buttonText){
|
||||
return props.buttonText
|
||||
}
|
||||
return t('common.fileUploadBtnText')
|
||||
})
|
||||
|
||||
const accept = computed(() => {
|
||||
let accept = ''
|
||||
props.fileType.forEach((type: string) => {
|
||||
accept += `.${type},`
|
||||
})
|
||||
return accept.slice(0, -1)
|
||||
})
|
||||
|
||||
// ========== 上传相关 ==========
|
||||
const uploadRef = ref<UploadInstance>()
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
const uploadNumber = ref<number>(0)
|
||||
const that = reactive({
|
||||
ids: [],
|
||||
})
|
||||
const {httpRequest} = useUpload()
|
||||
|
||||
// 文件上传之前判断
|
||||
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
||||
if (fileList.value.length >= props.limit) {
|
||||
message.error(`${t("common.fileUploadMaxCountTips")}${props.limit}`)
|
||||
return false
|
||||
}
|
||||
let fileExtension = ''
|
||||
if (file.name.lastIndexOf('.') > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
|
||||
}
|
||||
const isImg = props.fileType.some((type: string) => {
|
||||
if (file.type.indexOf(type) > -1) return true
|
||||
return !!(fileExtension && fileExtension.indexOf(type) > -1)
|
||||
})
|
||||
const isLimit = file.size < props.fileSize * 1024 * 1024
|
||||
if (!isImg) {
|
||||
|
||||
message.error(`${t("common.fileUploadFormatErrorTips")}${props.fileType.join('/')}`)
|
||||
return false
|
||||
}
|
||||
if (!isLimit) {
|
||||
message.error(`${t("common.fileUploadMaxSizeTips")}${props.fileSize}`)
|
||||
return false
|
||||
}
|
||||
message.success(t("common.fileUploadingTips"))
|
||||
uploadNumber.value++
|
||||
}
|
||||
// 文件上传成功
|
||||
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
||||
console.log("上传成功", res)
|
||||
const item = res.data;
|
||||
fileList.value.push({
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
size: item.size,
|
||||
// 'ready' | 'uploading' | 'success' | 'fail'
|
||||
status: "success",
|
||||
response: item,
|
||||
uid: item.id
|
||||
})
|
||||
that.ids.push(`${res.data.id}`)
|
||||
emitUpdateModelValue()
|
||||
}
|
||||
// 文件数超出提示
|
||||
const handleExceed: UploadProps['onExceed'] = (): void => {
|
||||
message.error(`${t('common.fileUploadMaxCountTips')}${props.limit}`)
|
||||
}
|
||||
// 上传错误提示
|
||||
const excelUploadError: UploadProps['onError'] = (): void => {
|
||||
message.error(t('common.fileUploadFailedTips'))
|
||||
}
|
||||
// 删除上传文件
|
||||
const handleRemove = (file: UploadFile) => {
|
||||
const index = fileList.value.map((f) => f.name).indexOf(file.name)
|
||||
if (index > -1) {
|
||||
useMessage().delConfirm().then((res) => {
|
||||
if (res) {
|
||||
fileList.value.splice(index, 1)
|
||||
emitUpdateModelValue()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
const reName = (file: UploadFile) => {
|
||||
console.log(file)
|
||||
if (file.status == 'success') {
|
||||
// 去除后缀
|
||||
const name = file.name.substring(0, file.name.lastIndexOf('.'))
|
||||
const options = {inputVal: name, tip: t('common.fileUploadInputFileName')};
|
||||
useMessage().submit(options).then((res) => {
|
||||
if (res) {
|
||||
renameById(`${file.uid}`, res.value).then((res2) => {
|
||||
message.success(t('common.success'))
|
||||
fileList.value.forEach((item, index) => {
|
||||
if (`${item.uid}` === `${file.uid}`) {
|
||||
fileList.value[index].name = res2.name
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
} else {
|
||||
useMessage().warning(t('common.fileUploadNotUploadTips'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
|
||||
console.log(uploadFile)
|
||||
}
|
||||
|
||||
// 监听模型绑定值变动
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: string | string[]) => {
|
||||
if (!val) {
|
||||
fileList.value = [] // fix:处理掉缓存,表单重置后上传组件的内容并没有重置
|
||||
return
|
||||
}
|
||||
const arr = `${val}`.split(",");
|
||||
const query = [];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const item = `${arr[i]}`
|
||||
if(!that.ids.includes(item)){
|
||||
query.push(item)
|
||||
}
|
||||
}
|
||||
if(query.length > 0){
|
||||
infoByIds(query.join(",")).then((res) => {
|
||||
console.log("init", res)
|
||||
if (res) {
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
const item = res[i];
|
||||
that.ids.push(`${item.id}`)
|
||||
fileList.value.push({
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
size: item.size,
|
||||
// 'ready' | 'uploading' | 'success' | 'fail'
|
||||
status: "success",
|
||||
response: item,
|
||||
uid: item.id
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
{immediate: true, deep: true}
|
||||
)
|
||||
// 发送文件链接列表更新
|
||||
const emitUpdateModelValue = () => {
|
||||
// 情况1:数组结果
|
||||
let result: string | string[] = fileList.value.map((file) => `${file.uid}`)
|
||||
console.log("result",result,fileList.value)
|
||||
result = result.join(',')
|
||||
emit('update:modelValue', result)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.upload-file-uploader {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
:deep(.upload-file-list .el-upload-list__item) {
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
line-height: 2;
|
||||
border: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
:deep(.el-upload-list__item-file-name) {
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
:deep(.upload-file-list .ele-upload-list__item-content) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
:deep(.ele-upload-list__item-content-action .el-link) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
import * as FileApi from '@/api/infra/file'
|
||||
import CryptoJS from 'crypto-js'
|
||||
import { UploadRawFile, UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
|
||||
import axios from 'axios'
|
||||
|
||||
export const useUpload = () => {
|
||||
// 后端上传地址
|
||||
const uploadUrl = import.meta.env.VITE_UPLOAD_URL
|
||||
// 是否使用前端直连上传
|
||||
const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE
|
||||
// 重写ElUpload上传方法
|
||||
const httpRequest = async (options: UploadRequestOptions) => {
|
||||
// 模式一:前端上传
|
||||
if (isClientUpload) {
|
||||
// 1.1 生成文件名称
|
||||
const fileName = await generateFileName(options.file)
|
||||
// 1.2 获取文件预签名地址
|
||||
const presignedInfo = await FileApi.getFilePresignedUrl(fileName)
|
||||
// 1.3 上传文件(不能使用 ElUpload 的 ajaxUpload 方法的原因:其使用的是 FormData 上传,Minio 不支持)
|
||||
return axios.put(presignedInfo.uploadUrl, options.file, {
|
||||
headers: {
|
||||
'Content-Type': options.file.type,
|
||||
}
|
||||
}).then(() => {
|
||||
// 1.4. 记录文件信息到后端(异步)
|
||||
createFile(presignedInfo, fileName, options.file)
|
||||
// 通知成功,数据格式保持与后端上传的返回结果一致
|
||||
return { data: presignedInfo.url , filename: options.file.name}
|
||||
})
|
||||
} else {
|
||||
// 模式二:后端上传
|
||||
// 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
|
||||
return new Promise((resolve, reject) => {
|
||||
FileApi.updateFilePlus({ file: options.file })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
resolve({
|
||||
...res,
|
||||
filename: options.file.name
|
||||
})
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
uploadUrl,
|
||||
httpRequest
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件信息
|
||||
* @param vo 文件预签名信息
|
||||
* @param name 文件名称
|
||||
* @param file 文件
|
||||
*/
|
||||
function createFile(vo: FileApi.FilePresignedUrlRespVO, name: string, file: UploadRawFile) {
|
||||
const fileVo = {
|
||||
configId: vo.configId,
|
||||
url: vo.url,
|
||||
path: name,
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size
|
||||
}
|
||||
FileApi.createFile(fileVo)
|
||||
return fileVo
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成文件名称(使用算法SHA256)
|
||||
* @param file 要上传的文件
|
||||
*/
|
||||
async function generateFileName(file: UploadRawFile) {
|
||||
// 读取文件内容
|
||||
const data = await file.arrayBuffer()
|
||||
const wordArray = CryptoJS.lib.WordArray.create(data)
|
||||
// 计算SHA256
|
||||
const sha256 = CryptoJS.SHA256(wordArray).toString()
|
||||
// 拼接后缀
|
||||
const ext = file.name.substring(file.name.lastIndexOf('.'))
|
||||
return `${sha256}${ext}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传类型
|
||||
*/
|
||||
enum UPLOAD_TYPE {
|
||||
// 客户端直接上传(只支持S3服务)
|
||||
CLIENT = 'client',
|
||||
// 客户端发送到后端上传
|
||||
SERVER = 'server'
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
|
||||
import { useI18n } from './useI18n'
|
||||
import {ElMessage, ElMessageBox, ElNotification} from 'element-plus'
|
||||
import {useI18n} from './useI18n'
|
||||
|
||||
export const useMessage = () => {
|
||||
const { t } = useI18n()
|
||||
const {t} = useI18n()
|
||||
return {
|
||||
// 消息提示
|
||||
info(content: string) {
|
||||
|
|
@ -25,15 +26,15 @@ export const useMessage = () => {
|
|||
},
|
||||
// 错误提示
|
||||
alertError(content: string) {
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), { type: 'error' })
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), {type: 'error'})
|
||||
},
|
||||
// 成功提示
|
||||
alertSuccess(content: string) {
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), { type: 'success' })
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), {type: 'success'})
|
||||
},
|
||||
// 警告提示
|
||||
alertWarning(content: string) {
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), { type: 'warning' })
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), {type: 'warning'})
|
||||
},
|
||||
// 通知提示
|
||||
notify(content: string) {
|
||||
|
|
@ -90,6 +91,29 @@ export const useMessage = () => {
|
|||
cancelButtonText: t('common.cancel'),
|
||||
type: 'warning'
|
||||
})
|
||||
},
|
||||
submit({ inputVal, tip, content = '',inputValidator = undefined }: {
|
||||
inputVal: string,
|
||||
tip: string,
|
||||
content?: string,
|
||||
inputValidator?: Function | undefined
|
||||
}) {
|
||||
return ElMessageBox.prompt(content, tip, {
|
||||
inputValue: inputVal,
|
||||
inputPlaceholder: t('common.inputText'),
|
||||
inputValidator: (value) => {
|
||||
if (inputValidator) {
|
||||
return inputValidator(value);
|
||||
} else {
|
||||
if (!value) {
|
||||
return t('common.inputText')
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
confirmButtonText: t('common.ok'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,23 @@ export default {
|
|||
updateTime: 'Update Time',
|
||||
copy: 'Copy',
|
||||
copySuccess: 'Copy Success',
|
||||
copyError: 'Copy Error'
|
||||
copyError: 'Copy Error',
|
||||
keywordPlaceholder: 'Please enter a keyword',
|
||||
fileUploadMaxSize: "Maximum size per file",
|
||||
fileUploadFormat: "Supported formats",
|
||||
fileUploadBtnText: "Upload attachments",
|
||||
fileUploadMaxCountTips: "The number of uploaded files cannot exceed",
|
||||
fileUploadMaxSizeTips: "The size of the uploaded file cannot exceed",
|
||||
fileUploadFormatErrorTips: "The file format is incorrect. Please upload",
|
||||
fileUploadingTips: "The file is being uploaded. Please wait...",
|
||||
fileUploadFailedTips: "The data upload failed. Please upload again!",
|
||||
fileUploadInputFileName: "Please enter the file name",
|
||||
fileUploadNotUploadTips: "Please upload the file first",
|
||||
fileDownloadNameLabel: "File name",
|
||||
fileDownloadSizeLabel: "File size",
|
||||
fileDownloadOptionsLabel: "Operations",
|
||||
fileDownloadTitle: "File list",
|
||||
fileDownloadBtnText: "Download working papers"
|
||||
},
|
||||
lock: {
|
||||
lockScreen: 'Lock screen',
|
||||
|
|
@ -671,6 +687,8 @@ export default {
|
|||
"viewOrder": "viewOrder",
|
||||
"backHome": "BackHome",
|
||||
"downloadSuccess": "downloadSuccess",
|
||||
"downloadFile": "Download of working papers",
|
||||
"labelAccessoryFile": "Attachment"
|
||||
},
|
||||
"productDialogList": {
|
||||
"title": "Product List",
|
||||
|
|
@ -679,6 +697,7 @@ export default {
|
|||
"colLabelCover": "Cover",
|
||||
"colLabelName": "Product Name",
|
||||
"colLabelRemark": "Remarks",
|
||||
"colLabelDetails": "Details Description"
|
||||
"colLabelDetails": "Details Description",
|
||||
"colLabelFileIds": 'Working paper attachments'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,23 @@ export default {
|
|||
updateTime: '更新时间',
|
||||
copy: '复制',
|
||||
copySuccess: '复制成功',
|
||||
copyError: '复制失败'
|
||||
copyError: '复制失败',
|
||||
fileUploadMaxSize: "单文件最大",
|
||||
keywordPlaceholder: '请输入关键字',
|
||||
fileUploadFormat: "支持格式",
|
||||
fileUploadBtnText: "附件上传",
|
||||
fileUploadMaxCountTips: "上传文件数量不能超过",
|
||||
fileUploadMaxSizeTips: "上传文件大小不能超过",
|
||||
fileUploadFormatErrorTips: "文件格式不正确, 请上传",
|
||||
fileUploadingTips: "正在上传文件,请稍候...",
|
||||
fileUploadFailedTips: "数据失败上传失败,请您重新上传!",
|
||||
fileUploadInputFileName: "请输入文件名称",
|
||||
fileUploadNotUploadTips: "请先上传文件",
|
||||
fileDownloadNameLabel: "文件名称",
|
||||
fileDownloadSizeLabel: "文件大小",
|
||||
fileDownloadOptionsLabel: "操作",
|
||||
fileDownloadTitle: "文件列表",
|
||||
fileDownloadBtnText: "底稿下载",
|
||||
},
|
||||
lock: {
|
||||
lockScreen: '锁定屏幕',
|
||||
|
|
@ -667,6 +683,8 @@ export default {
|
|||
viewOrder: "查看订单",
|
||||
backHome: "返回首页",
|
||||
downloadSuccess: "下载成功",
|
||||
downloadFile: "底稿下载",
|
||||
labelAccessoryFile: "附件"
|
||||
},
|
||||
productDialogList:{
|
||||
title: '产品列表',
|
||||
|
|
@ -676,5 +694,6 @@ export default {
|
|||
colLabelName: '产品名称',
|
||||
colLabelRemark: '备注',
|
||||
colLabelDetails: '详情描述',
|
||||
colLabelFileIds: '底稿附件',
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-form-item label="编码" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="请输入编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序号" prop="sort">
|
||||
<el-input-number v-model="formData.sort" placeholder="请输入排序号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { CustomerGroupApi, CustomerGroupVO } from '@/api/oms/customergroup'
|
||||
|
||||
/** 客户组别 表单 */
|
||||
defineOptions({ name: 'CustomerGroupForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
code: undefined,
|
||||
name: undefined,
|
||||
sort: 1,
|
||||
remark: undefined,
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await CustomerGroupApi.getCustomerGroup(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as CustomerGroupVO
|
||||
if (formType.value === 'create') {
|
||||
await CustomerGroupApi.createCustomerGroup(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await CustomerGroupApi.updateCustomerGroup(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
code: undefined,
|
||||
name: undefined,
|
||||
sort: undefined,
|
||||
remark: undefined,
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="编码" prop="code">
|
||||
<el-input
|
||||
v-model="queryParams.code"
|
||||
placeholder="请输入编码"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序号" prop="sort">
|
||||
<el-input-number
|
||||
v-model="queryParams.sort"
|
||||
placeholder="请输入排序号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="queryParams.remark"
|
||||
placeholder="请输入备注"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['oms:customer-group:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['oms:customer-group:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||
<el-table-column label="id" align="center" prop="id" />
|
||||
<el-table-column label="编码" align="center" prop="code" />
|
||||
<el-table-column label="名称" align="center" prop="name" />
|
||||
<el-table-column label="排序号" align="center" prop="sort" />
|
||||
<el-table-column label="备注" align="center" prop="remark" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
v-hasPermi="['oms:customer-group:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['oms:customer-group:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<CustomerGroupForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { CustomerGroupApi, CustomerGroupVO } from '@/api/oms/customergroup'
|
||||
import CustomerGroupForm from './CustomerGroupForm.vue'
|
||||
|
||||
/** 客户组别 列表 */
|
||||
defineOptions({ name: 'CustomerGroup' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<CustomerGroupVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
code: undefined,
|
||||
name: undefined,
|
||||
sort: undefined,
|
||||
remark: undefined,
|
||||
createTime: [],
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await CustomerGroupApi.getCustomerGroupPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await CustomerGroupApi.deleteCustomerGroup(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await CustomerGroupApi.exportCustomerGroup(queryParams)
|
||||
download.excel(data, '客户组别 .xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-button @click="editMore">编辑更多属性</el-button>
|
||||
<div>
|
||||
<DraftDesign ref="draftDesignRef" @save="submitForm"/>
|
||||
</div>
|
||||
|
|
@ -57,10 +58,26 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="cover" prop="cover">
|
||||
<UploadImg v-model="formData.cover" />
|
||||
</el-form-item>
|
||||
<el-row>
|
||||
<el-col :span="8" :xs="24">
|
||||
<el-form-item label="cover" prop="cover">
|
||||
<UploadImg v-model="formData.cover" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :xs="24">
|
||||
<el-form-item label="底稿文件" prop="fileList">
|
||||
<AccessoryUpload
|
||||
v-model="formData.fileIds"
|
||||
:fileType="['svg', 'png', 'jpg', 'jpeg','doc', 'xls', 'ppt', 'txt', 'pdf']"
|
||||
:file-size="20"
|
||||
:limit="10"
|
||||
drag
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div id="bottomDiv"> </div>
|
||||
</el-form>
|
||||
|
||||
</template>
|
||||
|
|
@ -77,7 +94,6 @@ const { t } = useI18n() // 国际化
|
|||
const message = useMessage() // 消息弹窗
|
||||
const route = useRoute() // 路由
|
||||
const draftDesignRef = ref()
|
||||
const designPropEditRef = ref()
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
|
|
@ -86,6 +102,7 @@ const formData = ref({
|
|||
id: route.params.id,
|
||||
code: undefined,
|
||||
cover: undefined,
|
||||
fileIds: "",
|
||||
name: undefined,
|
||||
author: undefined,
|
||||
version: 1,
|
||||
|
|
@ -110,12 +127,19 @@ onMounted(()=>{
|
|||
|
||||
})
|
||||
|
||||
const editMore = () => {
|
||||
// 滚动到底部
|
||||
const target = document.getElementById('bottomDiv');
|
||||
if (target) {
|
||||
target.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
// designPropEditRef.value.init(1);
|
||||
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ const submitForm = async () => {
|
|||
'tenant-id': getTenantId()
|
||||
}
|
||||
formLoading.value = true
|
||||
uploadRef.value!.submit()
|
||||
uploadRef.value!.submit({})
|
||||
}
|
||||
|
||||
/** 文件上传成功 */
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ const submitForm = async () => {
|
|||
'tenant-id': getTenantId()
|
||||
}
|
||||
formLoading.value = true
|
||||
uploadRef.value!.submit()
|
||||
uploadRef.value!.submit({})
|
||||
}
|
||||
|
||||
/** 文件上传成功 */
|
||||
|
|
|
|||
|
|
@ -43,6 +43,14 @@
|
|||
<el-form-item label="产品名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入产品名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品类型" prop="productTypeId">
|
||||
<ProductTypeDataListDialog
|
||||
v-model="formData.productTypeId"
|
||||
placeholder="请选择产品类型"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="品牌" prop="brandId">
|
||||
<BrandDataListDialog v-model="formData.brandId" placeholder="请选择品牌"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :xs="24">
|
||||
<el-form-item label="封面" prop="cover">
|
||||
|
|
@ -53,16 +61,15 @@
|
|||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12" :xs="24">
|
||||
<el-form-item label="品牌" prop="brandId">
|
||||
<BrandDataListDialog v-model="formData.brandId" placeholder="请选择品牌"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :xs="24">
|
||||
<el-form-item label="产品类型" prop="productTypeId">
|
||||
<ProductTypeDataListDialog v-model="formData.productTypeId"
|
||||
placeholder="请选择产品类型"/>
|
||||
<el-form-item label="客户组别" prop="customerGroupId">
|
||||
<CustomerGroupDataListDialog
|
||||
:is-can-edit="true"
|
||||
v-model="formData.customerGroupId"
|
||||
placeholder="请选择客户组别"/>
|
||||
</el-form-item>
|
||||
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12" :xs="24">
|
||||
|
|
@ -75,7 +82,6 @@
|
|||
<el-input-number :min="0" :precision="2" v-model="formData.specSizeHeight"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
|
|
@ -106,6 +112,7 @@
|
|||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="关联设计稿" v-if="formData.templateType === '1'"
|
||||
prop="draftDesignDataId">
|
||||
<div style="width: calc(100% - 20px);">
|
||||
|
|
@ -168,7 +175,8 @@
|
|||
</el-table-column>
|
||||
<el-table-column label="操作" width="80">
|
||||
<template #default="{$index}">
|
||||
<el-button size="small" type="danger" @click="removeRow(that.draftDesignList,$index) ">
|
||||
<el-button size="small" type="danger"
|
||||
@click="removeRow(that.draftDesignList,$index) ">
|
||||
<Icon icon="ep:delete"/>
|
||||
</el-button>
|
||||
</template>
|
||||
|
|
@ -240,12 +248,14 @@
|
|||
</div>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-checkbox v-model="scope.row.d" @change="changSetDefault(scope.$index,scope.row.d)"/>
|
||||
<el-checkbox v-model="scope.row.d"
|
||||
@change="changSetDefault(scope.$index,scope.row.d)"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80">
|
||||
<template #default="{$index}">
|
||||
<el-button size="small" type="danger" @click="removeRow(that.priceList,$index) ">
|
||||
<el-button size="small" type="danger"
|
||||
@click="removeRow(that.priceList,$index) ">
|
||||
<Icon icon="ep:delete"/>
|
||||
</el-button>
|
||||
</template>
|
||||
|
|
@ -371,6 +381,7 @@ const formData = ref({
|
|||
summary: undefined,
|
||||
brandId: '',
|
||||
productTypeId: '',
|
||||
customerGroupId: '',
|
||||
draftDesignDataId: undefined,
|
||||
draftDesignList: '',
|
||||
priceList: '',
|
||||
|
|
@ -415,7 +426,7 @@ const addPriceRow = () => {
|
|||
})
|
||||
}
|
||||
const activeName = ref('base')
|
||||
const removeRow = (data,index) => {
|
||||
const removeRow = (data, index) => {
|
||||
if (data.length > 1) {
|
||||
data.splice(index, 1)
|
||||
} else {
|
||||
|
|
@ -464,10 +475,10 @@ const duplicateInfoCheck = (name: string) => {
|
|||
}
|
||||
return count > 1;
|
||||
}
|
||||
const changSetDefault = (index,d)=>{
|
||||
if(d){
|
||||
for(let i=0;i<that.priceList.length;i++){
|
||||
if(index != i){
|
||||
const changSetDefault = (index, d) => {
|
||||
if (d) {
|
||||
for (let i = 0; i < that.priceList.length; i++) {
|
||||
if (index != i) {
|
||||
that.priceList[i].d = false
|
||||
}
|
||||
}
|
||||
|
|
@ -520,7 +531,7 @@ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成
|
|||
|
||||
const submitCheck = () => {
|
||||
|
||||
if(!formData.value.name){
|
||||
if (!formData.value.name) {
|
||||
formData.value.name = formData.value.code || '未命名'
|
||||
}
|
||||
formRef.value.validate().then(() => {
|
||||
|
|
@ -609,10 +620,10 @@ const submitForm = async () => {
|
|||
|
||||
data.draftDesignList = JSON.stringify(that.draftDesignList)
|
||||
// 默认单价
|
||||
if(that.priceList.length > 0){
|
||||
if (that.priceList.length > 0) {
|
||||
let match = false;
|
||||
for (let i = 0; i < that.priceList.length; i++) {
|
||||
if(duplicatePriceCheck(that.priceList[i].c)){
|
||||
if (duplicatePriceCheck(that.priceList[i].c)) {
|
||||
message.error(`单价设置-第${i + 1} 行币种重复`)
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,21 +43,27 @@ export const createFile = (data: any) => {
|
|||
export const updateFile = (data: any) => {
|
||||
return request.upload({ url: '/infra/file/upload', data })
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取资源的域名
|
||||
*/
|
||||
export const getDomain = () => {
|
||||
return new Promise((resolve)=>{
|
||||
const domain= sessionStorage.getItem('_$domain$_')
|
||||
if(domain){
|
||||
resolve(domain)
|
||||
return;
|
||||
}
|
||||
request.get({ url: '/infra/file/domain'}).then(res =>{
|
||||
sessionStorage.setItem('_$domain$_',res)
|
||||
resolve(res)
|
||||
})
|
||||
})
|
||||
return request.get({ url: '/infra/file/domain'})
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件 返回文件信息
|
||||
* @param data
|
||||
*/
|
||||
export const updateFilePlus = (data: any) => {
|
||||
return request.upload({ url: '/infra/file/upload-plus', data })
|
||||
}
|
||||
/**
|
||||
* 根据id 获取文件信息
|
||||
* @param ids
|
||||
*/
|
||||
export const infoByIds = (ids: string) => {
|
||||
return request.get({ url: '/infra/file/info/'+ids })
|
||||
}
|
||||
export const renameById = (id: string, name: string) => {
|
||||
return request.post({ url:`/infra/file/rename/${id}`,data:{name} })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
// 客户组别 VO
|
||||
export interface CustomerGroupVO {
|
||||
id: number // id
|
||||
code: string // 编码
|
||||
name: string // 名称
|
||||
sort: number // 排序号
|
||||
remark: string // 备注
|
||||
}
|
||||
|
||||
// 客户组别 API
|
||||
export const CustomerGroupApi = {
|
||||
// 查询客户组别 分页
|
||||
getCustomerGroupPage: async (params: any) => {
|
||||
return await request.get({ url: `/oms/customer-group/page`, params })
|
||||
},
|
||||
|
||||
// 查询客户组别 详情
|
||||
getCustomerGroup: async (id: number) => {
|
||||
return await request.get({ url: `/oms/customer-group/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增客户组别
|
||||
createCustomerGroup: async (data: CustomerGroupVO) => {
|
||||
return await request.post({ url: `/oms/customer-group/create`, data })
|
||||
},
|
||||
|
||||
// 修改客户组别
|
||||
updateCustomerGroup: async (data: CustomerGroupVO) => {
|
||||
return await request.put({ url: `/oms/customer-group/update`, data })
|
||||
},
|
||||
|
||||
// 删除客户组别
|
||||
deleteCustomerGroup: async (id: number) => {
|
||||
return await request.delete({ url: `/oms/customer-group/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出客户组别 Excel
|
||||
exportCustomerGroup: async (params) => {
|
||||
return await request.download({ url: `/oms/customer-group/export-excel`, params })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<template>
|
||||
<Dialog :title="dialogTitle" append-to-body v-model="dialogVisible">
|
||||
<Form :disabled="disabled" ref="formRef" :schema="allSchemas.formSchema" :rules="rules" v-loading="formLoading" />
|
||||
|
||||
<template #footer>
|
||||
<el-button v-if="!disabled" @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
||||
|
||||
import { rules, allSchemas } from './config.data'
|
||||
import {CustomerGroupApi, CustomerGroupVO} from "@/api/oms/customergroup";
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
const disabled = computed(() => {
|
||||
return formType.value !== 'create' && formType.value !== 'update'
|
||||
})
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: number) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = await CustomerGroupApi.getCustomerGroup(id)
|
||||
formRef.value.setValues(data)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.getElFormRef().validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formRef.value.formModel as CustomerGroupVO
|
||||
if (formType.value === 'create') {
|
||||
await CustomerGroupApi.createCustomerGroup(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await CustomerGroupApi.updateCustomerGroup(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import type { CrudSchema } from '@/hooks/web/useCrudSchemas'
|
||||
|
||||
// 表单校验
|
||||
export const rules = reactive({
|
||||
|
||||
})
|
||||
|
||||
// CrudSchema https://doc.iocoder.cn/vue3/crud-schema/
|
||||
const crudSchemas = reactive<CrudSchema[]>([
|
||||
|
||||
{
|
||||
label: 'code',
|
||||
field: 'code',
|
||||
width: 200,
|
||||
isSearch: true,
|
||||
},
|
||||
{
|
||||
label: 'name',
|
||||
field: 'name',
|
||||
isSearch: true,
|
||||
},
|
||||
{
|
||||
label: 'remark',
|
||||
field: 'remark',
|
||||
isTable: false,
|
||||
},
|
||||
{
|
||||
label: 'options',
|
||||
field: 'action',
|
||||
isForm: false
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||
|
|
@ -0,0 +1,319 @@
|
|||
<template>
|
||||
<slot>
|
||||
<div>
|
||||
<el-input
|
||||
v-model="that.showValue"
|
||||
clearable
|
||||
:placeholder="props.placeholder"
|
||||
@clear="clearData"
|
||||
@click="viewDetails"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click.stop="openDialog">
|
||||
<Icon icon="ep:search"/>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</slot>
|
||||
{{tmp}}
|
||||
<Dialog
|
||||
:title="dialogTitle"
|
||||
width="80%"
|
||||
append-to-body
|
||||
v-model="that.visible"
|
||||
@close="updateVisible(false)">
|
||||
<div>
|
||||
<!-- 搜索工作栏 -->
|
||||
<ContentWrap>
|
||||
<Search
|
||||
:schema="allSchemas.searchSchema"
|
||||
:is-col="false"
|
||||
@search="setSearchParams"
|
||||
@reset="setSearchParams">
|
||||
<!-- 新增等操作按钮 -->
|
||||
<template #actionMore>
|
||||
<el-button v-if="isCanEdit"
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['oms:draft-design-data:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px"/>
|
||||
新增
|
||||
</el-button>
|
||||
<div v-else>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</Search>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:columns="allSchemas.tableColumns"
|
||||
:data="tableObject.tableList"
|
||||
:loading="tableObject.loading"
|
||||
:selection="true"
|
||||
ref="tableRef"
|
||||
@selection-change="selectionChange"
|
||||
:pagination="{ total: tableObject.total
|
||||
}"
|
||||
v-model:pageSize="tableObject.pageSize"
|
||||
v-model:currentPage="tableObject.currentPage"
|
||||
>
|
||||
<template #action="{ row }">
|
||||
<div v-if="isCanEdit">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(row.id)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-else>
|
||||
-
|
||||
</div>
|
||||
</template>
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="updateVisible(false,true)">{{ t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit">{{ t('common.ok') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</Dialog>
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<DataForm ref="formRef" @success="getList"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="ProductTypeDataListDialog">
|
||||
import {allSchemas} from './config.data'
|
||||
|
||||
import DataForm from './DataForm.vue'
|
||||
import {CustomerGroupApi} from "@/api/oms/customergroup";
|
||||
|
||||
|
||||
defineOptions({name: 'CustomerGroupDataListDialog'})
|
||||
const emit = defineEmits(['update:modelValue', 'update:visible', 'submit']) // 定义 success 事件,用于操作成功后的回调
|
||||
|
||||
const {t} = useI18n() // 国际化
|
||||
|
||||
const dialogTitle = ref('Customer group') // 弹窗的标题
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String,Number],
|
||||
required: true
|
||||
},
|
||||
dataKey: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'id'
|
||||
},
|
||||
showKey: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'name'
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
isCanEdit: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
|
||||
placeholder: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'selectText'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '64px'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '64px'
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const that = reactive({
|
||||
inputVal: '',
|
||||
showValue: '',
|
||||
visible: false,
|
||||
})
|
||||
const toStr = (data: any, def = '') => {
|
||||
if (data !== null && data !== undefined) {
|
||||
return `${data}`
|
||||
}
|
||||
return def
|
||||
}
|
||||
const openDialog = () => {
|
||||
updateVisible(true);
|
||||
}
|
||||
const viewDetails = () => {
|
||||
if (that.inputVal) {
|
||||
openForm("preview", that.inputVal.split(",")[0])
|
||||
} else {
|
||||
openDialog();
|
||||
}
|
||||
}
|
||||
let map = new Map();
|
||||
const initInput = async () => {
|
||||
const dataKey = that.inputVal + ',' + props.dataKey + ',' + props.showKey + ',' + props.multiple;
|
||||
if (map.has(dataKey)) {
|
||||
const data = map.get(dataKey)
|
||||
if (data) {
|
||||
that.inputVal = data.inputVal
|
||||
that.showValue = data.showValue
|
||||
console.log('缓存数据', data)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const ids = that.inputVal.split(",");
|
||||
let tmpInput = [];
|
||||
let tmpShow = [];
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const data = await CustomerGroupApi.getCustomerGroup(ids[i])
|
||||
tmpInput.push(data[props.dataKey]);
|
||||
tmpShow.push(data[props.showKey]);
|
||||
}
|
||||
that.inputVal = tmpInput.join(',');
|
||||
that.showValue = tmpShow.join(',');
|
||||
map.set(dataKey, {
|
||||
inputVal: that.inputVal,
|
||||
showValue: that.showValue
|
||||
})
|
||||
}
|
||||
watch(() => props.visible, (newVal) => {
|
||||
that.visible = newVal;
|
||||
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, (newVal)=>{
|
||||
that.inputVal = toStr(newVal,'')
|
||||
if (that.inputVal) {
|
||||
initInput();
|
||||
}
|
||||
},{
|
||||
immediate: true
|
||||
})
|
||||
// 用监听属性变化无其他意义
|
||||
const tmp = computed(()=>{
|
||||
setTimeout(()=>{
|
||||
that.inputVal = toStr(props.modelValue,that.inputVal)
|
||||
if (that.inputVal) {
|
||||
initInput();
|
||||
}
|
||||
},100)
|
||||
return ''
|
||||
},{deep: true})
|
||||
|
||||
|
||||
const clearData = () => {
|
||||
that.inputVal = '';
|
||||
that.showValue = '';
|
||||
updateValue();
|
||||
}
|
||||
const updateVisible = (visible: boolean, clearInput = false) => {
|
||||
that.visible = visible;
|
||||
emit("update:visible", visible)
|
||||
if (clearInput) {
|
||||
clearData();
|
||||
}
|
||||
}
|
||||
defineExpose({}) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
const submit = () => {
|
||||
updateValue();
|
||||
updateVisible(false)
|
||||
}
|
||||
const updateValue = () => {
|
||||
emit("update:modelValue", that.inputVal)
|
||||
}
|
||||
//
|
||||
const selectionChange = (row) => {
|
||||
if (row && row.length > 0) {
|
||||
if (props.multiple) {
|
||||
that.inputVal = row.map(item => item[props.dataKey || 'id']).join(',')
|
||||
that.showValue = row.map(item => item[props.showKey || 'id']).join(',')
|
||||
} else {
|
||||
if (row.length > 1) {
|
||||
useMessage().warning('单选数据,已忽略其他')
|
||||
}
|
||||
that.showValue = row[row.length - 1][props.showKey || 'id']
|
||||
that.inputVal = row[row.length - 1][props.dataKey || 'id']
|
||||
}
|
||||
} else {
|
||||
that.inputVal = ''
|
||||
that.showValue = ''
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const {tableObject, tableMethods} = useTable({
|
||||
getListApi: CustomerGroupApi.getCustomerGroupPage, // 分页接口
|
||||
delListApi: CustomerGroupApi.deleteCustomerGroup // 删除接口
|
||||
})
|
||||
// 获得表格的各种操作
|
||||
const {getList, setSearchParams} = tableMethods
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = (id: number) => {
|
||||
tableMethods.delList(id, false)
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
position: relative;
|
||||
|
||||
.el-input__inner {
|
||||
padding-right: 18px;
|
||||
}
|
||||
|
||||
.el-input__suffix {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import type { CrudSchema } from '@/hooks/web/useCrudSchemas'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import {useI18n} from "@/hooks/web/useI18n";
|
||||
const {t} = useI18n()
|
||||
// 表单校验
|
||||
|
|
@ -20,6 +19,16 @@ const crudSchemas = reactive<CrudSchema[]>([
|
|||
field: 'code',
|
||||
isSearch: true,
|
||||
},
|
||||
{
|
||||
label: t('productDialogList.customerGroupId'),
|
||||
field: 'customerGroupId',
|
||||
isTable: false,
|
||||
isSearch: true,
|
||||
},
|
||||
{
|
||||
label: t('productDialogList.customerGroupName'),
|
||||
field: 'customerGroupName',
|
||||
},
|
||||
{
|
||||
label: t('productDialogList.colLabelType'),
|
||||
field: 'productTypeId',
|
||||
|
|
@ -45,6 +54,11 @@ const crudSchemas = reactive<CrudSchema[]>([
|
|||
label: t('productDialogList.colLabelDetails'),
|
||||
field: 'details',
|
||||
isSearch: false,
|
||||
},
|
||||
{
|
||||
label: t('productDialogList.colLabelFileIds'),
|
||||
field: 'fileIds',
|
||||
isSearch: false,
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<ContentWrap>
|
||||
<Search
|
||||
:expand="true"
|
||||
:expandField="['productTypeId','name']"
|
||||
:expandField="['productTypeId','name','customerGroupId']"
|
||||
:schema="allSchemas.searchSchema"
|
||||
:is-col="false"
|
||||
ref="searchRef"
|
||||
|
|
@ -21,17 +21,29 @@
|
|||
<!-- 新增等操作按钮 -->
|
||||
<template #productTypeId="{data}">
|
||||
<div>
|
||||
<el-select class="w-full min-w-[200px]" v-model="data.productTypeId" clearable placeholder="Please select the product type">
|
||||
<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="Please select the product type">
|
||||
<el-option
|
||||
v-for="item in that.typeList" :key="item.id" :label="item.label"
|
||||
:value="item.id"/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
<template #code="{data}">
|
||||
<div>
|
||||
<KeywordSearch v-model="data.code"
|
||||
:type="'productInfoCode'"
|
||||
:filter="that.queryInfo.brandId"
|
||||
:count="15"/>
|
||||
<KeywordSearch
|
||||
v-model="data.code"
|
||||
:type="'productInfoCode'"
|
||||
:filter="that.queryInfo.brandId"
|
||||
:count="15"/>
|
||||
</div>
|
||||
</template>
|
||||
<template #customerGroupId="{data}">
|
||||
<div>
|
||||
<CustomerGroupDataListDialog
|
||||
v-model="data.customerGroupId"
|
||||
:is-can-edit="false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Search>
|
||||
|
|
@ -65,6 +77,12 @@
|
|||
</div>
|
||||
<div v-else>-</div>
|
||||
</template>
|
||||
<template #fileIds="{row}">
|
||||
<div v-if="row.fileIds">
|
||||
<AccessoryDownload size="small" v-model="row.fileIds"/>
|
||||
</div>
|
||||
<div v-else>-</div>
|
||||
</template>
|
||||
|
||||
</Table>
|
||||
</ContentWrap>
|
||||
|
|
@ -86,7 +104,8 @@ import {allSchemas} from './config.data'
|
|||
import {ProductInfoApi} from '@/api/oms/productinfo'
|
||||
import DataForm from './DataForm.vue'
|
||||
import {ProductTypeApi} from "@/api/base/producttype";
|
||||
import {KeywordApi} from "@/api/base/keyword";
|
||||
import CustomerGroupDataListDialog
|
||||
from "@/components/Dialog/src/CustomerGroupDataListDialog/index.vue";
|
||||
|
||||
/** 稿件图片库 */
|
||||
defineOptions({name: 'ProductInfoList'})
|
||||
|
|
@ -154,7 +173,7 @@ const that = reactive({
|
|||
queryInfo: {
|
||||
productTypeId: null,
|
||||
code: '',
|
||||
brandId:'',
|
||||
brandId: '',
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="w-full">
|
||||
<el-button @click="dialogVisible = true" :size="size" type="primary">
|
||||
<Icon icon="ep:download"/>
|
||||
{{ t('common.fileDownloadBtnText') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<Dialog
|
||||
v-model="dialogVisible"
|
||||
:title="t('common.fileDownloadTitle')"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:show-close="false"
|
||||
:destroy-on-close="true"
|
||||
:close-on-hash-change="false"
|
||||
>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
stripe
|
||||
:data="fileList"
|
||||
border
|
||||
style="width: 100%">
|
||||
<el-table-column prop="name" :label="t('common.fileDownloadNameLabel')"/>
|
||||
<el-table-column prop="size" :label="t('common.fileDownloadSizeLabel')">
|
||||
<template #default="scope">
|
||||
{{ formatBytes(scope.row.size) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="url" :label="t('common.fileDownloadOptionsLabel')">
|
||||
<template #default="scope">
|
||||
<div>
|
||||
<el-link
|
||||
:href="scope.row.url"
|
||||
:underline="false"
|
||||
download
|
||||
target="_blank"
|
||||
type="primary"
|
||||
>
|
||||
<Icon icon="ep:download" :size="24"/>
|
||||
</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// @ts-nocheck
|
||||
import {propTypes} from '@/utils/propTypes'
|
||||
import type {UploadUserFile} from 'element-plus'
|
||||
import {infoByIds} from "@/api/infra/file";
|
||||
import {useMessage} from "@/hooks/web/useMessage";
|
||||
import {useI18n} from "@/hooks/web/useI18n";
|
||||
const {t} = useI18n()
|
||||
// 附件上传
|
||||
defineOptions({name: 'AccessoryDownload'})
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const text = t('fileUploadBtnText');
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
||||
size:{
|
||||
// "" | "default" | "small" | "large"
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const dialogVisible = ref(false);
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
const that = reactive({
|
||||
ids: [],
|
||||
})
|
||||
const formatBytes = (bytes: any, decimals = 2) =>{
|
||||
if (bytes === null || bytes === undefined) {
|
||||
return ''
|
||||
}
|
||||
if (bytes === 0) return '0 Bytes'
|
||||
|
||||
const k = 1024
|
||||
const dm = decimals < 0 ? 0 : decimals
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
const res = parseFloat((bytes / Math.pow(k, i)).toFixed(dm))
|
||||
if (isNaN(res)) {
|
||||
return '0 ' + sizes[0]
|
||||
}
|
||||
return res + ' ' + sizes[i]
|
||||
}
|
||||
// 监听模型绑定值变动
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: string | string[]) => {
|
||||
if (!val) {
|
||||
fileList.value = [] // fix:处理掉缓存,表单重置后上传组件的内容并没有重置
|
||||
return
|
||||
}
|
||||
const arr = `${val}`.split(",");
|
||||
const query = [];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const item = `${arr[i]}`
|
||||
if(!that.ids.includes(item)){
|
||||
query.push(item)
|
||||
}
|
||||
}
|
||||
if(query.length > 0){
|
||||
infoByIds(query.join(",")).then((res) => {
|
||||
console.log("init", res)
|
||||
if (res) {
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
const item = res[i];
|
||||
that.ids.push(`${item.id}`)
|
||||
fileList.value.push({
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
size: item.size,
|
||||
// 'ready' | 'uploading' | 'success' | 'fail'
|
||||
status: "success",
|
||||
response: item,
|
||||
uid: item.id
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
{immediate: true, deep: true}
|
||||
)
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
|
|
@ -0,0 +1,324 @@
|
|||
<template>
|
||||
<div class="upload-file w-full">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:file-list="fileList"
|
||||
:action="'#'"
|
||||
:auto-upload="autoUpload"
|
||||
:before-upload="beforeUpload"
|
||||
:drag="drag"
|
||||
:http-request="httpRequest"
|
||||
:limit="props.limit"
|
||||
:multiple="props.limit > 1"
|
||||
:on-error="excelUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
:on-preview="handlePreview"
|
||||
:on-remove="handleRemove"
|
||||
:on-success="handleFileSuccess"
|
||||
:show-file-list="true"
|
||||
:accept="accept"
|
||||
:disabled="disabled || fileList.length >= props.limit"
|
||||
class="upload-file-uploader"
|
||||
name="file"
|
||||
>
|
||||
<el-button v-if="!disabled" type="primary" :disabled="disabled || fileList.length >= props.limit">
|
||||
<Icon icon="ep:upload-filled"/>
|
||||
{{btnText}}
|
||||
<span>
|
||||
({{fileList.length}}/{{props.limit}})
|
||||
</span>
|
||||
</el-button>
|
||||
<template v-if="isShowTip && !disabled" #tip>
|
||||
<div style="font-size: 0.8rem">
|
||||
{{ t(`common.fileUploadMaxSize`) }} <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||
</div>
|
||||
<div style="font-size: 0.8rem">
|
||||
{{ t(`common.fileUploadFormat`) }} <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #file="row">
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
<!-- 'ready' | 'uploading' | 'success' | 'fail'-->
|
||||
<div v-if="row.file.status === 'ready'">
|
||||
<el-tag type="info">
|
||||
<Icon icon="ep:clock"/>
|
||||
</el-tag>
|
||||
|
||||
</div>
|
||||
<div v-if="row.file.status === 'uploading'">
|
||||
<el-tag type="warning" effect="dark">
|
||||
<Icon icon="ep:loading"/>
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-if="row.file.status === 'success'">
|
||||
<el-tag type="success" effect="dark">
|
||||
<Icon icon="ep:success-filled"/>
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-if="row.file.status === 'fail'" style="color: #ff0000">
|
||||
<el-tag type="danger" effect="dark">
|
||||
<Icon icon="ep:warning-filled"/>
|
||||
</el-tag>
|
||||
{{ row.file.error }}
|
||||
</div>
|
||||
</div>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="row.file.name"
|
||||
placement="top-start"
|
||||
>
|
||||
<div class="ml-2 line-clamp-1">{{ row.file.name }}</div>
|
||||
</el-tooltip>
|
||||
|
||||
<div class="flex">
|
||||
<div class="ml-1">
|
||||
<el-link
|
||||
:href="row.file.url"
|
||||
:underline="false"
|
||||
download
|
||||
target="_blank"
|
||||
type="primary"
|
||||
>
|
||||
<Icon icon="ep:download" :size="24"/>
|
||||
</el-link>
|
||||
</div>
|
||||
<div class="ml-1" v-if="!disabled">
|
||||
<el-button link type="primary" @click="reName(row.file)">
|
||||
<Icon icon="ep:edit" :size="18"/>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="ml-1" v-if="!disabled">
|
||||
<el-button link type="danger" @click="handleRemove(row.file)">
|
||||
<Icon icon="ep:delete" :size="18"/>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// @ts-nocheck
|
||||
import {propTypes} from '@/utils/propTypes'
|
||||
import type {UploadInstance, UploadProps, UploadRawFile, UploadUserFile} from 'element-plus'
|
||||
import {useUpload} from '@/components/UploadFile/src/useUploadlPlus'
|
||||
import {UploadFile} from 'element-plus/es/components/upload/src/upload'
|
||||
import {infoByIds, renameById} from "@/api/infra/file";
|
||||
import {useMessage} from "@/hooks/web/useMessage";
|
||||
import {useI18n} from "@/hooks/web/useI18n";
|
||||
const {t} = useI18n()
|
||||
// 附件上传
|
||||
defineOptions({name: 'AccessoryUpload'})
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const text = t('fileUploadBtnText');
|
||||
const props = defineProps({
|
||||
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
||||
fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileSize: propTypes.number.def(5), // 大小限制(MB)
|
||||
limit: propTypes.number.def(5), // 数量限制
|
||||
autoUpload: propTypes.bool.def(true), // 自动上传
|
||||
drag: propTypes.bool.def(false), // 拖拽上传
|
||||
isShowTip: propTypes.bool.def(true), // 是否显示提示
|
||||
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
|
||||
buttonText: propTypes.string.def(''),
|
||||
})
|
||||
|
||||
const btnText = computed(() => {
|
||||
if(props.buttonText){
|
||||
return props.buttonText
|
||||
}
|
||||
return t('common.fileUploadBtnText')
|
||||
})
|
||||
|
||||
const accept = computed(() => {
|
||||
let accept = ''
|
||||
props.fileType.forEach((type: string) => {
|
||||
accept += `.${type},`
|
||||
})
|
||||
return accept.slice(0, -1)
|
||||
})
|
||||
|
||||
// ========== 上传相关 ==========
|
||||
const uploadRef = ref<UploadInstance>()
|
||||
const fileList = ref<UploadUserFile[]>([])
|
||||
const uploadNumber = ref<number>(0)
|
||||
const that = reactive({
|
||||
ids: [],
|
||||
})
|
||||
const {httpRequest} = useUpload()
|
||||
|
||||
// 文件上传之前判断
|
||||
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
||||
if (fileList.value.length >= props.limit) {
|
||||
message.error(`${t("common.fileUploadMaxCountTips")}${props.limit}`)
|
||||
return false
|
||||
}
|
||||
let fileExtension = ''
|
||||
if (file.name.lastIndexOf('.') > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
|
||||
}
|
||||
const isImg = props.fileType.some((type: string) => {
|
||||
if (file.type.indexOf(type) > -1) return true
|
||||
return !!(fileExtension && fileExtension.indexOf(type) > -1)
|
||||
})
|
||||
const isLimit = file.size < props.fileSize * 1024 * 1024
|
||||
if (!isImg) {
|
||||
|
||||
message.error(`${t("common.fileUploadFormatErrorTips")}${props.fileType.join('/')}`)
|
||||
return false
|
||||
}
|
||||
if (!isLimit) {
|
||||
message.error(`${t("common.fileUploadMaxSizeTips")}${props.fileSize}`)
|
||||
return false
|
||||
}
|
||||
message.success(t("common.fileUploadingTips"))
|
||||
uploadNumber.value++
|
||||
}
|
||||
// 文件上传成功
|
||||
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
||||
console.log("上传成功", res)
|
||||
const item = res.data;
|
||||
fileList.value.push({
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
size: item.size,
|
||||
// 'ready' | 'uploading' | 'success' | 'fail'
|
||||
status: "success",
|
||||
response: item,
|
||||
uid: item.id
|
||||
})
|
||||
that.ids.push(`${res.data.id}`)
|
||||
emitUpdateModelValue()
|
||||
}
|
||||
// 文件数超出提示
|
||||
const handleExceed: UploadProps['onExceed'] = (): void => {
|
||||
message.error(`${t('common.fileUploadMaxCountTips')}${props.limit}`)
|
||||
}
|
||||
// 上传错误提示
|
||||
const excelUploadError: UploadProps['onError'] = (): void => {
|
||||
message.error(t('common.fileUploadFailedTips'))
|
||||
}
|
||||
// 删除上传文件
|
||||
const handleRemove = (file: UploadFile) => {
|
||||
const index = fileList.value.map((f) => f.name).indexOf(file.name)
|
||||
if (index > -1) {
|
||||
useMessage().delConfirm().then((res) => {
|
||||
if (res) {
|
||||
fileList.value.splice(index, 1)
|
||||
emitUpdateModelValue()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
const reName = (file: UploadFile) => {
|
||||
console.log(file)
|
||||
if (file.status == 'success') {
|
||||
// 去除后缀
|
||||
const name = file.name.substring(0, file.name.lastIndexOf('.'))
|
||||
const options = {inputVal: name, tip: t('common.fileUploadInputFileName')};
|
||||
useMessage().submit(options).then((res) => {
|
||||
if (res) {
|
||||
renameById(`${file.uid}`, res.value).then((res2) => {
|
||||
message.success(t('common.success'))
|
||||
fileList.value.forEach((item, index) => {
|
||||
if (`${item.uid}` === `${file.uid}`) {
|
||||
fileList.value[index].name = res2.name
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
} else {
|
||||
useMessage().warning(t('common.fileUploadNotUploadTips'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
|
||||
console.log(uploadFile)
|
||||
}
|
||||
|
||||
// 监听模型绑定值变动
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: string | string[]) => {
|
||||
if (!val) {
|
||||
fileList.value = [] // fix:处理掉缓存,表单重置后上传组件的内容并没有重置
|
||||
return
|
||||
}
|
||||
const arr = `${val}`.split(",");
|
||||
const query = [];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const item = `${arr[i]}`
|
||||
if(!that.ids.includes(item)){
|
||||
query.push(item)
|
||||
}
|
||||
}
|
||||
if(query.length > 0){
|
||||
infoByIds(query.join(",")).then((res) => {
|
||||
console.log("init", res)
|
||||
if (res) {
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
const item = res[i];
|
||||
that.ids.push(`${item.id}`)
|
||||
fileList.value.push({
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
size: item.size,
|
||||
// 'ready' | 'uploading' | 'success' | 'fail'
|
||||
status: "success",
|
||||
response: item,
|
||||
uid: item.id
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
{immediate: true, deep: true}
|
||||
)
|
||||
// 发送文件链接列表更新
|
||||
const emitUpdateModelValue = () => {
|
||||
// 情况1:数组结果
|
||||
let result: string | string[] = fileList.value.map((file) => `${file.uid}`)
|
||||
console.log("result",result,fileList.value)
|
||||
result = result.join(',')
|
||||
emit('update:modelValue', result)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.upload-file-uploader {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
:deep(.upload-file-list .el-upload-list__item) {
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
line-height: 2;
|
||||
border: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
:deep(.el-upload-list__item-file-name) {
|
||||
max-width: 250px;
|
||||
}
|
||||
|
||||
:deep(.upload-file-list .ele-upload-list__item-content) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
:deep(.ele-upload-list__item-content-action .el-link) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
import * as FileApi from '@/api/infra/file'
|
||||
import CryptoJS from 'crypto-js'
|
||||
import { UploadRawFile, UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
|
||||
import axios from 'axios'
|
||||
|
||||
export const useUpload = () => {
|
||||
// 后端上传地址
|
||||
const uploadUrl = import.meta.env.VITE_UPLOAD_URL
|
||||
// 是否使用前端直连上传
|
||||
const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE
|
||||
// 重写ElUpload上传方法
|
||||
const httpRequest = async (options: UploadRequestOptions) => {
|
||||
// 模式一:前端上传
|
||||
if (isClientUpload) {
|
||||
// 1.1 生成文件名称
|
||||
const fileName = await generateFileName(options.file)
|
||||
// 1.2 获取文件预签名地址
|
||||
const presignedInfo = await FileApi.getFilePresignedUrl(fileName)
|
||||
// 1.3 上传文件(不能使用 ElUpload 的 ajaxUpload 方法的原因:其使用的是 FormData 上传,Minio 不支持)
|
||||
return axios.put(presignedInfo.uploadUrl, options.file, {
|
||||
headers: {
|
||||
'Content-Type': options.file.type,
|
||||
}
|
||||
}).then(() => {
|
||||
// 1.4. 记录文件信息到后端(异步)
|
||||
createFile(presignedInfo, fileName, options.file)
|
||||
// 通知成功,数据格式保持与后端上传的返回结果一致
|
||||
return { data: presignedInfo.url , filename: options.file.name}
|
||||
})
|
||||
} else {
|
||||
// 模式二:后端上传
|
||||
// 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
|
||||
return new Promise((resolve, reject) => {
|
||||
FileApi.updateFilePlus({ file: options.file })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
resolve({
|
||||
...res,
|
||||
filename: options.file.name
|
||||
})
|
||||
} else {
|
||||
reject(res)
|
||||
}
|
||||
})
|
||||
.catch((res) => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
uploadUrl,
|
||||
httpRequest
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件信息
|
||||
* @param vo 文件预签名信息
|
||||
* @param name 文件名称
|
||||
* @param file 文件
|
||||
*/
|
||||
function createFile(vo: FileApi.FilePresignedUrlRespVO, name: string, file: UploadRawFile) {
|
||||
const fileVo = {
|
||||
configId: vo.configId,
|
||||
url: vo.url,
|
||||
path: name,
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size
|
||||
}
|
||||
FileApi.createFile(fileVo)
|
||||
return fileVo
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成文件名称(使用算法SHA256)
|
||||
* @param file 要上传的文件
|
||||
*/
|
||||
async function generateFileName(file: UploadRawFile) {
|
||||
// 读取文件内容
|
||||
const data = await file.arrayBuffer()
|
||||
const wordArray = CryptoJS.lib.WordArray.create(data)
|
||||
// 计算SHA256
|
||||
const sha256 = CryptoJS.SHA256(wordArray).toString()
|
||||
// 拼接后缀
|
||||
const ext = file.name.substring(file.name.lastIndexOf('.'))
|
||||
return `${sha256}${ext}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传类型
|
||||
*/
|
||||
enum UPLOAD_TYPE {
|
||||
// 客户端直接上传(只支持S3服务)
|
||||
CLIENT = 'client',
|
||||
// 客户端发送到后端上传
|
||||
SERVER = 'server'
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
|
||||
import { useI18n } from './useI18n'
|
||||
import {ElMessage, ElMessageBox, ElNotification} from 'element-plus'
|
||||
import {useI18n} from './useI18n'
|
||||
|
||||
export const useMessage = () => {
|
||||
const { t } = useI18n()
|
||||
const {t} = useI18n()
|
||||
return {
|
||||
// 消息提示
|
||||
info(content: string) {
|
||||
|
|
@ -25,15 +26,15 @@ export const useMessage = () => {
|
|||
},
|
||||
// 错误提示
|
||||
alertError(content: string) {
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), { type: 'error' })
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), {type: 'error'})
|
||||
},
|
||||
// 成功提示
|
||||
alertSuccess(content: string) {
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), { type: 'success' })
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), {type: 'success'})
|
||||
},
|
||||
// 警告提示
|
||||
alertWarning(content: string) {
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), { type: 'warning' })
|
||||
ElMessageBox.alert(content, t('common.confirmTitle'), {type: 'warning'})
|
||||
},
|
||||
// 通知提示
|
||||
notify(content: string) {
|
||||
|
|
@ -90,6 +91,29 @@ export const useMessage = () => {
|
|||
cancelButtonText: t('common.cancel'),
|
||||
type: 'warning'
|
||||
})
|
||||
},
|
||||
submit({ inputVal, tip, content = '',inputValidator = undefined }: {
|
||||
inputVal: string,
|
||||
tip: string,
|
||||
content?: string,
|
||||
inputValidator?: Function | undefined
|
||||
}) {
|
||||
return ElMessageBox.prompt(content, tip, {
|
||||
inputValue: inputVal,
|
||||
inputPlaceholder: t('common.inputText'),
|
||||
inputValidator: (value) => {
|
||||
if (inputValidator) {
|
||||
return inputValidator(value);
|
||||
} else {
|
||||
if (!value) {
|
||||
return t('common.inputText')
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
confirmButtonText: t('common.ok'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,22 @@ export default {
|
|||
copy: 'Copy',
|
||||
copySuccess: 'Copy Success',
|
||||
copyError: 'Copy Error',
|
||||
keywordPlaceholder: 'Please enter a keyword'
|
||||
keywordPlaceholder: 'Please enter a keyword',
|
||||
fileUploadMaxSize: "Maximum size per file",
|
||||
fileUploadFormat: "Supported formats",
|
||||
fileUploadBtnText: "Upload attachments",
|
||||
fileUploadMaxCountTips: "The number of uploaded files cannot exceed",
|
||||
fileUploadMaxSizeTips: "The size of the uploaded file cannot exceed",
|
||||
fileUploadFormatErrorTips: "The file format is incorrect. Please upload",
|
||||
fileUploadingTips: "The file is being uploaded. Please wait...",
|
||||
fileUploadFailedTips: "The data upload failed. Please upload again!",
|
||||
fileUploadInputFileName: "Please enter the file name",
|
||||
fileUploadNotUploadTips: "Please upload the file first",
|
||||
fileDownloadNameLabel: "File name",
|
||||
fileDownloadSizeLabel: "File size",
|
||||
fileDownloadOptionsLabel: "Operations",
|
||||
fileDownloadTitle: "File list",
|
||||
fileDownloadBtnText: "Download working papers"
|
||||
},
|
||||
lock: {
|
||||
lockScreen: 'Lock screen',
|
||||
|
|
@ -674,15 +689,19 @@ export default {
|
|||
"viewOrder": "viewOrder",
|
||||
"backHome": "BackHome",
|
||||
"downloadSuccess": "downloadSuccess",
|
||||
|
||||
"downloadFile": "Download of working papers",
|
||||
"labelAccessoryFile": "Attachment"
|
||||
},
|
||||
"productDialogList": {
|
||||
"title": "Product List",
|
||||
"colLabelCode": "Product Code",
|
||||
"customerGroupId": 'Customer group',
|
||||
"customerGroupName": 'Customer group',
|
||||
"colLabelType": "Product Type",
|
||||
"colLabelCover": "Cover",
|
||||
"colLabelName": "Product Name",
|
||||
"colLabelRemark": "Remarks",
|
||||
"colLabelDetails": "Details Description"
|
||||
"colLabelDetails": "Details Description",
|
||||
"colLabelFileIds": 'Working paper attachments'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,22 @@ export default {
|
|||
copy: '复制',
|
||||
copySuccess: '复制成功',
|
||||
copyError: '复制失败',
|
||||
keywordPlaceholder: '请输入关键字'
|
||||
keywordPlaceholder: '请输入关键字',
|
||||
fileUploadMaxSize: "单文件最大",
|
||||
fileUploadFormat: "支持格式",
|
||||
fileUploadBtnText: "附件上传",
|
||||
fileUploadMaxCountTips: "上传文件数量不能超过",
|
||||
fileUploadMaxSizeTips: "上传文件大小不能超过",
|
||||
fileUploadFormatErrorTips: "文件格式不正确, 请上传",
|
||||
fileUploadingTips: "正在上传文件,请稍候...",
|
||||
fileUploadFailedTips: "数据失败上传失败,请您重新上传!",
|
||||
fileUploadInputFileName: "请输入文件名称",
|
||||
fileUploadNotUploadTips: "请先上传文件",
|
||||
fileDownloadNameLabel: "文件名称",
|
||||
fileDownloadSizeLabel: "文件大小",
|
||||
fileDownloadOptionsLabel: "操作",
|
||||
fileDownloadTitle: "文件列表",
|
||||
fileDownloadBtnText: "底稿下载",
|
||||
},
|
||||
lock: {
|
||||
lockScreen: '锁定屏幕',
|
||||
|
|
@ -670,14 +685,19 @@ export default {
|
|||
viewOrder: "查看订单",
|
||||
backHome: "返回首页",
|
||||
downloadSuccess: "下载成功",
|
||||
downloadFile: "底稿下载",
|
||||
labelAccessoryFile: "附件"
|
||||
},
|
||||
productDialogList:{
|
||||
title: '产品列表',
|
||||
colLabelCode: '产品编码',
|
||||
customerGroupId: '客户组别',
|
||||
customerGroupName: '客户组别',
|
||||
colLabelType: '产品类型',
|
||||
colLabelCover: '封面',
|
||||
colLabelName: '产品名称',
|
||||
colLabelRemark: '备注',
|
||||
colLabelDetails: '详情描述',
|
||||
colLabelFileIds: '底稿附件',
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,19 @@
|
|||
<el-table-column prop="productCode" :label="t('createOrder.labelColProductCode')"/>
|
||||
<el-table-column prop="productName" :label="t('createOrder.labelColProductName')"/>
|
||||
<el-table-column prop="producer" :label="t('createOrder.labelColProducer')"/>
|
||||
<el-table-column prop="details" :label="t('createOrder.labelColProductDetails')"/>
|
||||
<el-table-column prop="details" :label="t('createOrder.labelColProductDetails')">
|
||||
<template #default="scope">
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="scope.row.details"
|
||||
placement="top-start"
|
||||
>
|
||||
<div class="line-clamp-2">
|
||||
{{scope.row.details}}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="cover" :label="t('createOrder.labelColProductPicture')">
|
||||
<template #default="scope">
|
||||
<el-image
|
||||
|
|
@ -44,7 +56,6 @@
|
|||
</el-table-column>
|
||||
<el-table-column prop="deliveryDate" :label="t('createOrder.labelColDeliveryDate')" width="200">
|
||||
<template #default="scope">
|
||||
|
||||
<el-date-picker
|
||||
v-if="that.isBatch"
|
||||
:disabledDate="(time)=> { return time.getTime() < (Date.now() + 8 * 60 * 60 * 1000); }"
|
||||
|
|
@ -57,6 +68,7 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="orderQty" :label="t('createOrder.labelColOrderQty')"/>
|
||||
|
||||
</el-table>
|
||||
|
||||
</div>
|
||||
|
|
@ -67,6 +79,7 @@
|
|||
import {useEmitt} from "@/hooks/web/useEmitt";
|
||||
import {PRODUCT_DEL_ROW_EVENT, STEP0_FINISH_EVENT} from "@/constants/EmitEventName";
|
||||
import {formatDate} from "@/utils/formatTime";
|
||||
import {copyToClip} from "@/utils";
|
||||
|
||||
const {t} = useI18n() // 国际化
|
||||
const emit = defineEmits(['rowClick'])
|
||||
|
|
@ -80,6 +93,7 @@ const that = reactive({
|
|||
filterParam: {
|
||||
brandId: '',
|
||||
},
|
||||
fileIds: '270,269,271,288',
|
||||
})
|
||||
|
||||
const handleEvent = (data: any) => {
|
||||
|
|
@ -117,7 +131,11 @@ const submit = (data) => {
|
|||
addRow(data[i])
|
||||
}
|
||||
}
|
||||
|
||||
const copyData = (str: string) => {
|
||||
copyToClip(str, () => {
|
||||
useMessage.success(t('common.copySuccess'));
|
||||
})
|
||||
}
|
||||
const addRow = (row) => {
|
||||
for (let i = 0; i < that.tableData.length; i++) {
|
||||
if (that.tableData[i].productId === row.id) {
|
||||
|
|
@ -125,6 +143,7 @@ const addRow = (row) => {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!row.deliveryDate) {
|
||||
row.deliveryDate = Date.now() + (24 * 60 * 60 * 1000)
|
||||
}
|
||||
|
|
@ -204,7 +223,6 @@ defineExpose({
|
|||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
:deep(.el-table__body tr.current-row>td) {
|
||||
background-color: rgba(49, 106, 197, 0.81) !important; // 设置高亮的背景颜色
|
||||
color: #ffffff;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
<template>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<div class="flex">
|
||||
<el-button
|
||||
type="primary"
|
||||
v-show="that.templateType !== '2' && that.tableData.length > 0"
|
||||
@click="addSku">{{ t('createOrder.btnAddSku') }}
|
||||
</el-button>
|
||||
<div v-if="that.productInfo.fileIds" class="ml-2">
|
||||
<AccessoryDownload
|
||||
v-model="that.productInfo.fileIds"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap" v-if="that.templateType === '1'">
|
||||
<template v-for="(item,index) in that.tableData" :key="item.key">
|
||||
|
|
@ -17,7 +21,6 @@
|
|||
ref="productItem1Ref"
|
||||
@del-item="delItem"
|
||||
:badge="`${index+1}`"
|
||||
|
||||
v-model:product-info="that.tableData[index]"/>
|
||||
</template>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -223,6 +223,17 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<el-form-item :label="t('createOrder.labelAccessoryFile')" prop="remarks">
|
||||
<AccessoryUpload
|
||||
:fileType="['zip', 'rar','xls', 'doc', 'xlsx', 'docx','pdf','png','jpg', 'jpeg']"
|
||||
:limit="10"
|
||||
v-model="formData.accessoryFileIds" />
|
||||
</el-form-item>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
</el-collapse-item>
|
||||
|
|
@ -544,6 +555,7 @@ const formData = ref({
|
|||
saleOrderEntry: [],
|
||||
orderFollowerUser: '',
|
||||
rejectReason: '',
|
||||
accessoryFileIds: '',
|
||||
})
|
||||
const isEditState = computed(() => {
|
||||
return route.query.id != undefined && route.query.type == undefined
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
-- 稿件模板 底稿附件id
|
||||
ALTER TABLE oms_draft_design_data
|
||||
ADD COLUMN `file_ids` varchar(1024) DEFAULT NULL COMMENT '底稿附件多个 infra_file 表 id' AFTER `cover`;
|
||||
-- 产品信息 添加客户组
|
||||
ALTER TABLE oms_product_info
|
||||
ADD COLUMN `customer_group_id` varchar(1024) DEFAULT NULL COMMENT 'oms_customer_group 表 id';
|
||||
|
||||
|
||||
-- 销售订单 添加附件上传
|
||||
ALTER TABLE oms_saleorder
|
||||
ADD COLUMN `accessory_file_ids` varchar(1024) DEFAULT NULL COMMENT '下单附件多个 infra_file 表 id';
|
||||
|
||||
-- 客户组别
|
||||
DROP TABLE IF EXISTS oms_customer_group;
|
||||
CREATE TABLE oms_customer_group(
|
||||
id BIGINT(19) NOT NULL AUTO_INCREMENT COMMENT 'id' ,
|
||||
code VARCHAR(64) COMMENT '编码' ,
|
||||
name VARCHAR(512) NOT NULL COMMENT '名称' ,
|
||||
sort INT DEFAULT 0 COMMENT '排序号' ,
|
||||
remark VARCHAR(512) COMMENT '备注' ,
|
||||
creator VARCHAR(64) COMMENT '创建者' ,
|
||||
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' ,
|
||||
updater VARCHAR(64) COMMENT '更新者' ,
|
||||
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间' ,
|
||||
deleted BIT(1) NOT NULL DEFAULT 0 COMMENT '是否删除' ,
|
||||
tenant_id BIGINT(19) NOT NULL COMMENT '租户编号' ,
|
||||
PRIMARY KEY (id)
|
||||
) COMMENT = '客户组别 ';
|
||||
|
||||
|
||||
-- 菜单sql
|
||||
INSERT INTO `system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2855, '客户组别 导出', 'oms:customer-group:export', 3, 5, 2850, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-04 09:56:36', '', '2025-03-04 09:56:36', b'0');
|
||||
INSERT INTO `system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2854, '客户组别 删除', 'oms:customer-group:delete', 3, 4, 2850, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-04 09:56:35', '', '2025-03-04 09:56:35', b'0');
|
||||
INSERT INTO `system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2852, '客户组别 创建', 'oms:customer-group:create', 3, 2, 2850, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-04 09:56:34', '', '2025-03-04 09:56:34', b'0');
|
||||
INSERT INTO `system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2853, '客户组别 更新', 'oms:customer-group:update', 3, 3, 2850, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-04 09:56:34', '', '2025-03-04 09:56:34', b'0');
|
||||
INSERT INTO `system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2851, '客户组别 查询', 'oms:customer-group:query', 3, 1, 2850, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-04 09:56:32', '', '2025-03-04 09:56:32', b'0');
|
||||
INSERT INTO `system_menu`(`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2850, '客户组别 管理', '', 2, 0, 2758, 'customer-group', '', 'oms/customergroup/index', 'CustomerGroup', 0, b'1', b'1', b'1', '', '2025-03-04 09:56:30', '', '2025-03-04 09:56:30', b'0');
|
||||
|
||||
Loading…
Reference in New Issue