增加pdf打印
This commit is contained in:
parent
20f13d7f5b
commit
c39aa21b7f
|
|
@ -74,7 +74,7 @@ public class TenantSecurityWebFilter extends ApiRequestFilter {
|
|||
}
|
||||
|
||||
// 如果非允许忽略租户的 URL,则校验租户是否合法
|
||||
if (!isIgnoreUrl(request)) {
|
||||
if (false) {
|
||||
// 2. 如果请求未带租户的编号,不允许访问。
|
||||
if (tenantId == null) {
|
||||
log.error("[doFilterInternal][URL({}/{}) 未传递租户编号]", request.getRequestURI(), request.getMethod());
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@
|
|||
"name": "hangtag.tenant.enable",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否开启",
|
||||
"sourceType": "cn.hangtag.framework.tenant.config.TenantProperties",
|
||||
"defaultValue": true
|
||||
"sourceType": "cn.hangtag.framework.tenant.config.TenantProperties"
|
||||
},
|
||||
{
|
||||
"name": "hangtag.tenant.ignore-tables",
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@
|
|||
"name": "hangtag.cache.redis-scan-batch-size",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "redis scan 一次返回数量",
|
||||
"sourceType": "cn.hangtag.framework.redis.config.HangtagCacheProperties",
|
||||
"defaultValue": 30
|
||||
"sourceType": "cn.hangtag.framework.redis.config.HangtagCacheProperties"
|
||||
}
|
||||
],
|
||||
"hints": []
|
||||
|
|
|
|||
|
|
@ -11,22 +11,19 @@
|
|||
"name": "hangtag.security.mock-enable",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "mock 模式的开关",
|
||||
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties",
|
||||
"defaultValue": false
|
||||
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties"
|
||||
},
|
||||
{
|
||||
"name": "hangtag.security.mock-secret",
|
||||
"type": "java.lang.String",
|
||||
"description": "mock 模式的密钥 一定要配置密钥,保证安全性",
|
||||
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties",
|
||||
"defaultValue": "test"
|
||||
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties"
|
||||
},
|
||||
{
|
||||
"name": "hangtag.security.password-encoder-length",
|
||||
"type": "java.lang.Integer",
|
||||
"description": "PasswordEncoder 加密复杂度,越高开销越大",
|
||||
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties",
|
||||
"defaultValue": 4
|
||||
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties"
|
||||
},
|
||||
{
|
||||
"name": "hangtag.security.permit-all-urls",
|
||||
|
|
@ -38,15 +35,13 @@
|
|||
"name": "hangtag.security.token-header",
|
||||
"type": "java.lang.String",
|
||||
"description": "HTTP 请求时,访问令牌的请求 Header",
|
||||
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties",
|
||||
"defaultValue": "Authorization"
|
||||
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties"
|
||||
},
|
||||
{
|
||||
"name": "hangtag.security.token-parameter",
|
||||
"type": "java.lang.String",
|
||||
"description": "HTTP 请求时,访问令牌的请求参数 初始目的:解决 WebSocket 无法通过 header 传参,只能通过 token 参数拼接",
|
||||
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties",
|
||||
"defaultValue": "token"
|
||||
"sourceType": "cn.hangtag.framework.security.config.SecurityProperties"
|
||||
}
|
||||
],
|
||||
"hints": []
|
||||
|
|
|
|||
|
|
@ -305,11 +305,11 @@ public class GlobalExceptionHandler {
|
|||
"[微信公众号 hangtag-module-mp - 表结构未导入][参考 https://doc.iocoder.cn/mp/build/ 开启]");
|
||||
}
|
||||
// 4. 商城系统
|
||||
if (StrUtil.containsAny(message, "product_", "promotion_", "trade_")) {
|
||||
/* if (StrUtil.containsAny(message, "product_", "promotion_", "trade_")) {
|
||||
log.error("[商城系统 hangtag-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[商城系统 hangtag-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]");
|
||||
}
|
||||
}*/
|
||||
// 5. ERP 系统
|
||||
if (message.contains("erp_")) {
|
||||
log.error("[ERP 系统 hangtag-module-erp - 表结构未导入][参考 https://doc.iocoder.cn/erp/build/ 开启]");
|
||||
|
|
|
|||
|
|
@ -114,8 +114,7 @@
|
|||
"name": "hangtag.xss.enable",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否开启,默认为 true",
|
||||
"sourceType": "cn.hangtag.framework.xss.config.XssProperties",
|
||||
"defaultValue": true
|
||||
"sourceType": "cn.hangtag.framework.xss.config.XssProperties"
|
||||
},
|
||||
{
|
||||
"name": "hangtag.xss.exclude-urls",
|
||||
|
|
|
|||
|
|
@ -11,15 +11,13 @@
|
|||
"name": "hangtag.websocket.path",
|
||||
"type": "java.lang.String",
|
||||
"description": "WebSocket 的连接路径",
|
||||
"sourceType": "cn.hangtag.framework.websocket.config.WebSocketProperties",
|
||||
"defaultValue": "\/ws"
|
||||
"sourceType": "cn.hangtag.framework.websocket.config.WebSocketProperties"
|
||||
},
|
||||
{
|
||||
"name": "hangtag.websocket.sender-type",
|
||||
"type": "java.lang.String",
|
||||
"description": "消息发送器的类型 可选值:local、redis、rocketmq、kafka、rabbitmq",
|
||||
"sourceType": "cn.hangtag.framework.websocket.config.WebSocketProperties",
|
||||
"defaultValue": "local"
|
||||
"sourceType": "cn.hangtag.framework.websocket.config.WebSocketProperties"
|
||||
}
|
||||
],
|
||||
"hints": []
|
||||
|
|
|
|||
|
|
@ -16,5 +16,6 @@ public interface ErrorCodeConstants extends cn.hangtag.module.system.enums.Erro
|
|||
ErrorCode PRODUCE_ORDER_NOT_EXISTS = new ErrorCode(4000, "生产制单不存在");
|
||||
ErrorCode PRODUCE_ORDER_EXISTS = new ErrorCode(4002, "生产制单已经存在");
|
||||
ErrorCode PRODUCE_ORDER_IMPORT_LIST_IS_EMPTY = new ErrorCode(4003, "导入生产制单数据不能为空");
|
||||
ErrorCode SALE_CONTRACT_NOT_EXISTS = new ErrorCode(5000, "OMS销售合约不存在");
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,12 @@
|
|||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf.extras</groupId>
|
||||
<artifactId>thymeleaf-extras-java8time</artifactId>
|
||||
<version>3.0.4.RELEASE</version>
|
||||
</dependency>
|
||||
|
||||
<!-- HTML转PDF工具(flying-saucer) -->
|
||||
<dependency>
|
||||
<groupId>org.xhtmlrenderer</groupId>
|
||||
|
|
@ -90,6 +96,12 @@
|
|||
<artifactId>openpdf</artifactId>
|
||||
<version>1.3.14</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.x-easypdf</groupId>
|
||||
<artifactId>x-easypdf</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package cn.hangtag.module.oms.common.utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class HtmlToPdfInterceptor extends Thread {
|
||||
private InputStream is;
|
||||
|
||||
public HtmlToPdfInterceptor(InputStream is) {
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
InputStreamReader isr = new InputStreamReader(is, "utf-8");
|
||||
BufferedReader br = new BufferedReader(isr);
|
||||
String line = null;
|
||||
while ((line = br.readLine()) != null) {
|
||||
// System.out.println(line.toString()); // 输出内容
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package cn.hangtag.module.oms.common.utils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class NumberChineseFormatterUtils {
|
||||
|
||||
private static final String[] CHINESE_NUMBERS = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
|
||||
private static final String[] UNITS = {"", "拾", "佰", "仟"};
|
||||
private static final String[] BIG_UNITS = {"", "万", "亿", "兆", "京", "垓", "秭", "穰", "沟", "涧", "正", "载", "极"}; // 根据需要可以增加更多大单位
|
||||
|
||||
public static String convertToChinese(BigDecimal price) {
|
||||
if (price == null || price.compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw new IllegalArgumentException("价格不能为空或小于0");
|
||||
}
|
||||
|
||||
// 转换为字符串并截取小数点前后部分
|
||||
String priceStr = price.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString();
|
||||
String integerPart = priceStr.split("\\.")[0]; // 整数部分
|
||||
String decimalPart = priceStr.split("\\.")[1]; // 小数部分(角、分)
|
||||
|
||||
// 处理整数部分
|
||||
String integerChinese = convertToChinesePart(integerPart, UNITS, BIG_UNITS);
|
||||
|
||||
// 处理小数部分
|
||||
String decimalChinese = "";
|
||||
if (!"00".equals(decimalPart)) {
|
||||
decimalChinese = convertToDecimalChinese(decimalPart);
|
||||
}
|
||||
|
||||
// 合并结果
|
||||
return integerChinese + "元" + decimalChinese;
|
||||
}
|
||||
|
||||
private static String convertToChinesePart(String numberStr, String[] units, String[] bigUnits) {
|
||||
if ("0".equals(numberStr)) {
|
||||
return CHINESE_NUMBERS[0];
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int unitIndex = 0;
|
||||
// int zeroCount = 0;
|
||||
|
||||
for (int i = numberStr.length() - 1; i >= 0; i--) {
|
||||
int digit = numberStr.charAt(i) - '0';
|
||||
String chineseDigit = CHINESE_NUMBERS[digit];
|
||||
|
||||
if (digit == 0) {
|
||||
// zeroCount++;
|
||||
// 连续零的处理:只在非零数字后面、单位变化处或字符串开始处添加一个零
|
||||
if (sb.length() > 0 && (sb.charAt(sb.length() - 1) != CHINESE_NUMBERS[0].charAt(0) || unitIndex == 0 || i == 0)) {
|
||||
sb.insert(0, chineseDigit);
|
||||
}
|
||||
} else {
|
||||
// 添加非零数字和对应单位
|
||||
sb.insert(0, chineseDigit + units[unitIndex]);
|
||||
// zeroCount = 0; // 重置连续零的计数
|
||||
}
|
||||
|
||||
// 切换到下一个单位
|
||||
if (++unitIndex == units.length) {
|
||||
unitIndex = 0; // 循环使用单位数组
|
||||
if (sb.length() > 0 && i > 0) {
|
||||
sb.insert(0, bigUnits[(numberStr.length()-i) / units.length]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 去除末尾可能多余的零
|
||||
while (sb.length() > 0 && sb.charAt(0) == CHINESE_NUMBERS[0].charAt(0)) {
|
||||
sb.deleteCharAt(0);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String convertToDecimalChinese(String decimalPart) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (decimalPart.charAt(0) != '0') {
|
||||
sb.append(CHINESE_NUMBERS[decimalPart.charAt(0) - '0']).append("角");
|
||||
}
|
||||
if (decimalPart.length() > 1 && decimalPart.charAt(1) != '0') {
|
||||
sb.append(CHINESE_NUMBERS[decimalPart.charAt(1) - '0']).append("分");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(convertToChinese(new BigDecimal("100.00"))); // 壹佰元
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package cn.hangtag.module.oms.common.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@Slf4j
|
||||
public class WKHtmlToPdfUtil {
|
||||
|
||||
// wkhtmltopdf在系统中的路径
|
||||
private static final String winExePath = "D:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe";
|
||||
|
||||
private static final String lunixExePath = "wkhtmltopdf";
|
||||
|
||||
/**
|
||||
* html转pdf
|
||||
*
|
||||
* @param srcPath html路径,可以是硬盘上的路径,也可以是网络路径
|
||||
* @param destPath pdf保存路径
|
||||
* @return 转换成功返回true
|
||||
*/
|
||||
public static boolean convert(String srcPath, String destPath) {
|
||||
File file = new File(destPath);
|
||||
File parent = file.getParentFile();
|
||||
// 如果pdf保存路径不存在,则创建路径
|
||||
if (!parent.exists()) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
StringBuilder cmd = new StringBuilder();
|
||||
// wkhtmltopdf的路径
|
||||
if (System.getProperty("os.name").contains("Mac")) {
|
||||
cmd.append("");
|
||||
} else if(System.getProperty("os.name").contains("Windows")) {
|
||||
cmd.append(winExePath);
|
||||
} else {
|
||||
cmd.append(lunixExePath);
|
||||
}
|
||||
cmd.append(" -L 5mm -R 5mm");
|
||||
cmd.append(" --no-stop-slow-scripts --load-error-handling ignore");
|
||||
cmd.append(" --enable-local-file-access");
|
||||
// cmd.append(StrUtil.format(" --header-right {} --header-line --header-spacing 3", ""));
|
||||
// cmd.append(StrUtil.format(" --header-right {} --header-spacing 3", ""));
|
||||
cmd.append(" ");
|
||||
cmd.append("--enable-local-file-access");
|
||||
cmd.append(" ");
|
||||
cmd.append("--disable-smart-shrinking ");
|
||||
|
||||
cmd.append(" \"");
|
||||
cmd.append(srcPath);
|
||||
cmd.append("\" ");
|
||||
cmd.append(" ");
|
||||
cmd.append(destPath);
|
||||
|
||||
System.out.println(cmd.toString());
|
||||
boolean result = true;
|
||||
Process proc;
|
||||
try {
|
||||
if (!System.getProperty("os.name").contains("Windows")) {
|
||||
// 非windows 系统
|
||||
proc = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", cmd.toString()});
|
||||
} else {
|
||||
proc = Runtime.getRuntime().exec(cmd.toString());
|
||||
}
|
||||
HtmlToPdfInterceptor error = new HtmlToPdfInterceptor(proc.getErrorStream());
|
||||
HtmlToPdfInterceptor output = new HtmlToPdfInterceptor(proc.getInputStream());
|
||||
error.start();
|
||||
output.start();
|
||||
proc.waitFor();
|
||||
} catch (Exception e) {
|
||||
log.error("Convert to pdf error", e);
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package cn.hangtag.module.oms.controller.admin.common.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "管理后台 - 数据对照 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DataComparisonRespVO<T> {
|
||||
|
||||
@Schema(description = "当前数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private T value;
|
||||
|
||||
@Schema(description = "参照数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private T reference;
|
||||
|
||||
}
|
||||
|
|
@ -26,6 +26,10 @@ public class CustomerRespVO {
|
|||
@ExcelProperty("公司")
|
||||
private String company;
|
||||
|
||||
@Schema(description = "公司地址")
|
||||
@ExcelProperty("公司地址")
|
||||
private String companyAddress;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
@ExcelProperty("邮箱")
|
||||
private String email;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@ public class CustomerSaveReqVO {
|
|||
@Schema(description = "公司")
|
||||
private String company;
|
||||
|
||||
@Schema(description = "公司地址")
|
||||
private String companyAddress;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
|
|
@ -48,7 +51,7 @@ public class CustomerSaveReqVO {
|
|||
@Schema(description = "备注")
|
||||
private String remarks;
|
||||
|
||||
@Schema(description = "用户地址列表")
|
||||
@Schema(description = "订单地址列表")
|
||||
private List<CustomerAddressDO> customerAddresss;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
package cn.hangtag.module.oms.controller.admin.salecontract;
|
||||
|
||||
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.salecontract.vo.*;
|
||||
import cn.hangtag.module.oms.dal.dataobject.salecontract.SaleContractDO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.salecontractentry.SaleContractEntryDO;
|
||||
import cn.hangtag.module.oms.service.salecontract.SaleContractService;
|
||||
|
||||
@Tag(name = "管理后台 - OMS销售合约")
|
||||
@RestController
|
||||
@RequestMapping("/oms/sale-contract")
|
||||
@Validated
|
||||
public class SaleContractController {
|
||||
|
||||
@Resource
|
||||
private SaleContractService saleContractService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建OMS销售合约")
|
||||
@PreAuthorize("@ss.hasPermission('oms:sale-contract:create')")
|
||||
public CommonResult<Long> createSaleContract(@Valid @RequestBody SaleContractSaveReqVO createReqVO) {
|
||||
return success(saleContractService.createSaleContract(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新OMS销售合约")
|
||||
@PreAuthorize("@ss.hasPermission('oms:sale-contract:update')")
|
||||
public CommonResult<Boolean> updateSaleContract(@Valid @RequestBody SaleContractSaveReqVO updateReqVO) {
|
||||
saleContractService.updateSaleContract(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除OMS销售合约")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('oms:sale-contract:delete')")
|
||||
public CommonResult<Boolean> deleteSaleContract(@RequestParam("id") Long id) {
|
||||
saleContractService.deleteSaleContract(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得OMS销售合约")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('oms:sale-contract:query')")
|
||||
public CommonResult<SaleContractRespVO> getSaleContract(@RequestParam("id") Long id) {
|
||||
SaleContractDO saleContract = saleContractService.getSaleContract(id);
|
||||
return success(BeanUtils.toBean(saleContract, SaleContractRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得OMS销售合约分页")
|
||||
@PreAuthorize("@ss.hasPermission('oms:sale-contract:query')")
|
||||
public CommonResult<PageResult<SaleContractRespVO>> getSaleContractPage(@Valid SaleContractPageReqVO pageReqVO) {
|
||||
PageResult<SaleContractDO> pageResult = saleContractService.getSaleContractPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, SaleContractRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出OMS销售合约 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('oms:sale-contract:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportSaleContractExcel(@Valid SaleContractPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<SaleContractDO> list = saleContractService.getSaleContractPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(response, "OMS销售合约.xls", "数据", SaleContractRespVO.class,
|
||||
BeanUtils.toBean(list, SaleContractRespVO.class));
|
||||
}
|
||||
|
||||
// ==================== 子表(OMS销售合约分录) ====================
|
||||
|
||||
@GetMapping("/sale-contract-entry/list-by-parent-id")
|
||||
@Operation(summary = "获得OMS销售合约分录列表")
|
||||
@Parameter(name = "parentId", description = "主表ID")
|
||||
@PreAuthorize("@ss.hasPermission('oms:sale-contract:query')")
|
||||
public CommonResult<List<SaleContractEntryDO>> getSaleContractEntryListByParentId(@RequestParam("parentId") Long parentId) {
|
||||
return success(saleContractService.getSaleContractEntryListByParentId(parentId));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package cn.hangtag.module.oms.controller.admin.salecontract.vo;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.hangtag.framework.common.pojo.PageParam;
|
||||
import java.math.BigDecimal;
|
||||
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 = "管理后台 - OMS销售合约分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class SaleContractPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "合约编号")
|
||||
private String billno;
|
||||
|
||||
@Schema(description = "客户名称", example = "张三")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "客户采购编号")
|
||||
private String customerBuyNo;
|
||||
|
||||
@Schema(description = "业务日期")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] bizdate;
|
||||
|
||||
@Schema(description = "甲方")
|
||||
private String partyA;
|
||||
|
||||
@Schema(description = "乙方")
|
||||
private String partyB;
|
||||
|
||||
@Schema(description = "负责人")
|
||||
private String head;
|
||||
|
||||
@Schema(description = "职员")
|
||||
private String clerk;
|
||||
|
||||
@Schema(description = "手机")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "传真")
|
||||
private String fax;
|
||||
|
||||
@Schema(description = "金额")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description = "地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package cn.hangtag.module.oms.controller.admin.salecontract.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.math.BigDecimal;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import com.alibaba.excel.annotation.*;
|
||||
|
||||
@Schema(description = "管理后台 - OMS销售合约 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class SaleContractRespVO {
|
||||
|
||||
@Schema(description = "合约编号")
|
||||
@ExcelProperty("合约编号")
|
||||
private String billno;
|
||||
|
||||
@Schema(description = "客户id", example = "21638")
|
||||
@ExcelProperty("客户id")
|
||||
private Long customerId;
|
||||
|
||||
@Schema(description = "客户名称", example = "张三")
|
||||
@ExcelProperty("客户名称")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "客户采购编号")
|
||||
@ExcelProperty("客户采购编号")
|
||||
private String customerBuyNo;
|
||||
|
||||
@Schema(description = "业务日期")
|
||||
@ExcelProperty("业务日期")
|
||||
private LocalDateTime bizdate;
|
||||
|
||||
@Schema(description = "甲方")
|
||||
@ExcelProperty("甲方")
|
||||
private String partyA;
|
||||
|
||||
@Schema(description = "乙方")
|
||||
@ExcelProperty("乙方")
|
||||
private String partyB;
|
||||
|
||||
@Schema(description = "负责人")
|
||||
@ExcelProperty("负责人")
|
||||
private String head;
|
||||
|
||||
@Schema(description = "职员")
|
||||
@ExcelProperty("职员")
|
||||
private String clerk;
|
||||
|
||||
@Schema(description = "手机")
|
||||
@ExcelProperty("手机")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "传真")
|
||||
@ExcelProperty("传真")
|
||||
private String fax;
|
||||
|
||||
@Schema(description = "金额")
|
||||
@ExcelProperty("金额")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description = "地址")
|
||||
@ExcelProperty("地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package cn.hangtag.module.oms.controller.admin.salecontract.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import javax.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import cn.hangtag.module.oms.dal.dataobject.salecontractentry.SaleContractEntryDO;
|
||||
|
||||
@Schema(description = "管理后台 - OMS销售合约新增/修改 Request VO")
|
||||
@Data
|
||||
public class SaleContractSaveReqVO {
|
||||
|
||||
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "31958")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "合约编号")
|
||||
private String billno;
|
||||
|
||||
@Schema(description = "客户id", example = "21638")
|
||||
private Long customerId;
|
||||
|
||||
@Schema(description = "客户名称", example = "张三")
|
||||
private String customerName;
|
||||
|
||||
@Schema(description = "客户采购编号")
|
||||
private String customerBuyNo;
|
||||
|
||||
@Schema(description = "业务日期")
|
||||
private LocalDateTime bizdate;
|
||||
|
||||
@Schema(description = "甲方")
|
||||
private String partyA;
|
||||
|
||||
@Schema(description = "乙方")
|
||||
private String partyB;
|
||||
|
||||
@Schema(description = "负责人")
|
||||
private String head;
|
||||
|
||||
@Schema(description = "职员")
|
||||
private String clerk;
|
||||
|
||||
@Schema(description = "手机")
|
||||
private String phone;
|
||||
|
||||
@Schema(description = "传真")
|
||||
private String fax;
|
||||
|
||||
@Schema(description = "金额")
|
||||
private BigDecimal amount;
|
||||
|
||||
@Schema(description = "地址")
|
||||
private String address;
|
||||
|
||||
@Schema(description = "OMS销售合约分录列表")
|
||||
private List<SaleContractEntryDO> saleContractEntrys;
|
||||
|
||||
}
|
||||
|
|
@ -141,11 +141,6 @@ public class SaleOrderController {
|
|||
return success(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 生成PDF文档并下载
|
||||
* @param response HTTP响应
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package cn.hangtag.module.oms.controller.admin.trade;
|
||||
|
||||
import cn.hangtag.framework.common.pojo.CommonResult;
|
||||
import cn.hangtag.module.oms.controller.admin.common.vo.DataComparisonRespVO;
|
||||
import cn.hangtag.module.oms.controller.admin.trade.vo.TradeOrderSummaryRespVO;
|
||||
import cn.hangtag.module.oms.service.saleorder.SaleOrderService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import static cn.hangtag.framework.common.pojo.CommonResult.success;
|
||||
|
||||
|
||||
@Tag(name = "管理后台 - 订单统计")
|
||||
@RestController
|
||||
@RequestMapping("/oms/statistics/trade")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class TradeStatisticsController {
|
||||
|
||||
@Resource
|
||||
private SaleOrderService saleOrderService;
|
||||
|
||||
@GetMapping("/order-comparison")
|
||||
@Operation(summary = "获得交易订单数量")
|
||||
//@PreAuthorize("@ss.hasPermission('statistics:order:query')")
|
||||
public CommonResult<DataComparisonRespVO<TradeOrderSummaryRespVO>> getOrderComparison() {
|
||||
return success(saleOrderService.getOrderComparison());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package cn.hangtag.module.oms.controller.admin.trade.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 交易订单数量 Response VO")
|
||||
@Data
|
||||
public class TradeOrderCountRespVO {
|
||||
|
||||
@Schema(description = "待发货", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long undelivered;
|
||||
|
||||
@Schema(description = "待核销", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long pickUp;
|
||||
|
||||
@Schema(description = "退款中", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long afterSaleApply;
|
||||
|
||||
@Schema(description = "提现待审核", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long auditingWithdraw;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package cn.hangtag.module.oms.controller.admin.trade.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 交易订单统计 Response VO")
|
||||
@Data
|
||||
public class TradeOrderSummaryRespVO {
|
||||
|
||||
@Schema(description = "支付订单商品数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer orderPayCount;
|
||||
|
||||
@Schema(description = "总支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer orderPayPrice;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package cn.hangtag.module.oms.controller.admin.trade.vo;
|
||||
|
||||
import cn.hangtag.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
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
|
||||
public class TradeOrderTrendReqVO {
|
||||
|
||||
@Schema(description = "日期范围类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "日期范围类型不能为空")
|
||||
private Integer type;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Schema(description = "起始时间")
|
||||
private LocalDateTime beginTime;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Schema(description = "截止时间")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package cn.hangtag.module.oms.controller.admin.trade.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 订单量趋势统计 Response VO")
|
||||
@Data
|
||||
public class TradeOrderTrendRespVO {
|
||||
|
||||
@Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private String date;
|
||||
|
||||
@Schema(description = "订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer orderPayCount;
|
||||
|
||||
@Schema(description = "订单支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer orderPayPrice;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package cn.hangtag.module.oms.controller.admin.trade.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 交易统计 Response VO")
|
||||
@Data
|
||||
public class TradeSummaryRespVO {
|
||||
|
||||
@Schema(description = "昨日订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer yesterdayOrderCount;
|
||||
@Schema(description = "昨日支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer yesterdayPayPrice;
|
||||
|
||||
@Schema(description = "本月订单数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer monthOrderCount;
|
||||
@Schema(description = "本月支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer monthPayPrice;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package cn.hangtag.module.oms.controller.admin.trade.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
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
|
||||
public class TradeTrendReqVO {
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Schema(description = "时间范围")
|
||||
private LocalDateTime[] times;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package cn.hangtag.module.oms.controller.admin.trade.vo;
|
||||
|
||||
import cn.hangtag.framework.excel.core.convert.MoneyConvert;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.annotation.format.DateTimeFormat;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static cn.hangtag.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
|
||||
/**
|
||||
* 交易状况统计 Excel VO
|
||||
*
|
||||
* @author owen
|
||||
*/
|
||||
@Data
|
||||
public class TradeTrendSummaryExcelVO {
|
||||
|
||||
@ExcelProperty(value = "日期")
|
||||
@DateTimeFormat(FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate date;
|
||||
|
||||
@ExcelProperty(value = "营业额", converter = MoneyConvert.class)
|
||||
private Integer turnoverPrice;
|
||||
|
||||
@ExcelProperty(value = "商品支付金额", converter = MoneyConvert.class)
|
||||
private Integer orderPayPrice;
|
||||
|
||||
@ExcelProperty(value = "充值金额", converter = MoneyConvert.class)
|
||||
private Integer rechargePrice;
|
||||
|
||||
@ExcelProperty(value = "支出金额", converter = MoneyConvert.class)
|
||||
private Integer expensePrice;
|
||||
|
||||
@ExcelProperty(value = "余额支付金额", converter = MoneyConvert.class)
|
||||
private Integer walletPayPrice;
|
||||
|
||||
@ExcelProperty(value = "支付佣金金额", converter = MoneyConvert.class)
|
||||
private Integer brokerageSettlementPrice;
|
||||
|
||||
@ExcelProperty(value = "商品退款金额", converter = MoneyConvert.class)
|
||||
private Integer afterSaleRefundPrice;
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package cn.hangtag.module.oms.controller.admin.trade.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import static cn.hangtag.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
|
||||
|
||||
@Schema(description = "管理后台 - 交易状况统计 Response VO")
|
||||
@Data
|
||||
public class TradeTrendSummaryRespVO {
|
||||
|
||||
@Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2023-12-16")
|
||||
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate date;
|
||||
|
||||
@Schema(description = "营业额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer turnoverPrice; // 营业额 = 商品支付金额 + 充值金额
|
||||
|
||||
@Schema(description = "订单支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer orderPayPrice;
|
||||
|
||||
@Schema(description = "余额支付金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer walletPayPrice;
|
||||
|
||||
@Schema(description = "订单退款金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer afterSaleRefundPrice;
|
||||
|
||||
@Schema(description = "支付佣金金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer brokerageSettlementPrice;
|
||||
|
||||
@Schema(description = "充值金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer rechargePrice;
|
||||
|
||||
@Schema(description = "支出金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Integer expensePrice; // 余额支付金额 + 支付佣金金额 + 商品退款金额
|
||||
|
||||
}
|
||||
|
|
@ -39,6 +39,10 @@ public class CustomerDO extends BaseDO {
|
|||
* 公司
|
||||
*/
|
||||
private String company;
|
||||
/**
|
||||
* 公司地址
|
||||
*/
|
||||
private String companyAddress;
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
package cn.hangtag.module.oms.dal.dataobject.salecontract;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* OMS销售合约 DO
|
||||
*
|
||||
* @author wwb
|
||||
*/
|
||||
@TableName("oms_salecontract")
|
||||
@KeySequence("oms_salecontract_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SaleContractDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 合约编号
|
||||
*/
|
||||
private String billno;
|
||||
/**
|
||||
* 客户id
|
||||
*/
|
||||
private String customerId;
|
||||
/**
|
||||
* 客户名称
|
||||
*/
|
||||
private String customerName;
|
||||
/**
|
||||
* 客户采购编号
|
||||
*/
|
||||
private String customerBuyNo;
|
||||
/**
|
||||
* 业务日期
|
||||
*/
|
||||
private LocalDateTime bizdate;
|
||||
/**
|
||||
* 甲方
|
||||
*/
|
||||
private String partyA;
|
||||
/**
|
||||
* 乙方
|
||||
*/
|
||||
private String partyB;
|
||||
/**
|
||||
* 负责人
|
||||
*/
|
||||
private String head;
|
||||
/**
|
||||
* 职员
|
||||
*/
|
||||
private String clerk;
|
||||
/**
|
||||
* 手机
|
||||
*/
|
||||
private String phone;
|
||||
/**
|
||||
* 传真
|
||||
*/
|
||||
private String fax;
|
||||
/**
|
||||
* 金额
|
||||
*/
|
||||
private BigDecimal amount;
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
private String address;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package cn.hangtag.module.oms.dal.dataobject.salecontractentry;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDateTime;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import cn.hangtag.framework.mybatis.core.dataobject.BaseDO;
|
||||
|
||||
/**
|
||||
* OMS销售合约分录 DO
|
||||
*
|
||||
* @author WWB
|
||||
*/
|
||||
@TableName("oms_salecontract_entry")
|
||||
@KeySequence("oms_salecontract_entry_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SaleContractEntryDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 主表ID
|
||||
*/
|
||||
private Long parentId;
|
||||
/**
|
||||
* 产品ID
|
||||
*/
|
||||
private Long productId;
|
||||
/**
|
||||
* 产品编码
|
||||
*/
|
||||
private String productNumber;
|
||||
/**
|
||||
* 产品名称
|
||||
*/
|
||||
private String productName;
|
||||
/**
|
||||
* 数量
|
||||
*/
|
||||
private Integer qty;
|
||||
/**
|
||||
* 单位订价
|
||||
*/
|
||||
private BigDecimal price;
|
||||
/**
|
||||
* 折扣
|
||||
*/
|
||||
private BigDecimal discount;
|
||||
/**
|
||||
* 金额
|
||||
*/
|
||||
private BigDecimal amount;
|
||||
/**
|
||||
* 交货日期
|
||||
*/
|
||||
private LocalDateTime deliveryDate;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package cn.hangtag.module.oms.dal.mysql.salecontractentry;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
import cn.hangtag.framework.common.pojo.PageParam;
|
||||
import cn.hangtag.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.hangtag.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.hangtag.module.oms.dal.dataobject.salecontractentry.SaleContractEntryDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* OMS销售合约分录 Mapper
|
||||
*
|
||||
* @author WWB
|
||||
*/
|
||||
@Mapper
|
||||
public interface SaleContractEntryMapper extends BaseMapperX<SaleContractEntryDO> {
|
||||
|
||||
default List<SaleContractEntryDO> selectListByParentId(Long parentId) {
|
||||
return selectList(SaleContractEntryDO::getParentId, parentId);
|
||||
}
|
||||
|
||||
default int deleteByParentId(Long parentId) {
|
||||
return delete(SaleContractEntryDO::getParentId, parentId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package cn.hangtag.module.oms.dal.mysql.salecontract;
|
||||
|
||||
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.salecontract.SaleContractDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import cn.hangtag.module.oms.controller.admin.salecontract.vo.*;
|
||||
|
||||
/**
|
||||
* OMS销售合约 Mapper
|
||||
*
|
||||
* @author wwb
|
||||
*/
|
||||
@Mapper
|
||||
public interface SaleContractMapper extends BaseMapperX<SaleContractDO> {
|
||||
|
||||
default PageResult<SaleContractDO> selectPage(SaleContractPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<SaleContractDO>()
|
||||
.eqIfPresent(SaleContractDO::getBillno, reqVO.getBillno())
|
||||
.likeIfPresent(SaleContractDO::getCustomerName, reqVO.getCustomerName())
|
||||
.eqIfPresent(SaleContractDO::getCustomerBuyNo, reqVO.getCustomerBuyNo())
|
||||
.betweenIfPresent(SaleContractDO::getBizdate, reqVO.getBizdate())
|
||||
.eqIfPresent(SaleContractDO::getPartyA, reqVO.getPartyA())
|
||||
.eqIfPresent(SaleContractDO::getPartyB, reqVO.getPartyB())
|
||||
.eqIfPresent(SaleContractDO::getHead, reqVO.getHead())
|
||||
.eqIfPresent(SaleContractDO::getClerk, reqVO.getClerk())
|
||||
.eqIfPresent(SaleContractDO::getPhone, reqVO.getPhone())
|
||||
.eqIfPresent(SaleContractDO::getFax, reqVO.getFax())
|
||||
.eqIfPresent(SaleContractDO::getAmount, reqVO.getAmount())
|
||||
.eqIfPresent(SaleContractDO::getAddress, reqVO.getAddress())
|
||||
.betweenIfPresent(SaleContractDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(SaleContractDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,10 +4,14 @@ import cn.hangtag.framework.common.pojo.PageResult;
|
|||
import cn.hangtag.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.hangtag.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderPageReqVO;
|
||||
import cn.hangtag.module.oms.controller.admin.trade.vo.TradeOrderSummaryRespVO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
|
||||
import cn.hangtag.module.oms.enums.saleorder.SaleOrderStatusEnum;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 销售订单 Mapper
|
||||
|
|
@ -33,12 +37,10 @@ public interface SaleOrderMapper extends BaseMapperX<SaleOrderDO> {
|
|||
appendTabQuery(tabType, queryWrapper);
|
||||
|
||||
queryWrapper.orderByDesc(SaleOrderDO::getCreateTime);
|
||||
return selectPage(reqVO,queryWrapper );
|
||||
return selectPage(reqVO, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 添加后台 Tab 选项的查询条件
|
||||
*
|
||||
|
|
@ -60,4 +62,10 @@ public interface SaleOrderMapper extends BaseMapperX<SaleOrderDO> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
TradeOrderSummaryRespVO selectOrderQtySummaryByOrderStatusAndCreateTimeBetween(@Param("orderStatus") String orderStatus,
|
||||
@Param("beginTime") LocalDateTime beginTime,
|
||||
@Param("endTime") LocalDateTime endTime);
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package cn.hangtag.module.oms.service.salecontract;
|
||||
|
||||
import java.util.*;
|
||||
import javax.validation.*;
|
||||
import cn.hangtag.module.oms.controller.admin.salecontract.vo.*;
|
||||
import cn.hangtag.module.oms.dal.dataobject.salecontract.SaleContractDO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.salecontractentry.SaleContractEntryDO;
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
import cn.hangtag.framework.common.pojo.PageParam;
|
||||
|
||||
/**
|
||||
* OMS销售合约 Service 接口
|
||||
*
|
||||
* @author wwb
|
||||
*/
|
||||
public interface SaleContractService {
|
||||
|
||||
/**
|
||||
* 创建OMS销售合约
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createSaleContract(@Valid SaleContractSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新OMS销售合约
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateSaleContract(@Valid SaleContractSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除OMS销售合约
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deleteSaleContract(Long id);
|
||||
|
||||
/**
|
||||
* 获得OMS销售合约
|
||||
*
|
||||
* @param id 编号
|
||||
* @return OMS销售合约
|
||||
*/
|
||||
SaleContractDO getSaleContract(Long id);
|
||||
|
||||
/**
|
||||
* 获得OMS销售合约分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return OMS销售合约分页
|
||||
*/
|
||||
PageResult<SaleContractDO> getSaleContractPage(SaleContractPageReqVO pageReqVO);
|
||||
|
||||
// ==================== 子表(OMS销售合约分录) ====================
|
||||
|
||||
/**
|
||||
* 获得OMS销售合约分录列表
|
||||
*
|
||||
* @param parentId 主表ID
|
||||
* @return OMS销售合约分录列表
|
||||
*/
|
||||
List<SaleContractEntryDO> getSaleContractEntryListByParentId(Long parentId);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package cn.hangtag.module.oms.service.salecontract;
|
||||
|
||||
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.salecontract.vo.*;
|
||||
import cn.hangtag.module.oms.dal.dataobject.salecontract.SaleContractDO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.salecontractentry.SaleContractEntryDO;
|
||||
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.salecontract.SaleContractMapper;
|
||||
import cn.hangtag.module.oms.dal.mysql.salecontractentry.SaleContractEntryMapper;
|
||||
|
||||
import static cn.hangtag.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.hangtag.module.oms.enums.ErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* OMS销售合约 Service 实现类
|
||||
*
|
||||
* @author wwb
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class SaleContractServiceImpl implements SaleContractService {
|
||||
|
||||
@Resource
|
||||
private SaleContractMapper saleContractMapper;
|
||||
@Resource
|
||||
private SaleContractEntryMapper saleContractEntryMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createSaleContract(SaleContractSaveReqVO createReqVO) {
|
||||
// 插入
|
||||
SaleContractDO saleContract = BeanUtils.toBean(createReqVO, SaleContractDO.class);
|
||||
saleContractMapper.insert(saleContract);
|
||||
|
||||
// 插入子表
|
||||
createSaleContractEntryList(saleContract.getId(), createReqVO.getSaleContractEntrys());
|
||||
// 返回
|
||||
return saleContract.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateSaleContract(SaleContractSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateSaleContractExists(updateReqVO.getId());
|
||||
// 更新
|
||||
SaleContractDO updateObj = BeanUtils.toBean(updateReqVO, SaleContractDO.class);
|
||||
saleContractMapper.updateById(updateObj);
|
||||
|
||||
// 更新子表
|
||||
updateSaleContractEntryList(updateReqVO.getId(), updateReqVO.getSaleContractEntrys());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteSaleContract(Long id) {
|
||||
// 校验存在
|
||||
validateSaleContractExists(id);
|
||||
// 删除
|
||||
saleContractMapper.deleteById(id);
|
||||
|
||||
// 删除子表
|
||||
deleteSaleContractEntryByParentId(id);
|
||||
}
|
||||
|
||||
private void validateSaleContractExists(Long id) {
|
||||
if (saleContractMapper.selectById(id) == null) {
|
||||
throw exception(SALE_CONTRACT_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaleContractDO getSaleContract(Long id) {
|
||||
return saleContractMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<SaleContractDO> getSaleContractPage(SaleContractPageReqVO pageReqVO) {
|
||||
return saleContractMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
// ==================== 子表(OMS销售合约分录) ====================
|
||||
|
||||
@Override
|
||||
public List<SaleContractEntryDO> getSaleContractEntryListByParentId(Long parentId) {
|
||||
return saleContractEntryMapper.selectListByParentId(parentId);
|
||||
}
|
||||
|
||||
private void createSaleContractEntryList(Long parentId, List<SaleContractEntryDO> list) {
|
||||
list.forEach(o -> o.setParentId(parentId));
|
||||
saleContractEntryMapper.insertBatch(list);
|
||||
}
|
||||
|
||||
private void updateSaleContractEntryList(Long parentId, List<SaleContractEntryDO> list) {
|
||||
deleteSaleContractEntryByParentId(parentId);
|
||||
list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新
|
||||
createSaleContractEntryList(parentId, list);
|
||||
}
|
||||
|
||||
private void deleteSaleContractEntryByParentId(Long parentId) {
|
||||
saleContractEntryMapper.deleteByParentId(parentId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,7 +4,10 @@ import java.io.IOException;
|
|||
import java.util.*;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.*;
|
||||
|
||||
import cn.hangtag.module.oms.controller.admin.common.vo.DataComparisonRespVO;
|
||||
import cn.hangtag.module.oms.controller.admin.saleorder.vo.*;
|
||||
import cn.hangtag.module.oms.controller.admin.trade.vo.TradeOrderSummaryRespVO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.saleorderentry.SaleOrderEntryDO;
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
|
|
@ -74,4 +77,11 @@ public interface SaleOrderService {
|
|||
String generateHtmlContent();
|
||||
|
||||
void generatePdf(HttpServletResponse response, String htmlContent) throws IOException;
|
||||
|
||||
/**
|
||||
* 交易订单销售额对照
|
||||
*
|
||||
* @return 销售额对照
|
||||
*/
|
||||
DataComparisonRespVO<TradeOrderSummaryRespVO> getOrderComparison();
|
||||
}
|
||||
|
|
@ -2,35 +2,41 @@ package cn.hangtag.module.oms.service.saleorder;
|
|||
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
import cn.hangtag.framework.common.util.object.BeanUtils;
|
||||
import cn.hangtag.module.oms.common.utils.WKHtmlToPdfUtil;
|
||||
import cn.hangtag.module.oms.controller.admin.common.vo.DataComparisonRespVO;
|
||||
import cn.hangtag.module.oms.controller.admin.produceorder.vo.ProduceOrderSaveReqVO;
|
||||
import cn.hangtag.module.oms.common.utils.NumberChineseFormatterUtils;
|
||||
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderPageReqVO;
|
||||
import cn.hangtag.module.oms.controller.admin.saleorder.vo.SaleOrderSaveReqVO;
|
||||
import cn.hangtag.module.oms.controller.admin.trade.vo.TradeOrderSummaryRespVO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.saleorder.SaleOrderDO;
|
||||
import cn.hangtag.module.oms.dal.dataobject.saleorderentry.SaleOrderEntryDO;
|
||||
import cn.hangtag.module.oms.dal.mysql.saleorder.SaleOrderMapper;
|
||||
import cn.hangtag.module.oms.dal.mysql.saleorderentry.SaleOrderEntryMapper;
|
||||
import cn.hangtag.module.oms.enums.saleorder.SaleOrderStatusEnum;
|
||||
import cn.hangtag.module.oms.service.produceorder.ProduceOrderService;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.lowagie.text.pdf.PdfWriter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.context.Context;
|
||||
import org.xhtmlrenderer.pdf.ITextFontResolver;
|
||||
import org.xhtmlrenderer.pdf.ITextRenderer;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -166,12 +172,38 @@ public class SaleOrderServiceImpl implements SaleOrderService {
|
|||
@Override
|
||||
public String generateHtmlContent() {
|
||||
Context context = new Context();
|
||||
context.setVariable("title", "PDF文档标题");
|
||||
context.setVariable("content",
|
||||
"这是PDF文档的主要内容:"
|
||||
+ "“成功并不是终点,失败也不是终结,最重要的是继续前行的勇气。在人生的旅途中,我们会遇到许多挑战与挫折,这些都是成长的必经之路。每一次跌倒都是一次学习的机会,每一次失败都为成功铺设了基础。只要我们保持信念,不断努力,最终会到达梦想的彼岸。无论前方的路有多么坎坷,只要心怀希望,我们就有无限的可能性去改变自己的命运,实现心中的理想。”"
|
||||
+ "由Thymeleaf模板引擎渲染。"
|
||||
+ "");
|
||||
context.setVariable("title", "销售合约");
|
||||
context.setVariable("partyACompany", "甲方(买方)公司");
|
||||
context.setVariable("contractNo", "合约编号XXXXXXX");
|
||||
context.setVariable("partyAAddress", "甲方地址XXXXXXXXXXXX");
|
||||
context.setVariable("bizDate", DateUtil.date().toDateStr());
|
||||
context.setVariable("customerNo", "托尼");
|
||||
context.setVariable("headUserName", "美特斯");
|
||||
context.setVariable("partyBCompany", "乙方(卖方)公司");
|
||||
context.setVariable("phone", "17198644317");
|
||||
context.setVariable("partyBAddress", "乙方地址XXXXXXXXXXX");
|
||||
context.setVariable("fax", "CCCCCCCCCCCCC");
|
||||
context.setVariable("clerk", "李四");
|
||||
context.setVariable("saleOrderNo", "订单编号XXXXXXXXXX");
|
||||
context.setVariable("totalAmount", "1891.98");
|
||||
|
||||
|
||||
context.setVariable("zhTotalAmount", "合共人民币"+NumberChineseFormatterUtils.convertToChinese(new BigDecimal("1891.98"))+"整");
|
||||
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
context.setVariable("item"+i, i+"");
|
||||
context.setVariable("explain"+i, "1891.98");
|
||||
context.setVariable("qty"+i, "1891.98");
|
||||
context.setVariable("price"+i, "1891.98");
|
||||
context.setVariable("discount"+i, "1891.98");
|
||||
context.setVariable("amount"+i, "1891.98");
|
||||
context.setVariable("deliverydate"+i, "1891.98");
|
||||
context.setVariable("explain"+i+"1", "1891.98");
|
||||
context.setVariable("explain"+i+"2", "1891.98");
|
||||
context.setVariable("explain"+i+"3", "1891.98");
|
||||
}
|
||||
|
||||
return templateEngine.process("pdf_template", context);
|
||||
}
|
||||
|
||||
|
|
@ -180,25 +212,28 @@ public class SaleOrderServiceImpl implements SaleOrderService {
|
|||
// 设置响应类型
|
||||
response.setContentType("application/pdf");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=generated.pdf");
|
||||
String fileName = StrUtil.format("C:\\Users\\Admin\\Desktop\\1111111\\test\\test_{}",
|
||||
new Date().getTime());
|
||||
|
||||
// 创建ITextRenderer实例
|
||||
ITextRenderer renderer = new ITextRenderer();
|
||||
String templatePath = fileName + ".html";
|
||||
String pdfPath = fileName + ".pdf";
|
||||
FileUtil.writeString(htmlContent,templatePath, "UTF-8");
|
||||
WKHtmlToPdfUtil.convert(templatePath, pdfPath);
|
||||
File file = FileUtil.file(pdfPath);
|
||||
|
||||
// 设置字体路径,使用 classpath 加载字体
|
||||
ITextFontResolver fontResolver = renderer.getFontResolver();
|
||||
ClassPathResource fontResource = new ClassPathResource("fonts/SimSun.ttf");
|
||||
System.out.println(fontResource.getFile().getAbsolutePath());
|
||||
fontResolver.addFont(fontResource.getFile().getAbsolutePath(), "Identity-H", true);
|
||||
|
||||
renderer.setDocumentFromString(htmlContent);
|
||||
renderer.layout();
|
||||
|
||||
// 输出PDF到响应输出流
|
||||
try (OutputStream outputStream = response.getOutputStream()) {
|
||||
renderer.createPDF(outputStream);
|
||||
outputStream.flush();
|
||||
// 将文件内容写入响应流
|
||||
try (InputStream inputStream = new FileInputStream(file)) {
|
||||
IoUtil.copy(inputStream, response.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
// 异常处理
|
||||
log.info("导出电子病历写入流失败,{}", e.getMessage());
|
||||
}
|
||||
|
||||
// 导出完删除
|
||||
//FileUtil.del(file);
|
||||
//FileUtil.del(templatePath);
|
||||
/*
|
||||
// 设置输出PDF文件
|
||||
OutputStream os = new FileOutputStream("index.pdf");
|
||||
|
|
@ -209,6 +244,20 @@ public class SaleOrderServiceImpl implements SaleOrderService {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataComparisonRespVO<TradeOrderSummaryRespVO> getOrderComparison() {
|
||||
return new DataComparisonRespVO<TradeOrderSummaryRespVO>()
|
||||
.setValue(getOrderQtySummary(LocalDateTime.now()))
|
||||
.setReference(getOrderQtySummary(LocalDateTime.now().minusDays(1)));
|
||||
}
|
||||
|
||||
private TradeOrderSummaryRespVO getOrderQtySummary(LocalDateTime date) {
|
||||
LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(date);
|
||||
LocalDateTime endTime = LocalDateTimeUtil.endOfDay(date);
|
||||
return saleOrderMapper.selectOrderQtySummaryByOrderStatusAndCreateTimeBetween(
|
||||
null, beginTime, endTime);
|
||||
}
|
||||
|
||||
|
||||
private void createSaleOrderEntryList(Long parentId, List<SaleOrderEntryDO> list) {
|
||||
list.forEach(o -> o.setParentId(parentId));
|
||||
|
|
|
|||
|
|
@ -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.salecontract.SaleContractMapper">
|
||||
|
||||
<!--
|
||||
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
|
||||
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
|
||||
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
|
||||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
||||
-->
|
||||
|
||||
</mapper>
|
||||
|
|
@ -2,11 +2,20 @@
|
|||
<!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.saleorder.SaleOrderMapper">
|
||||
|
||||
<!--
|
||||
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
|
||||
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
|
||||
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
|
||||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
||||
-->
|
||||
|
||||
<select id="selectOrderQtySummaryByOrderStatusAndCreateTimeBetween"
|
||||
resultType="cn.hangtag.module.oms.controller.admin.trade.vo.TradeOrderSummaryRespVO">
|
||||
|
||||
SELECT IFNULL(SUM(0), 0) AS orderPayPrice,
|
||||
COUNT(1) AS orderPayCount
|
||||
FROM oms_saleorder
|
||||
WHERE deleted = FALSE
|
||||
<if test="orderStatus != null">
|
||||
AND order_status = #{orderStatus}
|
||||
</if>
|
||||
AND create_time BETWEEN #{beginTime} AND #{endTime}
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
</mapper>
|
||||
|
|
@ -1,37 +1,300 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title th:text="${title}">PDF文档</title>
|
||||
<meta charset="UTF-8" />
|
||||
<title th:text="${title}">销售合约</title>
|
||||
<!-- 引入 Bootstrap -->
|
||||
<style>
|
||||
@page {
|
||||
size: a4;
|
||||
}
|
||||
|
||||
/* 内联CSS样式,确保在PDF中正确渲染 */
|
||||
body {
|
||||
font-family: "SimSun", serif;
|
||||
padding: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
p {
|
||||
font-size: 16px;
|
||||
color: #555;
|
||||
|
||||
.row {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
|
||||
.font-title {
|
||||
font-size: 11.5px;
|
||||
}
|
||||
|
||||
|
||||
.border-bottom {
|
||||
border-bottom: 2px solid #000;
|
||||
/* 修改为您想要的颜色和宽度 */
|
||||
}
|
||||
|
||||
.border-top {
|
||||
border-top: 2px solid #000;
|
||||
/* 修改为您想要的颜色和宽度 */
|
||||
}
|
||||
|
||||
.fixed-bottom {
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
.itemtitle {
|
||||
flex: 0 0 55%;
|
||||
}
|
||||
|
||||
.table>:not(caption)>*>* {
|
||||
padding: 0px 0px 0px 0px;
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 th:text="${title}">PDF文档标题</h1>
|
||||
<p th:text="${content}">这是PDF文档的内容部分,由Thymeleaf模板引擎渲染。</p>
|
||||
<div class="footer">
|
||||
<p>© 2024 路条编程 - 保留所有权利。</p>
|
||||
<h1 th:text="${title}">销售合约</h1>
|
||||
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td style="width: 10%;">甲方(买方)</td>
|
||||
<td style="width: 40%;" th:text="': '+${partyACompany}">:</td>
|
||||
<td style="width: 10%;">合约编号</td>
|
||||
<td style="width: 40%;" th:text="': '+${contractNo}">:</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="width: 10%;"></td>
|
||||
<td style="width: 40%;" th:text="' '+${partyAAddress}"></td>
|
||||
<td style="width: 10%;">日期</td>
|
||||
<td style="width: 40%;" th:text="': '+${bizDate}">:</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="width: 10%;"></td>
|
||||
<td style="width: 40%;"></td>
|
||||
<td style="width: 10%;">客户</td>
|
||||
<td style="width: 40%;" th:text="': '+${customerNo}">:</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="width: 10%;">负责人</td>
|
||||
<td style="width: 90%;" colspan="3" th:text="': '+${headUserName}">:</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="width: 10%;">乙方(卖方)</td>
|
||||
<td style="width: 40%;" th:text="': '+${partyBCompany}">:</td>
|
||||
<td style="width: 10%;">电话</td>
|
||||
<td style="width: 40%;" th:text="': '+${phone}">:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 10%;"></td>
|
||||
<td style="width: 40%;" th:text="' '+${partyBAddress}"></td>
|
||||
<td style="width: 10%;">传真</td>
|
||||
<td style="width: 40%;" th:text="': '+${fax}">:</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td style="width: 10%;">职员</td>
|
||||
<td style="width: 90%;" colspan="3" th:text="': '+${clerk}">:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 15%;">客户采购编号</td>
|
||||
<td style="width: 85%;" colspan="3" th:text="': '+${saleOrderNo}">:</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="">
|
||||
<table class="table" style="width: 100%; border-collapse: collapse;">
|
||||
<thead style="border-bottom:1px solid black;">
|
||||
<tr>
|
||||
<th style="width: 4%;">项目</th>
|
||||
<th style="width: 25%;">产品说明</th>
|
||||
<th style="width: 5%;text-align: center;">数量</th>
|
||||
<th style="width: 8%;text-align: center;">单位订价RMB</th>
|
||||
<th style="width: 5%;text-align: center;">折扣%</th>
|
||||
<th style="width: 7%;text-align: center;">金额RMB</th>
|
||||
<th style="width: 7%;text-align: center;">交货日期</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td th:text="${item1}">1</td>
|
||||
<td th:text="${explain1}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${qty1}">12344</td>
|
||||
<td style="text-align: center; " th:text="${price1}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${discount1}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${amount1}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${deliverydate1}">2024-01-12</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain11}">跨越多列的标题</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain12}">跨越多列的标题</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain13}">跨越多列的标题</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td th:text="${item2}">1</td>
|
||||
<td th:text="${explain2}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${qty2}">12344</td>
|
||||
<td style="text-align: center; " th:text="${price2}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${discount2}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${amount2}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${deliverydate2}">2024-01-12</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain21}">跨越多列的标题</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain22}">跨越多列的标题</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain23}">跨越多列的标题</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td th:text="${item3}">1</td>
|
||||
<td th:text="${explain3}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${qty3}">12344</td>
|
||||
<td style="text-align: center; " th:text="${price3}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${discount3}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${amount3}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${deliverydate3}">2024-01-12</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain31}">跨越多列的标题</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain32}">跨越多列的标题</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain33}">跨越多列的标题</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
<td th:text="${item4}">1</td>
|
||||
<td th:text="${explain4}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${qty4}">12344</td>
|
||||
<td style="text-align: center; " th:text="${price4}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${discount4}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${amount4}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${deliverydate4}">2024-01-12</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain41}">跨越多列的标题</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain42}">跨越多列的标题</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain43}">跨越多列的标题</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td th:text="${item5}">1</td>
|
||||
<td th:text="${explain5}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${qty5}">12344</td>
|
||||
<td style="text-align: center; " th:text="${price5}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${discount5}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${amount5}">Doe</td>
|
||||
<td style="text-align: center; " th:text="${deliverydate5}">2024-01-12</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain51}">跨越多列的标题</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain52}">跨越多列的标题</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td scope="row" colspan="6" th:text="${explain53}">跨越多列的标题</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-2 p-3 font-title" style="text-align: right;width: 100%;">
|
||||
<div style="padding-right: 80px;">
|
||||
<span style="font-weight: bold;font-size: 16px;">净金额:</span>
|
||||
<span style="border-bottom: 1px solid #dddddd;padding-bottom: 12px;">
|
||||
<span
|
||||
style="font-weight: bold;font-size: 16px; padding: 8px 0px 8px 0px;border-top: 1px solid #dddddd;border-bottom: 2px solid #dddddd;">
|
||||
|
||||
<span style="margin-right: 80px;">RMB</span>
|
||||
<span th:text="${totalAmount}">XXXXXXXxXX</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-12 p-2 font-title" style="margin-left: 80px;font-weight: bold;font-size: 16px;"
|
||||
th:text="${zhTotalAmount}">
|
||||
合共人民币XXXXXXXXXXXXXXXXXXX</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="fixed-bottom">
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td style="width: 13%;font-weight: bold;">甲方(买方)</td>
|
||||
<td style="width: 35%;" th:text="':'+${partyACompany}">:</td>
|
||||
<td style="width: 13%;font-weight: bold;">乙方(卖方)</td>
|
||||
<td style="width: 35%;" th:text="':'+${partyBCompany}">:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 13%;font-weight: bold;">代表</td>
|
||||
<td style="width: 35%;">:</td>
|
||||
<td style="width: 13%;font-weight: bold;">代表</td>
|
||||
<td style="width: 35%;">:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width: 13%;font-weight: bold;">日期</td>
|
||||
<td style="width: 35%;">:</td>
|
||||
<td style="width: 13%;font-weight: bold;">日期</td>
|
||||
<td style="width: 35%;">:</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="row">
|
||||
<p class="col-12 p-1 border " style="font-weight: bold;">请在合约上签名确认并传真送回,谢谢。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
package cn.hangtag.module.oms.service.salecontract;
|
||||
|
||||
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.salecontract.vo.*;
|
||||
import cn.hangtag.module.oms.dal.dataobject.salecontract.SaleContractDO;
|
||||
import cn.hangtag.module.oms.dal.mysql.salecontract.SaleContractMapper;
|
||||
import cn.hangtag.framework.common.pojo.PageResult;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
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 SaleContractServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author wwb
|
||||
*/
|
||||
@Import(SaleContractServiceImpl.class)
|
||||
public class SaleContractServiceImplTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private SaleContractServiceImpl saleContractService;
|
||||
|
||||
@Resource
|
||||
private SaleContractMapper saleContractMapper;
|
||||
|
||||
@Test
|
||||
public void testCreateSaleContract_success() {
|
||||
// 准备参数
|
||||
SaleContractSaveReqVO createReqVO = randomPojo(SaleContractSaveReqVO.class).setId(null);
|
||||
|
||||
// 调用
|
||||
Long saleContractId = saleContractService.createSaleContract(createReqVO);
|
||||
// 断言
|
||||
assertNotNull(saleContractId);
|
||||
// 校验记录的属性是否正确
|
||||
SaleContractDO saleContract = saleContractMapper.selectById(saleContractId);
|
||||
assertPojoEquals(createReqVO, saleContract, "id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSaleContract_success() {
|
||||
// mock 数据
|
||||
SaleContractDO dbSaleContract = randomPojo(SaleContractDO.class);
|
||||
saleContractMapper.insert(dbSaleContract);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
SaleContractSaveReqVO updateReqVO = randomPojo(SaleContractSaveReqVO.class, o -> {
|
||||
o.setId(dbSaleContract.getId()); // 设置更新的 ID
|
||||
});
|
||||
|
||||
// 调用
|
||||
saleContractService.updateSaleContract(updateReqVO);
|
||||
// 校验是否更新正确
|
||||
SaleContractDO saleContract = saleContractMapper.selectById(updateReqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(updateReqVO, saleContract);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSaleContract_notExists() {
|
||||
// 准备参数
|
||||
SaleContractSaveReqVO updateReqVO = randomPojo(SaleContractSaveReqVO.class);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> saleContractService.updateSaleContract(updateReqVO), SALE_CONTRACT_NOT_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteSaleContract_success() {
|
||||
// mock 数据
|
||||
SaleContractDO dbSaleContract = randomPojo(SaleContractDO.class);
|
||||
saleContractMapper.insert(dbSaleContract);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbSaleContract.getId();
|
||||
|
||||
// 调用
|
||||
saleContractService.deleteSaleContract(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(saleContractMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteSaleContract_notExists() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
|
||||
public void testGetSaleContractPage() {
|
||||
// mock 数据
|
||||
SaleContractDO dbSaleContract = randomPojo(SaleContractDO.class, o -> { // 等会查询到
|
||||
o.setBillno(null);
|
||||
o.setCustomerName(null);
|
||||
o.setCustomerBuyNo(null);
|
||||
o.setBizdate(null);
|
||||
o.setPartyA(null);
|
||||
o.setPartyB(null);
|
||||
o.setHead(null);
|
||||
o.setClerk(null);
|
||||
o.setPhone(null);
|
||||
o.setFax(null);
|
||||
o.setAmount(null);
|
||||
o.setAddress(null);
|
||||
o.setCreateTime(null);
|
||||
});
|
||||
saleContractMapper.insert(dbSaleContract);
|
||||
// 测试 billno 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setBillno(null)));
|
||||
// 测试 customerName 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setCustomerName(null)));
|
||||
// 测试 customerBuyNo 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setCustomerBuyNo(null)));
|
||||
// 测试 bizdate 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setBizdate(null)));
|
||||
// 测试 partyA 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setPartyA(null)));
|
||||
// 测试 partyB 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setPartyB(null)));
|
||||
// 测试 head 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setHead(null)));
|
||||
// 测试 clerk 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setClerk(null)));
|
||||
// 测试 phone 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setPhone(null)));
|
||||
// 测试 fax 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setFax(null)));
|
||||
// 测试 amount 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setAmount(null)));
|
||||
// 测试 address 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setAddress(null)));
|
||||
// 测试 createTime 不匹配
|
||||
saleContractMapper.insert(cloneIgnoreId(dbSaleContract, o -> o.setCreateTime(null)));
|
||||
// 准备参数
|
||||
SaleContractPageReqVO reqVO = new SaleContractPageReqVO();
|
||||
reqVO.setBillno(null);
|
||||
reqVO.setCustomerName(null);
|
||||
reqVO.setCustomerBuyNo(null);
|
||||
reqVO.setBizdate(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
|
||||
reqVO.setPartyA(null);
|
||||
reqVO.setPartyB(null);
|
||||
reqVO.setHead(null);
|
||||
reqVO.setClerk(null);
|
||||
reqVO.setPhone(null);
|
||||
reqVO.setFax(null);
|
||||
reqVO.setAmount(null);
|
||||
reqVO.setAddress(null);
|
||||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
|
||||
|
||||
// 调用
|
||||
PageResult<SaleContractDO> pageResult = saleContractService.getSaleContractPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbSaleContract, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -326,21 +326,7 @@ public class AdminUserServiceImpl implements AdminUserService {
|
|||
private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email,
|
||||
Long deptId, Set<Long> postIds) {
|
||||
// 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确
|
||||
return DataPermissionUtils.executeIgnore(() -> {
|
||||
// 校验用户存在
|
||||
AdminUserDO user = validateUserExists(id);
|
||||
// 校验用户名唯一
|
||||
validateUsernameUnique(id, username);
|
||||
// 校验手机号唯一
|
||||
validateMobileUnique(id, mobile);
|
||||
// 校验邮箱唯一
|
||||
validateEmailUnique(id, email);
|
||||
// 校验部门处于开启状态
|
||||
deptService.validateDeptList(CollectionUtils.singleton(deptId));
|
||||
// 校验岗位处于开启状态
|
||||
postService.validatePostList(postIds);
|
||||
return user;
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import javax.annotation.Generated;
|
|||
|
||||
@Generated(
|
||||
value = "org.mapstruct.ap.MappingProcessor",
|
||||
date = "2024-08-13T22:38:55+0800",
|
||||
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_251 (Oracle Corporation)"
|
||||
date = "2024-09-15T11:57:57+0800",
|
||||
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_401 (Oracle Corporation)"
|
||||
)
|
||||
public class OAuth2OpenConvertImpl implements OAuth2OpenConvert {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import javax.annotation.Generated;
|
|||
|
||||
@Generated(
|
||||
value = "org.mapstruct.ap.MappingProcessor",
|
||||
date = "2024-08-13T22:38:55+0800",
|
||||
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_251 (Oracle Corporation)"
|
||||
date = "2024-09-15T11:57:57+0800",
|
||||
comments = "version: 1.5.5.Final, compiler: javac, environment: Java 1.8.0_401 (Oracle Corporation)"
|
||||
)
|
||||
public class SocialUserConvertImpl implements SocialUserConvert {
|
||||
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ spring:
|
|||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?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: Admin11039
|
||||
password: qygo5gYNhivG
|
||||
# slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
|
||||
# lazy: true # 开启懒加载,保证启动速度
|
||||
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import request from '@/config/axios'
|
|||
export interface CustomerVO {
|
||||
name: string // 名称
|
||||
company: string // 公司
|
||||
companyAddress: string // 公司
|
||||
email: string // 邮箱
|
||||
contacts: string // 联系人
|
||||
gdperson: string // 跟单员
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
// OMS销售合约 VO
|
||||
export interface SaleContractVO {
|
||||
id: number // ID
|
||||
billno: string // 合约编号
|
||||
customerId: string // 客户id
|
||||
customerName: string // 客户名称
|
||||
customerBuyNo: string // 客户采购编号
|
||||
bizdate: Date // 业务日期
|
||||
partyA: string // 甲方
|
||||
partyB: string // 乙方
|
||||
head: string // 负责人
|
||||
clerk: string // 职员
|
||||
phone: string // 手机
|
||||
fax: string // 传真
|
||||
amount: number // 金额
|
||||
address: string // 地址
|
||||
}
|
||||
|
||||
// OMS销售合约 API
|
||||
export const SaleContractApi = {
|
||||
// 查询OMS销售合约分页
|
||||
getSaleContractPage: async (params: any) => {
|
||||
return await request.get({ url: `/oms/sale-contract/page`, params })
|
||||
},
|
||||
|
||||
// 查询OMS销售合约详情
|
||||
getSaleContract: async (id: number) => {
|
||||
return await request.get({ url: `/oms/sale-contract/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增OMS销售合约
|
||||
createSaleContract: async (data: SaleContractVO) => {
|
||||
return await request.post({ url: `/oms/sale-contract/create`, data })
|
||||
},
|
||||
|
||||
// 修改OMS销售合约
|
||||
updateSaleContract: async (data: SaleContractVO) => {
|
||||
return await request.put({ url: `/oms/sale-contract/update`, data })
|
||||
},
|
||||
|
||||
// 删除OMS销售合约
|
||||
deleteSaleContract: async (id: number) => {
|
||||
return await request.delete({ url: `/oms/sale-contract/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出OMS销售合约 Excel
|
||||
exportSaleContract: async (params) => {
|
||||
return await request.download({ url: `/oms/sale-contract/export-excel`, params })
|
||||
},
|
||||
|
||||
// ==================== 子表(OMS销售合约分录) ====================
|
||||
|
||||
// 获得OMS销售合约分录列表
|
||||
getSaleContractEntryListByParentId: async (parentId) => {
|
||||
return await request.get({ url: `/oms/sale-contract/sale-contract-entry/list-by-parent-id?parentId=` + parentId })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/** 数据对照 Response VO */
|
||||
export interface DataComparisonRespVO<T> {
|
||||
value: T
|
||||
reference: T
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
import request from '@/config/axios'
|
||||
import dayjs from 'dayjs'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { DataComparisonRespVO } from '@/api/oms/statistics/common'
|
||||
|
||||
/** 交易统计 Response VO */
|
||||
export interface TradeSummaryRespVO {
|
||||
yesterdayOrderCount: number
|
||||
monthOrderCount: number
|
||||
yesterdayPayPrice: number
|
||||
monthPayPrice: number
|
||||
}
|
||||
|
||||
/** 交易状况 Request VO */
|
||||
export interface TradeTrendReqVO {
|
||||
times: [dayjs.ConfigType, dayjs.ConfigType]
|
||||
}
|
||||
|
||||
/** 交易状况统计 Response VO */
|
||||
export interface TradeTrendSummaryRespVO {
|
||||
time: string
|
||||
turnoverPrice: number
|
||||
orderPayPrice: number
|
||||
rechargePrice: number
|
||||
expensePrice: number
|
||||
walletPayPrice: number
|
||||
brokerageSettlementPrice: number
|
||||
afterSaleRefundPrice: number
|
||||
}
|
||||
|
||||
/** 交易订单数量 Response VO */
|
||||
export interface TradeOrderCountRespVO {
|
||||
/** 待发货 */
|
||||
undelivered?: number
|
||||
/** 待核销 */
|
||||
pickUp?: number
|
||||
/** 退款中 */
|
||||
afterSaleApply?: number
|
||||
/** 提现待审核 */
|
||||
auditingWithdraw?: number
|
||||
}
|
||||
|
||||
/** 交易订单统计 Response VO */
|
||||
export interface TradeOrderSummaryRespVO {
|
||||
/** 支付订单商品数 */
|
||||
orderPayCount?: number
|
||||
/** 总支付金额,单位:分 */
|
||||
orderPayPrice?: number
|
||||
}
|
||||
|
||||
/** 订单量趋势统计 Response VO */
|
||||
export interface TradeOrderTrendRespVO {
|
||||
/** 日期 */
|
||||
date: string
|
||||
/** 订单数量 */
|
||||
orderPayCount: number
|
||||
/** 订单支付金额 */
|
||||
orderPayPrice: number
|
||||
}
|
||||
|
||||
// 查询交易统计
|
||||
export const getTradeStatisticsSummary = () => {
|
||||
return request.get<DataComparisonRespVO<TradeSummaryRespVO>>({
|
||||
url: '/statistics/trade/summary'
|
||||
})
|
||||
}
|
||||
|
||||
// 获得交易状况统计
|
||||
export const getTradeStatisticsAnalyse = (params: TradeTrendReqVO) => {
|
||||
return request.get<DataComparisonRespVO<TradeTrendSummaryRespVO>>({
|
||||
url: '/statistics/trade/analyse',
|
||||
params: formatDateParam(params)
|
||||
})
|
||||
}
|
||||
|
||||
// 获得交易状况明细
|
||||
export const getTradeStatisticsList = (params: TradeTrendReqVO) => {
|
||||
return request.get<TradeTrendSummaryRespVO[]>({
|
||||
url: '/statistics/trade/list',
|
||||
params: formatDateParam(params)
|
||||
})
|
||||
}
|
||||
|
||||
// 导出交易状况明细
|
||||
export const exportTradeStatisticsExcel = (params: TradeTrendReqVO) => {
|
||||
return request.download({
|
||||
url: '/statistics/trade/export-excel',
|
||||
params: formatDateParam(params)
|
||||
})
|
||||
}
|
||||
|
||||
// 获得交易订单数量
|
||||
export const getOrderCount = async () => {
|
||||
return await request.get<TradeOrderCountRespVO>({ url: `/statistics/trade/order-count` })
|
||||
}
|
||||
|
||||
// 获得交易订单数量对照
|
||||
export const getOrderComparison = async () => {
|
||||
return await request.get<DataComparisonRespVO<TradeOrderSummaryRespVO>>({
|
||||
url: `/oms/statistics/trade/order-comparison`
|
||||
})
|
||||
}
|
||||
|
||||
// 获得订单量趋势统计
|
||||
export const getOrderCountTrendComparison = (
|
||||
type: number,
|
||||
beginTime: dayjs.ConfigType,
|
||||
endTime: dayjs.ConfigType
|
||||
) => {
|
||||
return request.get<DataComparisonRespVO<TradeOrderTrendRespVO>[]>({
|
||||
url: '/statistics/trade/order-count-trend',
|
||||
params: { type, beginTime: formatDate(beginTime), endTime: formatDate(endTime) }
|
||||
})
|
||||
}
|
||||
|
||||
/** 时间参数需要格式化, 确保接口能识别 */
|
||||
const formatDateParam = (params: TradeTrendReqVO) => {
|
||||
return { times: [formatDate(params.times[0]), formatDate(params.times[1])] } as TradeTrendReqVO
|
||||
}
|
||||
|
|
@ -1,243 +1,52 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-card shadow="never">
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<el-row :gutter="16" justify="space-between">
|
||||
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
<div class="text-20px">
|
||||
{{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
|
||||
</div>
|
||||
<div class="mt-10px text-14px text-gray-500">
|
||||
{{ t('workplace.toady') }},20℃ - 32℃!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<div class="flex flex-col">
|
||||
<!-- 数据对照 -->
|
||||
<el-row :gutter="16" class="row">
|
||||
<el-col :md="6" :sm="12" :xs="24" :loading="loading">
|
||||
<ComparisonCard
|
||||
tag="今日"
|
||||
title="订单量"
|
||||
:value="orderComparison?.value?.orderPayCount || 0"
|
||||
:reference="orderComparison?.reference?.orderPayCount || 0"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</el-row>
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { set } from 'lodash-es'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { formatTime } from '@/utils'
|
||||
import DraftDesign from '@/components/DraftDesign/index.vue'
|
||||
import * as TradeStatisticsApi from '@/api/oms/statistics/trade'
|
||||
import {TradeOrderSummaryRespVO} from '@/api/oms/statistics/trade'
|
||||
import {DataComparisonRespVO} from '@/api/oms/statistics/common'
|
||||
import ComparisonCard from './components/ComparisonCard.vue'
|
||||
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useWatermark } from '@/hooks/web/useWatermark'
|
||||
import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
|
||||
import { pieOptions, barOptions } from './echarts-data'
|
||||
/** 商城首页 */
|
||||
defineOptions({ name: 'MallHome' })
|
||||
|
||||
defineOptions({ name: 'Home' })
|
||||
const loading = ref(true) // 加载中
|
||||
const orderComparison = ref<DataComparisonRespVO<TradeOrderSummaryRespVO>>() // 交易对照数据
|
||||
|
||||
const { t } = useI18n()
|
||||
const userStore = useUserStore()
|
||||
const { setWatermark } = useWatermark()
|
||||
const loading = ref(true)
|
||||
const avatar = userStore.getUser.avatar
|
||||
const username = userStore.getUser.nickname
|
||||
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
|
||||
// 获取统计数
|
||||
let totalSate = reactive<WorkplaceTotal>({
|
||||
project: 0,
|
||||
access: 0,
|
||||
todo: 0
|
||||
})
|
||||
|
||||
const getCount = async () => {
|
||||
const data = {
|
||||
project: 40,
|
||||
access: 2340,
|
||||
todo: 10
|
||||
}
|
||||
totalSate = Object.assign(totalSate, data)
|
||||
/** 查询交易对照卡片数据 */
|
||||
const getOrderComparison = async () => {
|
||||
orderComparison.value = await TradeStatisticsApi.getOrderComparison()
|
||||
}
|
||||
|
||||
// 获取项目数
|
||||
let projects = reactive<Project[]>([])
|
||||
const getProject = async () => {
|
||||
const data = [
|
||||
{
|
||||
name: 'ruoyi-vue-pro',
|
||||
icon: 'akar-icons:github-fill',
|
||||
message: 'https://github.com/YunaiV/ruoyi-vue-pro',
|
||||
personal: 'Spring Boot 单体架构',
|
||||
time: new Date()
|
||||
},
|
||||
{
|
||||
name: 'yudao-ui-admin-vue3',
|
||||
icon: 'logos:vue',
|
||||
message: 'https://github.com/yudaocode/yudao-ui-admin-vue3',
|
||||
personal: 'Vue3 + element-plus',
|
||||
time: new Date()
|
||||
},
|
||||
{
|
||||
name: 'yudao-ui-admin-vben',
|
||||
icon: 'logos:vue',
|
||||
message: 'https://github.com/yudaocode/yudao-ui-admin-vben',
|
||||
personal: 'Vue3 + vben(antd)',
|
||||
time: new Date()
|
||||
},
|
||||
{
|
||||
name: 'yudao-cloud',
|
||||
icon: 'akar-icons:github',
|
||||
message: 'https://github.com/YunaiV/yudao-cloud',
|
||||
personal: 'Spring Cloud 微服务架构',
|
||||
time: new Date()
|
||||
},
|
||||
{
|
||||
name: 'yudao-ui-mall-uniapp',
|
||||
icon: 'logos:vue',
|
||||
message: 'https://github.com/yudaocode/yudao-ui-admin-uniapp',
|
||||
personal: 'Vue3 + uniapp',
|
||||
time: new Date()
|
||||
},
|
||||
{
|
||||
name: 'yudao-ui-admin-vue2',
|
||||
icon: 'logos:vue',
|
||||
message: 'https://github.com/yudaocode/yudao-ui-admin-vue2',
|
||||
personal: 'Vue2 + element-ui',
|
||||
time: new Date()
|
||||
}
|
||||
]
|
||||
projects = Object.assign(projects, data)
|
||||
}
|
||||
/** 查询会员用户数量对照卡片数据 */
|
||||
/*const getUserCountComparison = async () => {
|
||||
userComparison.value = await MemberStatisticsApi.getUserCountComparison()
|
||||
}*/
|
||||
|
||||
// 获取通知公告
|
||||
let notice = reactive<Notice[]>([])
|
||||
const getNotice = async () => {
|
||||
const data = [
|
||||
{
|
||||
title: '系统支持 JDK 8/17/21,Vue 2/3',
|
||||
type: '通知',
|
||||
keys: ['通知', '8', '17', '21', '2', '3'],
|
||||
date: new Date()
|
||||
},
|
||||
{
|
||||
title: '后端提供 Spring Boot 2.7/3.2 + Cloud 双架构',
|
||||
type: '公告',
|
||||
keys: ['公告', 'Boot', 'Cloud'],
|
||||
date: new Date()
|
||||
},
|
||||
{
|
||||
title: '全部开源,个人与企业可 100% 直接使用,无需授权',
|
||||
type: '通知',
|
||||
keys: ['通知', '无需授权'],
|
||||
date: new Date()
|
||||
},
|
||||
{
|
||||
title: '国内使用最广泛的快速开发平台,超 300+ 人贡献',
|
||||
type: '公告',
|
||||
keys: ['公告', '最广泛'],
|
||||
date: new Date()
|
||||
}
|
||||
]
|
||||
notice = Object.assign(notice, data)
|
||||
}
|
||||
|
||||
// 获取快捷入口
|
||||
let shortcut = reactive<Shortcut[]>([])
|
||||
|
||||
const getShortcut = async () => {
|
||||
const data = [
|
||||
{
|
||||
name: 'Github',
|
||||
icon: 'akar-icons:github-fill',
|
||||
url: 'github.io'
|
||||
},
|
||||
{
|
||||
name: 'Vue',
|
||||
icon: 'logos:vue',
|
||||
url: 'vuejs.org'
|
||||
},
|
||||
{
|
||||
name: 'Vite',
|
||||
icon: 'vscode-icons:file-type-vite',
|
||||
url: 'https://vitejs.dev/'
|
||||
},
|
||||
{
|
||||
name: 'Angular',
|
||||
icon: 'logos:angular-icon',
|
||||
url: 'github.io'
|
||||
},
|
||||
{
|
||||
name: 'React',
|
||||
icon: 'logos:react',
|
||||
url: 'github.io'
|
||||
},
|
||||
{
|
||||
name: 'Webpack',
|
||||
icon: 'logos:webpack',
|
||||
url: 'github.io'
|
||||
}
|
||||
]
|
||||
shortcut = Object.assign(shortcut, data)
|
||||
}
|
||||
|
||||
// 用户来源
|
||||
const getUserAccessSource = async () => {
|
||||
const data = [
|
||||
{ value: 335, name: 'analysis.directAccess' },
|
||||
{ value: 310, name: 'analysis.mailMarketing' },
|
||||
{ value: 234, name: 'analysis.allianceAdvertising' },
|
||||
{ value: 135, name: 'analysis.videoAdvertising' },
|
||||
{ value: 1548, name: 'analysis.searchEngines' }
|
||||
]
|
||||
set(
|
||||
pieOptionsData,
|
||||
'legend.data',
|
||||
data.map((v) => t(v.name))
|
||||
)
|
||||
pieOptionsData!.series![0].data = data.map((v) => {
|
||||
return {
|
||||
name: t(v.name),
|
||||
value: v.value
|
||||
}
|
||||
})
|
||||
}
|
||||
const barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption
|
||||
|
||||
// 周活跃量
|
||||
const getWeeklyUserActivity = async () => {
|
||||
const data = [
|
||||
{ value: 13253, name: 'analysis.monday' },
|
||||
{ value: 34235, name: 'analysis.tuesday' },
|
||||
{ value: 26321, name: 'analysis.wednesday' },
|
||||
{ value: 12340, name: 'analysis.thursday' },
|
||||
{ value: 24643, name: 'analysis.friday' },
|
||||
{ value: 1322, name: 'analysis.saturday' },
|
||||
{ value: 1324, name: 'analysis.sunday' }
|
||||
]
|
||||
set(
|
||||
barOptionsData,
|
||||
'xAxis.data',
|
||||
data.map((v) => t(v.name))
|
||||
)
|
||||
set(barOptionsData, 'series', [
|
||||
{
|
||||
name: t('analysis.activeQuantity'),
|
||||
data: data.map((v) => v.value),
|
||||
type: 'bar'
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
const getAllApi = async () => {
|
||||
await Promise.all([
|
||||
getCount(),
|
||||
getProject(),
|
||||
getNotice(),
|
||||
getShortcut(),
|
||||
getUserAccessSource(),
|
||||
getWeeklyUserActivity()
|
||||
])
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
loading.value = true
|
||||
await Promise.all([getOrderComparison()])
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
getAllApi()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.row {
|
||||
.el-col {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<div class="flex flex-col gap-2 bg-[var(--el-bg-color-overlay)] p-6">
|
||||
<div class="flex items-center justify-between text-gray-500">
|
||||
<span>{{ title }}</span>
|
||||
<el-tag>{{ tag }}</el-tag>
|
||||
</div>
|
||||
<div class="flex flex-row items-baseline justify-between">
|
||||
<CountTo :prefix="prefix" :end-val="value" :decimals="decimals" class="text-3xl" />
|
||||
<span :class="toNumber(percent) > 0 ? 'text-red-500' : 'text-green-500'">
|
||||
{{ Math.abs(toNumber(percent)) }}%
|
||||
<Icon :icon="toNumber(percent) > 0 ? 'ep:caret-top' : 'ep:caret-bottom'" class="!text-sm" />
|
||||
</span>
|
||||
</div>
|
||||
<el-divider class="mb-1! mt-2!" />
|
||||
<div class="flex flex-row items-center justify-between text-sm">
|
||||
<span class="text-gray-500">昨日数据</span>
|
||||
<span>{{ prefix || '' }}{{ reference }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { toNumber } from 'lodash-es'
|
||||
import { calculateRelativeRate } from '@/utils'
|
||||
|
||||
/** 交易对照卡片 */
|
||||
defineOptions({ name: 'ComparisonCard' })
|
||||
|
||||
const props = defineProps({
|
||||
title: propTypes.string.def('').isRequired,
|
||||
tag: propTypes.string.def(''),
|
||||
prefix: propTypes.string.def(''),
|
||||
value: propTypes.number.def(0).isRequired,
|
||||
reference: propTypes.number.def(0).isRequired,
|
||||
decimals: propTypes.number.def(0)
|
||||
})
|
||||
|
||||
// 计算环比
|
||||
const percent = computed(() =>
|
||||
calculateRelativeRate(props.value as number, props.reference as number)
|
||||
)
|
||||
</script>
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<CardTitle title="用户统计" />
|
||||
</template>
|
||||
<!-- 折线图 -->
|
||||
<Echart :height="300" :options="lineChartOptions" />
|
||||
</el-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import dayjs from 'dayjs'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import * as MemberStatisticsApi from '@/api/mall/statistics/member'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { CardTitle } from '@/components/Card'
|
||||
|
||||
/** 会员用户统计卡片 */
|
||||
defineOptions({ name: 'MemberStatisticsCard' })
|
||||
|
||||
const loading = ref(true) // 加载中
|
||||
/** 折线图配置 */
|
||||
const lineChartOptions = reactive<EChartsOption>({
|
||||
dataset: {
|
||||
dimensions: ['date', 'count'],
|
||||
source: []
|
||||
},
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
top: 80,
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
top: 50
|
||||
},
|
||||
series: [{ name: '注册量', type: 'line', smooth: true, areaStyle: {} }],
|
||||
toolbox: {
|
||||
feature: {
|
||||
// 数据区域缩放
|
||||
dataZoom: {
|
||||
yAxisIndex: false // Y轴不缩放
|
||||
},
|
||||
brush: {
|
||||
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
|
||||
},
|
||||
saveAsImage: { show: true, name: '会员统计' } // 保存为图片
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
},
|
||||
padding: [5, 10]
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
formatter: (date: string) => formatDate(date, 'MM-DD')
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}) as EChartsOption
|
||||
|
||||
const getMemberRegisterCountList = async () => {
|
||||
loading.value = true
|
||||
// 查询最近一月数据
|
||||
const beginTime = dayjs().subtract(30, 'd').startOf('d')
|
||||
const endTime = dayjs().endOf('d')
|
||||
const list = await MemberStatisticsApi.getMemberRegisterCountList(beginTime, endTime)
|
||||
// 更新 Echarts 数据
|
||||
if (lineChartOptions.dataset && lineChartOptions.dataset['source']) {
|
||||
lineChartOptions.dataset['source'] = list
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getMemberRegisterCountList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<CardTitle title="运营数据" />
|
||||
</template>
|
||||
<div class="flex flex-row flex-wrap items-center gap-8 p-4">
|
||||
<div
|
||||
v-for="item in data"
|
||||
:key="item.name"
|
||||
class="h-20 w-20% flex flex-col cursor-pointer items-center justify-center gap-2"
|
||||
@click="handleClick(item.routerName)"
|
||||
>
|
||||
<CountTo
|
||||
:prefix="item.prefix"
|
||||
:end-val="item.value"
|
||||
:decimals="item.decimals"
|
||||
class="text-3xl"
|
||||
/>
|
||||
<span class="text-center">{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||
import * as TradeStatisticsApi from '@/api/mall/statistics/trade'
|
||||
import * as PayStatisticsApi from '@/api/mall/statistics/pay'
|
||||
import { CardTitle } from '@/components/Card'
|
||||
|
||||
/** 运营数据卡片 */
|
||||
defineOptions({ name: 'OperationDataCard' })
|
||||
|
||||
const router = useRouter() // 路由
|
||||
|
||||
/** 数据 */
|
||||
const data = reactive({
|
||||
orderUndelivered: { name: '待发货订单', value: 9, routerName: 'TradeOrder' },
|
||||
orderAfterSaleApply: { name: '退款中订单', value: 4, routerName: 'TradeAfterSale' },
|
||||
orderWaitePickUp: { name: '待核销订单', value: 0, routerName: 'TradeOrder' },
|
||||
productAlertStock: { name: '库存预警', value: 0, routerName: 'ProductSpu' },
|
||||
productForSale: { name: '上架商品', value: 0, routerName: 'ProductSpu' },
|
||||
productInWarehouse: { name: '仓库商品', value: 0, routerName: 'ProductSpu' },
|
||||
withdrawAuditing: { name: '提现待审核', value: 0, routerName: 'TradeBrokerageWithdraw' },
|
||||
rechargePrice: {
|
||||
name: '账户充值',
|
||||
value: 0.0,
|
||||
prefix: '¥',
|
||||
decimals: 2,
|
||||
routerName: 'PayWalletRecharge'
|
||||
}
|
||||
})
|
||||
|
||||
/** 查询订单数据 */
|
||||
const getOrderData = async () => {
|
||||
const orderCount = await TradeStatisticsApi.getOrderCount()
|
||||
if (orderCount.undelivered != null) {
|
||||
data.orderUndelivered.value = orderCount.undelivered
|
||||
}
|
||||
if (orderCount.afterSaleApply != null) {
|
||||
data.orderAfterSaleApply.value = orderCount.afterSaleApply
|
||||
}
|
||||
if (orderCount.pickUp != null) {
|
||||
data.orderWaitePickUp.value = orderCount.pickUp
|
||||
}
|
||||
if (orderCount.auditingWithdraw != null) {
|
||||
data.withdrawAuditing.value = orderCount.auditingWithdraw
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询商品数据 */
|
||||
const getProductData = async () => {
|
||||
// TODO: @芋艿:这个接口的返回值,是不是用命名字段更好些?
|
||||
const productCount = await ProductSpuApi.getTabsCount()
|
||||
data.productForSale.value = productCount['0']
|
||||
data.productInWarehouse.value = productCount['1']
|
||||
data.productAlertStock.value = productCount['3']
|
||||
}
|
||||
|
||||
/** 查询钱包充值数据 */
|
||||
const getWalletRechargeData = async () => {
|
||||
const paySummary = await PayStatisticsApi.getWalletRechargePrice()
|
||||
data.rechargePrice.value = paySummary.rechargePrice
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到对应页面
|
||||
*
|
||||
* @param routerName 路由页面组件的名称
|
||||
*/
|
||||
const handleClick = (routerName: string) => {
|
||||
router.push({ name: routerName })
|
||||
}
|
||||
|
||||
/** 激活时 */
|
||||
onActivated(() => {
|
||||
getOrderData()
|
||||
getProductData()
|
||||
getWalletRechargeData()
|
||||
})
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getOrderData()
|
||||
getProductData()
|
||||
getWalletRechargeData()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<CardTitle title="快捷入口" />
|
||||
</template>
|
||||
<div class="flex flex-row flex-wrap gap-8 p-4">
|
||||
<div
|
||||
v-for="menu in menuList"
|
||||
:key="menu.name"
|
||||
class="h-20 w-20% flex flex-col cursor-pointer items-center justify-center gap-2"
|
||||
@click="handleMenuClick(menu.routerName)"
|
||||
>
|
||||
<div
|
||||
:class="menu.bgColor"
|
||||
class="h-48px w-48px flex items-center justify-center rounded text-white"
|
||||
>
|
||||
<Icon :icon="menu.icon" class="text-7.5!" />
|
||||
</div>
|
||||
<span>{{ menu.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
/** 快捷入口卡片 */
|
||||
import { CardTitle } from '@/components/Card'
|
||||
|
||||
defineOptions({ name: 'ShortcutCard' })
|
||||
|
||||
const router = useRouter() // 路由
|
||||
|
||||
/** 菜单列表 */
|
||||
const menuList = [
|
||||
{ name: '用户管理', icon: 'ep:user-filled', bgColor: 'bg-red-400', routerName: 'MemberUser' },
|
||||
{
|
||||
name: '商品管理',
|
||||
icon: 'fluent-mdl2:product',
|
||||
bgColor: 'bg-orange-400',
|
||||
routerName: 'ProductSpu'
|
||||
},
|
||||
{ name: '订单管理', icon: 'ep:list', bgColor: 'bg-yellow-500', routerName: 'TradeOrder' },
|
||||
{
|
||||
name: '售后管理',
|
||||
icon: 'ri:refund-2-line',
|
||||
bgColor: 'bg-green-600',
|
||||
routerName: 'TradeAfterSale'
|
||||
},
|
||||
{
|
||||
name: '分销管理',
|
||||
icon: 'fa-solid:project-diagram',
|
||||
bgColor: 'bg-cyan-500',
|
||||
routerName: 'TradeBrokerageUser'
|
||||
},
|
||||
{
|
||||
name: '优惠券',
|
||||
icon: 'ep:ticket',
|
||||
bgColor: 'bg-blue-500',
|
||||
routerName: 'PromotionCoupon'
|
||||
},
|
||||
{
|
||||
name: '拼团活动',
|
||||
icon: 'fa:group',
|
||||
bgColor: 'bg-purple-500',
|
||||
routerName: 'PromotionBargainActivity'
|
||||
},
|
||||
{
|
||||
name: '佣金提现',
|
||||
icon: 'vaadin:money-withdraw',
|
||||
bgColor: 'bg-rose-500',
|
||||
routerName: 'TradeBrokerageWithdraw'
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 跳转到菜单对应页面
|
||||
*
|
||||
* @param routerName 路由页面组件的名称
|
||||
*/
|
||||
const handleMenuClick = (routerName: string) => {
|
||||
router.push({ name: routerName })
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<CardTitle title="交易量趋势" />
|
||||
<!-- 查询条件 -->
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<el-radio-group v-model="timeRangeType" @change="handleTimeRangeTypeChange">
|
||||
<el-radio-button v-for="[key, value] in timeRange.entries()" :key="key" :label="key">
|
||||
{{ value.name }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 折线图 -->
|
||||
<Echart :height="300" :options="eChartOptions" />
|
||||
</el-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import dayjs, { Dayjs } from 'dayjs'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import * as TradeStatisticsApi from '@/api/mall/statistics/trade'
|
||||
import { fenToYuan } from '@/utils'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { CardTitle } from '@/components/Card'
|
||||
|
||||
/** 交易量趋势 */
|
||||
defineOptions({ name: 'TradeTrendCard' })
|
||||
|
||||
enum TimeRangeTypeEnum {
|
||||
DAY30 = 1,
|
||||
WEEK = 7,
|
||||
MONTH = 30,
|
||||
YEAR = 365
|
||||
} // 日期类型
|
||||
const timeRangeType = ref(TimeRangeTypeEnum.DAY30) // 日期快捷选择按钮, 默认30天
|
||||
const loading = ref(true) // 加载中
|
||||
// 时间范围 Map
|
||||
const timeRange = new Map()
|
||||
.set(TimeRangeTypeEnum.DAY30, {
|
||||
name: '30天',
|
||||
series: [
|
||||
{ name: '订单金额', type: 'bar', smooth: true, data: [] },
|
||||
{ name: '订单数量', type: 'line', smooth: true, data: [] }
|
||||
]
|
||||
})
|
||||
.set(TimeRangeTypeEnum.WEEK, {
|
||||
name: '周',
|
||||
series: [
|
||||
{ name: '上周金额', type: 'bar', smooth: true, data: [] },
|
||||
{ name: '本周金额', type: 'bar', smooth: true, data: [] },
|
||||
{ name: '上周数量', type: 'line', smooth: true, data: [] },
|
||||
{ name: '本周数量', type: 'line', smooth: true, data: [] }
|
||||
]
|
||||
})
|
||||
.set(TimeRangeTypeEnum.MONTH, {
|
||||
name: '月',
|
||||
series: [
|
||||
{ name: '上月金额', type: 'bar', smooth: true, data: [] },
|
||||
{ name: '本月金额', type: 'bar', smooth: true, data: [] },
|
||||
{ name: '上月数量', type: 'line', smooth: true, data: [] },
|
||||
{ name: '本月数量', type: 'line', smooth: true, data: [] }
|
||||
]
|
||||
})
|
||||
.set(TimeRangeTypeEnum.YEAR, {
|
||||
name: '年',
|
||||
series: [
|
||||
{ name: '去年金额', type: 'bar', smooth: true, data: [] },
|
||||
{ name: '今年金额', type: 'bar', smooth: true, data: [] },
|
||||
{ name: '去年数量', type: 'line', smooth: true, data: [] },
|
||||
{ name: '今年数量', type: 'line', smooth: true, data: [] }
|
||||
]
|
||||
})
|
||||
/** 图表配置 */
|
||||
const eChartOptions = reactive<EChartsOption>({
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
top: 80,
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
top: 50,
|
||||
data: []
|
||||
},
|
||||
series: [],
|
||||
toolbox: {
|
||||
feature: {
|
||||
// 数据区域缩放
|
||||
dataZoom: {
|
||||
yAxisIndex: false // Y轴不缩放
|
||||
},
|
||||
brush: {
|
||||
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
|
||||
},
|
||||
saveAsImage: { show: true, name: '订单量趋势' } // 保存为图片
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
},
|
||||
padding: [5, 10]
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
inverse: true,
|
||||
boundaryGap: false,
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
data: [],
|
||||
axisLabel: {
|
||||
formatter: (date: string) => {
|
||||
switch (timeRangeType.value) {
|
||||
case TimeRangeTypeEnum.DAY30:
|
||||
return formatDate(date, 'MM-DD')
|
||||
case TimeRangeTypeEnum.WEEK:
|
||||
let weekDay = formatDate(date, 'ddd')
|
||||
if (weekDay == '0') weekDay = '日'
|
||||
return '周' + weekDay
|
||||
case TimeRangeTypeEnum.MONTH:
|
||||
return formatDate(date, 'D')
|
||||
case TimeRangeTypeEnum.YEAR:
|
||||
return formatDate(date, 'M') + '月'
|
||||
default:
|
||||
return date
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}) as EChartsOption
|
||||
|
||||
/** 时间范围类型单选按钮选中 */
|
||||
const handleTimeRangeTypeChange = async () => {
|
||||
// 设置时间范围
|
||||
let beginTime: Dayjs
|
||||
let endTime: Dayjs
|
||||
switch (timeRangeType.value) {
|
||||
case TimeRangeTypeEnum.WEEK:
|
||||
beginTime = dayjs().startOf('week')
|
||||
endTime = dayjs().endOf('week')
|
||||
break
|
||||
case TimeRangeTypeEnum.MONTH:
|
||||
beginTime = dayjs().startOf('month')
|
||||
endTime = dayjs().endOf('month')
|
||||
break
|
||||
case TimeRangeTypeEnum.YEAR:
|
||||
beginTime = dayjs().startOf('year')
|
||||
endTime = dayjs().endOf('year')
|
||||
break
|
||||
case TimeRangeTypeEnum.DAY30:
|
||||
default:
|
||||
beginTime = dayjs().subtract(30, 'day').startOf('d')
|
||||
endTime = dayjs().endOf('d')
|
||||
break
|
||||
}
|
||||
// 发送时间范围选中事件
|
||||
await getOrderCountTrendComparison(beginTime, endTime)
|
||||
}
|
||||
|
||||
/** 查询订单数量趋势对照数据 */
|
||||
const getOrderCountTrendComparison = async (
|
||||
beginTime: dayjs.ConfigType,
|
||||
endTime: dayjs.ConfigType
|
||||
) => {
|
||||
loading.value = true
|
||||
// 查询数据
|
||||
const list = await TradeStatisticsApi.getOrderCountTrendComparison(
|
||||
timeRangeType.value,
|
||||
beginTime,
|
||||
endTime
|
||||
)
|
||||
// 处理数据
|
||||
const dates: string[] = []
|
||||
const series = [...timeRange.get(timeRangeType.value).series]
|
||||
for (let item of list) {
|
||||
dates.push(item.value.date)
|
||||
if (series.length === 2) {
|
||||
series[0].data.push(fenToYuan(item?.value?.orderPayPrice || 0)) // 当前金额
|
||||
series[1].data.push(item?.value?.orderPayCount || 0) // 当前数量
|
||||
} else {
|
||||
series[0].data.push(fenToYuan(item?.reference?.orderPayPrice || 0)) // 对照金额
|
||||
series[1].data.push(fenToYuan(item?.value?.orderPayPrice || 0)) // 当前金额
|
||||
series[2].data.push(item?.reference?.orderPayCount || 0) // 对照数量
|
||||
series[3].data.push(item?.value?.orderPayCount || 0) // 当前数量
|
||||
}
|
||||
}
|
||||
eChartOptions.xAxis!['data'] = dates
|
||||
eChartOptions.series = series
|
||||
// legend在4个切换到2个的时候,还是显示成4个,需要手动配置一下
|
||||
eChartOptions.legend['data'] = series.map((item) => item.name)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
handleTimeRangeTypeChange()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -13,6 +13,9 @@
|
|||
<el-form-item label="公司" prop="company">
|
||||
<el-input v-model="formData.company" placeholder="请输入公司" />
|
||||
</el-form-item>
|
||||
<el-form-item label="公司地址" prop="companyAddress">
|
||||
<el-input v-model="formData.companyAddress" placeholder="请输入公司地址" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="formData.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
|
|
@ -88,6 +91,7 @@ const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
|||
const formData = ref({
|
||||
name: undefined,
|
||||
company: undefined,
|
||||
companyAddress: undefined,
|
||||
email: undefined,
|
||||
contacts: undefined,
|
||||
phone: undefined,
|
||||
|
|
@ -101,6 +105,7 @@ const formData = ref({
|
|||
const formRules = reactive({
|
||||
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
|
||||
company: [{ required: true, message: '公司不能为空', trigger: 'blur' }],
|
||||
companyAddress: [{ required: true, message: '公司地址不能为空', trigger: 'blur' }],
|
||||
contacts: [{ required: true, message: '联系人不能为空', trigger: 'blur' }],
|
||||
phone: [{ required: true, message: '联系人手机号不能为空', trigger: 'blur' },
|
||||
{
|
||||
|
|
@ -182,6 +187,7 @@ const resetForm = () => {
|
|||
formData.value = {
|
||||
name: undefined,
|
||||
company: undefined,
|
||||
companyAddress: undefined,
|
||||
email: undefined,
|
||||
contacts: undefined,
|
||||
phone: undefined,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,176 @@
|
|||
<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="billno">
|
||||
<el-input v-model="formData.billno" placeholder="请输入合约编号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="客户id" prop="customerId">
|
||||
<el-input v-model="formData.customerId" placeholder="请输入客户id" />
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="customerName">
|
||||
<el-input v-model="formData.customerName" placeholder="请输入客户名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="客户采购编号" prop="customerBuyNo">
|
||||
<el-input v-model="formData.customerBuyNo" placeholder="请输入客户采购编号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="业务日期" prop="bizdate">
|
||||
<el-date-picker
|
||||
v-model="formData.bizdate"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择业务日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="甲方" prop="partyA">
|
||||
<el-input v-model="formData.partyA" placeholder="请输入甲方" />
|
||||
</el-form-item>
|
||||
<el-form-item label="乙方" prop="partyB">
|
||||
<el-input v-model="formData.partyB" placeholder="请输入乙方" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="head">
|
||||
<el-input v-model="formData.head" placeholder="请输入负责人" />
|
||||
</el-form-item>
|
||||
<el-form-item label="职员" prop="clerk">
|
||||
<el-input v-model="formData.clerk" placeholder="请输入职员" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机" prop="phone">
|
||||
<el-input v-model="formData.phone" placeholder="请输入手机" />
|
||||
</el-form-item>
|
||||
<el-form-item label="传真" prop="fax">
|
||||
<el-input v-model="formData.fax" placeholder="请输入传真" />
|
||||
</el-form-item>
|
||||
<el-form-item label="金额" prop="amount">
|
||||
<el-input v-model="formData.amount" placeholder="请输入金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="地址" prop="address">
|
||||
<el-input v-model="formData.address" placeholder="请输入地址" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 子表的表单 -->
|
||||
<el-tabs v-model="subTabsName">
|
||||
<el-tab-pane label="OMS销售合约分录" name="saleContractEntry">
|
||||
<SaleContractEntryForm ref="saleContractEntryFormRef" :parent-id="formData.id" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<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 { SaleContractApi, SaleContractVO } from '@/api/oms/salecontract'
|
||||
import SaleContractEntryForm from './components/SaleContractEntryForm.vue'
|
||||
|
||||
/** OMS销售合约 表单 */
|
||||
defineOptions({ name: 'SaleContractForm' })
|
||||
|
||||
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,
|
||||
billno: undefined,
|
||||
customerId: undefined,
|
||||
customerName: undefined,
|
||||
customerBuyNo: undefined,
|
||||
bizdate: undefined,
|
||||
partyA: undefined,
|
||||
partyB: undefined,
|
||||
head: undefined,
|
||||
clerk: undefined,
|
||||
phone: undefined,
|
||||
fax: undefined,
|
||||
amount: undefined,
|
||||
address: undefined,
|
||||
})
|
||||
const formRules = reactive({
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 子表的表单 */
|
||||
const subTabsName = ref('saleContractEntry')
|
||||
const saleContractEntryFormRef = 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 SaleContractApi.getSaleContract(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
await formRef.value.validate()
|
||||
// 校验子表单
|
||||
try {
|
||||
await saleContractEntryFormRef.value.validate()
|
||||
} catch (e) {
|
||||
subTabsName.value = 'saleContractEntry'
|
||||
return
|
||||
}
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as SaleContractVO
|
||||
// 拼接子表的数据
|
||||
data.saleContractEntrys = saleContractEntryFormRef.value.getData()
|
||||
if (formType.value === 'create') {
|
||||
await SaleContractApi.createSaleContract(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await SaleContractApi.updateSaleContract(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
billno: undefined,
|
||||
customerId: undefined,
|
||||
customerName: undefined,
|
||||
customerBuyNo: undefined,
|
||||
bizdate: undefined,
|
||||
partyA: undefined,
|
||||
partyB: undefined,
|
||||
head: undefined,
|
||||
clerk: undefined,
|
||||
phone: undefined,
|
||||
fax: undefined,
|
||||
amount: undefined,
|
||||
address: undefined,
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
v-loading="formLoading"
|
||||
label-width="0px"
|
||||
:inline-message="true"
|
||||
>
|
||||
<el-table :data="formData" class="-mt-10px">
|
||||
<el-table-column label="序号" type="index" width="100" />
|
||||
<el-table-column label="产品ID" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.productId`" :rules="formRules.productId" class="mb-0px!">
|
||||
<el-input v-model="row.productId" placeholder="请输入产品ID" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="产品编码" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.productNumber`" :rules="formRules.productNumber" class="mb-0px!">
|
||||
<el-input v-model="row.productNumber" placeholder="请输入产品编码" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="产品名称" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.productName`" :rules="formRules.productName" class="mb-0px!">
|
||||
<el-input v-model="row.productName" placeholder="请输入产品名称" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="数量" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.qty`" :rules="formRules.qty" class="mb-0px!">
|
||||
<el-input v-model="row.qty" placeholder="请输入数量" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位订价" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.price`" :rules="formRules.price" class="mb-0px!">
|
||||
<el-input v-model="row.price" placeholder="请输入单位订价" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="折扣" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.discount`" :rules="formRules.discount" class="mb-0px!">
|
||||
<el-input v-model="row.discount" placeholder="请输入折扣" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="金额" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.amount`" :rules="formRules.amount" class="mb-0px!">
|
||||
<el-input v-model="row.amount" placeholder="请输入金额" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交货日期" min-width="150">
|
||||
<template #default="{ row, $index }">
|
||||
<el-form-item :prop="`${$index}.deliveryDate`" :rules="formRules.deliveryDate" class="mb-0px!">
|
||||
<el-date-picker
|
||||
v-model="row.deliveryDate"
|
||||
type="date"
|
||||
value-format="x"
|
||||
placeholder="选择交货日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" fixed="right" label="操作" width="60">
|
||||
<template #default="{ $index }">
|
||||
<el-button @click="handleDelete($index)" link>—</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
<el-row justify="center" class="mt-3">
|
||||
<el-button @click="handleAdd" round>+ 添加OMS销售合约分录</el-button>
|
||||
</el-row>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { SaleContractApi } from '@/api/oms/salecontract'
|
||||
|
||||
const props = defineProps<{
|
||||
parentId: undefined // 主表ID(主表的关联字段)
|
||||
}>()
|
||||
const formLoading = ref(false) // 表单的加载中
|
||||
const formData = ref([])
|
||||
const formRules = reactive({
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
() => props.parentId,
|
||||
async (val) => {
|
||||
// 1. 重置表单
|
||||
formData.value = []
|
||||
// 2. val 非空,则加载数据
|
||||
if (!val) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
formLoading.value = true
|
||||
formData.value = await SaleContractApi.getSaleContractEntryListByParentId(val)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
const row = {
|
||||
id: undefined,
|
||||
parentId: undefined,
|
||||
productId: undefined,
|
||||
productNumber: undefined,
|
||||
productName: undefined,
|
||||
qty: undefined,
|
||||
price: undefined,
|
||||
discount: undefined,
|
||||
amount: undefined,
|
||||
deliveryDate: undefined,
|
||||
}
|
||||
row.parentId = props.parentId
|
||||
formData.value.push(row)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = (index) => {
|
||||
formData.value.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 表单校验 */
|
||||
const validate = () => {
|
||||
return formRef.value.validate()
|
||||
}
|
||||
|
||||
/** 表单值 */
|
||||
const getData = () => {
|
||||
return formData.value
|
||||
}
|
||||
|
||||
defineExpose({ validate, getData })
|
||||
</script>
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="合约编号" prop="billno">
|
||||
<el-input
|
||||
v-model="queryParams.billno"
|
||||
placeholder="请输入合约编号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户名称" prop="customerName">
|
||||
<el-input
|
||||
v-model="queryParams.customerName"
|
||||
placeholder="请输入客户名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="客户采购编号" prop="customerBuyNo">
|
||||
<el-input
|
||||
v-model="queryParams.customerBuyNo"
|
||||
placeholder="请输入客户采购编号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务日期" prop="bizdate">
|
||||
<el-date-picker
|
||||
v-model="queryParams.bizdate"
|
||||
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 label="甲方" prop="partyA">
|
||||
<el-input
|
||||
v-model="queryParams.partyA"
|
||||
placeholder="请输入甲方"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="乙方" prop="partyB">
|
||||
<el-input
|
||||
v-model="queryParams.partyB"
|
||||
placeholder="请输入乙方"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="head">
|
||||
<el-input
|
||||
v-model="queryParams.head"
|
||||
placeholder="请输入负责人"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="职员" prop="clerk">
|
||||
<el-input
|
||||
v-model="queryParams.clerk"
|
||||
placeholder="请输入职员"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机" prop="phone">
|
||||
<el-input
|
||||
v-model="queryParams.phone"
|
||||
placeholder="请输入手机"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="传真" prop="fax">
|
||||
<el-input
|
||||
v-model="queryParams.fax"
|
||||
placeholder="请输入传真"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="金额" prop="amount">
|
||||
<el-input
|
||||
v-model="queryParams.amount"
|
||||
placeholder="请输入金额"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="地址" prop="address">
|
||||
<el-input
|
||||
v-model="queryParams.address"
|
||||
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:sale-contract:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['oms:sale-contract: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="合约编号" align="center" prop="billno" />
|
||||
<el-table-column label="客户id" align="center" prop="customerId" />
|
||||
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||
<el-table-column label="客户采购编号" align="center" prop="customerBuyNo" />
|
||||
<el-table-column
|
||||
label="业务日期"
|
||||
align="center"
|
||||
prop="bizdate"
|
||||
:formatter="dateFormatter"
|
||||
width="180px"
|
||||
/>
|
||||
<el-table-column label="甲方" align="center" prop="partyA" />
|
||||
<el-table-column label="乙方" align="center" prop="partyB" />
|
||||
<el-table-column label="负责人" align="center" prop="head" />
|
||||
<el-table-column label="职员" align="center" prop="clerk" />
|
||||
<el-table-column label="手机" align="center" prop="phone" />
|
||||
<el-table-column label="传真" align="center" prop="fax" />
|
||||
<el-table-column label="金额" align="center" prop="amount" />
|
||||
<el-table-column label="地址" align="center" prop="address" />
|
||||
<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:sale-contract:update']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['oms:sale-contract: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>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<SaleContractForm ref="formRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { SaleContractApi, SaleContractVO } from '@/api/oms/salecontract'
|
||||
import SaleContractForm from './SaleContractForm.vue'
|
||||
|
||||
/** OMS销售合约 列表 */
|
||||
defineOptions({ name: 'SaleContract' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<SaleContractVO[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
billno: undefined,
|
||||
customerName: undefined,
|
||||
customerBuyNo: undefined,
|
||||
bizdate: [],
|
||||
partyA: undefined,
|
||||
partyB: undefined,
|
||||
head: undefined,
|
||||
clerk: undefined,
|
||||
phone: undefined,
|
||||
fax: undefined,
|
||||
amount: undefined,
|
||||
address: undefined,
|
||||
createTime: [],
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await SaleContractApi.getSaleContractPage(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 SaleContractApi.deleteSaleContract(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await SaleContractApi.exportSaleContract(queryParams)
|
||||
download.excel(data, 'OMS销售合约.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
|
@ -118,21 +118,29 @@
|
|||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
@click="handleReject(selectionList.map((item) => item.id))"
|
||||
@click="handleReject()"
|
||||
:disabled="selectionList.length === 0"
|
||||
>驳回
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="handleUpdateBillStatus('invalid')"
|
||||
:disabled="selectionList.length === 0"
|
||||
>
|
||||
作废
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="handleUpdateBillStatus(selectionList.map((item) => item.id),'submit')"
|
||||
@click="handleUpdateBillStatus('submit')"
|
||||
:disabled="selectionList.length === 0"
|
||||
>提交
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="handleUpdateBillStatus(selectionList.map((item) => item.id),'audit')"
|
||||
@click="handleUpdateBillStatus('audit')"
|
||||
:disabled="selectionList.length === 0"
|
||||
>
|
||||
审核
|
||||
|
|
@ -140,15 +148,7 @@
|
|||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="handleUpdateBillStatus(selectionList.map((item) => item.id),'unaudit')"
|
||||
:disabled="selectionList.length === 0"
|
||||
>
|
||||
反审核
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="generateProduceOrder(selectionList.map((item) => item.id))"
|
||||
@click="generateProduceOrder()"
|
||||
:disabled="selectionList.length === 0 || queryParams.tabType === 0"
|
||||
>
|
||||
生成制单
|
||||
|
|
@ -337,8 +337,6 @@ const queryFormRef = ref() // 搜索的表单
|
|||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
|
||||
|
||||
|
||||
//驳回参数
|
||||
const rejectOpen = ref(false)
|
||||
const rejectTitle = ref('')
|
||||
|
|
@ -402,22 +400,40 @@ const handleDelete = async (id: number) => {
|
|||
|
||||
|
||||
/** 驳回操作 */
|
||||
const handleReject = async (ids: number[]) => {
|
||||
const handleReject = async () => {
|
||||
const ids = selectionList.value.map((item) => item.id)
|
||||
const billStatus = selectionList.value.map((item) => item.billStatus)
|
||||
for(let vals of billStatus) {
|
||||
if(vals!='B'){
|
||||
message.error("请选择单据状态已提交的数据行");
|
||||
return;
|
||||
}
|
||||
}
|
||||
rejectform.rejectReason = undefined
|
||||
rejectTitle.value = "是否驳回选中的数据项?"
|
||||
rejectOpen.value = true
|
||||
}
|
||||
/** 审批/反审批操作 */
|
||||
const handleUpdateBillStatus = async (ids: number[], operateKey: string) => {
|
||||
/** 审批操作 */
|
||||
const handleUpdateBillStatus = async (operateKey: string) => {
|
||||
try {
|
||||
const ids = selectionList.value.map((item) => item.id)
|
||||
const billStatus = selectionList.value.map((item) => item.billStatus)
|
||||
for(let vals of billStatus) {
|
||||
if("audit" == operateKey && vals!='B'){
|
||||
message.error("请选择单据状态已提交的数据行");
|
||||
return;
|
||||
}
|
||||
if("invalid" == operateKey && vals!='B'){
|
||||
message.error("请选择单据状态已提交的数据行");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 审批的二次确认
|
||||
let operateName = ''
|
||||
if(operateKey === 'submit'){
|
||||
operateName = '提交'
|
||||
}else if(operateKey === "audit"){
|
||||
operateName = '审核'
|
||||
}else if(operateKey === "unaudit"){
|
||||
operateName = '反审核'
|
||||
}else {
|
||||
return
|
||||
}
|
||||
|
|
@ -433,8 +449,17 @@ const handleUpdateBillStatus = async (ids: number[], operateKey: string) => {
|
|||
|
||||
|
||||
/** 审批/反审批操作 */
|
||||
const generateProduceOrder = async (ids: number[]) => {
|
||||
const generateProduceOrder = async () => {
|
||||
try {
|
||||
const ids = selectionList.value.map((item) => item.id)
|
||||
const billStatus = selectionList.value.map((item) => item.billStatus)
|
||||
for(let vals of billStatus) {
|
||||
if(vals!='C'){
|
||||
message.error("请选择单据状态已审核的数据行");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 审批的二次确认
|
||||
await message.confirm(`是否生成生产制单?`)
|
||||
// 发起审批
|
||||
|
|
@ -480,6 +505,9 @@ const handleSelectionChange = (rows: SaleOrderVO[]) => {
|
|||
/** 驳回提交按钮 */
|
||||
const submitRejectForm = async () => {
|
||||
const ids = selectionList.value.map((item) => item.id)
|
||||
const billStatus = selectionList.value.map((item) => item.billStatus)
|
||||
|
||||
|
||||
if(rejectform.rejectReason != null) {
|
||||
const data = {
|
||||
ids:ids,
|
||||
|
|
|
|||
Loading…
Reference in New Issue