✨ 新增毛利分析报表及提成设置
This commit is contained in:
+103
@@ -0,0 +1,103 @@
|
|||||||
|
package com.bms.web.controller.report;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import com.bms.common.annotation.Log;
|
||||||
|
import com.bms.common.core.controller.BaseController;
|
||||||
|
import com.bms.common.core.domain.AjaxResult;
|
||||||
|
import com.bms.common.core.page.TableDataInfo;
|
||||||
|
import com.bms.common.enums.BusinessType;
|
||||||
|
import com.bms.common.utils.poi.ExcelUtil;
|
||||||
|
import com.bms.price.domain.PrcProfitCommissionRate;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportOrderRow;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportProductRow;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportQuery;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportSummary;
|
||||||
|
import com.bms.price.service.IPrcProfitReportService;
|
||||||
|
|
||||||
|
/** 产品/订单维度毛利报表。 */
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/report/profit")
|
||||||
|
public class PrcProfitReportController extends BaseController
|
||||||
|
{
|
||||||
|
@Autowired
|
||||||
|
private IPrcProfitReportService profitReportService;
|
||||||
|
|
||||||
|
@PreAuthorize("@ss.hasPermi('report:profit:list')")
|
||||||
|
@GetMapping("/summary")
|
||||||
|
public AjaxResult summary(PrcProfitReportQuery query)
|
||||||
|
{
|
||||||
|
return success(profitReportService.selectSummary(query));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("@ss.hasPermi('report:profit:commission:list')")
|
||||||
|
@GetMapping("/commission-rates")
|
||||||
|
public AjaxResult commissionRates()
|
||||||
|
{
|
||||||
|
return success(profitReportService.selectCommissionRates());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "毛利报表-业务员提成比例", businessType = BusinessType.INSERT)
|
||||||
|
@PreAuthorize("@ss.hasPermi('report:profit:commission:save')")
|
||||||
|
@PostMapping("/commission-rates")
|
||||||
|
public AjaxResult saveCommissionRate(@RequestBody PrcProfitCommissionRate rate)
|
||||||
|
{
|
||||||
|
profitReportService.saveCommissionRate(rate);
|
||||||
|
return success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "毛利报表-业务员提成比例", businessType = BusinessType.DELETE)
|
||||||
|
@PreAuthorize("@ss.hasPermi('report:profit:commission:remove')")
|
||||||
|
@DeleteMapping("/commission-rates/{rateId}")
|
||||||
|
public AjaxResult deleteCommissionRate(@PathVariable Long rateId)
|
||||||
|
{
|
||||||
|
return toAjax(profitReportService.deleteCommissionRate(rateId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("@ss.hasPermi('report:profit:list')")
|
||||||
|
@GetMapping("/orders")
|
||||||
|
public TableDataInfo orders(PrcProfitReportQuery query)
|
||||||
|
{
|
||||||
|
startPage();
|
||||||
|
List<PrcProfitReportOrderRow> rows = profitReportService.selectOrderRows(query);
|
||||||
|
return getDataTable(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("@ss.hasPermi('report:profit:list')")
|
||||||
|
@GetMapping("/products")
|
||||||
|
public TableDataInfo products(PrcProfitReportQuery query)
|
||||||
|
{
|
||||||
|
startPage();
|
||||||
|
List<PrcProfitReportProductRow> rows = profitReportService.selectProductRows(query);
|
||||||
|
return getDataTable(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "毛利报表-订单维度", businessType = BusinessType.EXPORT)
|
||||||
|
@PreAuthorize("@ss.hasPermi('report:profit:export')")
|
||||||
|
@PostMapping("/orders/export")
|
||||||
|
public void exportOrders(HttpServletResponse response, PrcProfitReportQuery query)
|
||||||
|
{
|
||||||
|
List<PrcProfitReportOrderRow> rows = profitReportService.selectOrderRows(query);
|
||||||
|
new ExcelUtil<PrcProfitReportOrderRow>(PrcProfitReportOrderRow.class)
|
||||||
|
.exportExcel(response, rows, "订单毛利报表");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "毛利报表-产品维度", businessType = BusinessType.EXPORT)
|
||||||
|
@PreAuthorize("@ss.hasPermi('report:profit:export')")
|
||||||
|
@PostMapping("/products/export")
|
||||||
|
public void exportProducts(HttpServletResponse response, PrcProfitReportQuery query)
|
||||||
|
{
|
||||||
|
List<PrcProfitReportProductRow> rows = profitReportService.selectProductRows(query);
|
||||||
|
new ExcelUtil<PrcProfitReportProductRow>(PrcProfitReportProductRow.class)
|
||||||
|
.exportExcel(response, rows, "产品毛利报表");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.bms.price.domain;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/** 业务员固定提成比例配置。commissionRate 按百分数存储,例如 5 表示 5%。 */
|
||||||
|
@Data
|
||||||
|
public class PrcProfitCommissionRate implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long rateId;
|
||||||
|
private String belongSalerCode;
|
||||||
|
private String belongSaler;
|
||||||
|
private BigDecimal commissionRate;
|
||||||
|
private String status;
|
||||||
|
private String remark;
|
||||||
|
private String createBy;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createTime;
|
||||||
|
private String updateBy;
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
package com.bms.price.domain.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import com.bms.common.annotation.Excel;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/** 订单维度毛利报表行。 */
|
||||||
|
@Data
|
||||||
|
public class PrcProfitReportOrderRow implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Excel(name = "订单号", width = 24)
|
||||||
|
private String orderNo;
|
||||||
|
@Excel(name = "客户代码", width = 16)
|
||||||
|
private String customerCode;
|
||||||
|
@Excel(name = "客户名称", width = 24)
|
||||||
|
private String customerName;
|
||||||
|
@Excel(name = "业务员", width = 16)
|
||||||
|
private String belongSaler;
|
||||||
|
@Excel(name = "业务员编码", width = 16)
|
||||||
|
private String belongSalerCode;
|
||||||
|
@Excel(name = "产品代码", width = 16)
|
||||||
|
private String productCode;
|
||||||
|
@Excel(name = "产品名称", width = 24)
|
||||||
|
private String productName;
|
||||||
|
@Excel(name = "国家代码", width = 12)
|
||||||
|
private String countryCode;
|
||||||
|
@Excel(name = "国家/地区", width = 18)
|
||||||
|
private String countryName;
|
||||||
|
@Excel(name = "币种", width = 10)
|
||||||
|
private String currency;
|
||||||
|
@Excel(name = "计费时间", dateFormat = "yyyy-MM-dd HH:mm:ss", width = 22)
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date bizDate;
|
||||||
|
@Excel(name = "产品价(目录价)", scale = 4, width = 20)
|
||||||
|
private BigDecimal costAmount;
|
||||||
|
@Excel(name = "红线价", scale = 4, width = 16)
|
||||||
|
private BigDecimal redlineAmount;
|
||||||
|
@Excel(name = "红线毛利", scale = 4, width = 16)
|
||||||
|
private BigDecimal redlineGrossProfit;
|
||||||
|
@Excel(name = "红线毛利率(%)", scale = 4, width = 18)
|
||||||
|
private BigDecimal redlineGrossMarginRate;
|
||||||
|
@Excel(name = "销售价", scale = 4, width = 16)
|
||||||
|
private BigDecimal salesAmount;
|
||||||
|
@Excel(name = "销售毛利", scale = 4, width = 16)
|
||||||
|
private BigDecimal salesGrossProfit;
|
||||||
|
@Excel(name = "销售毛利率(%)", scale = 4, width = 18)
|
||||||
|
private BigDecimal salesGrossMarginRate;
|
||||||
|
@Excel(name = "提成比例(%)", scale = 4, width = 16)
|
||||||
|
private BigDecimal commissionRate;
|
||||||
|
@Excel(name = "提成金额", scale = 4, width = 16)
|
||||||
|
private BigDecimal commissionAmount;
|
||||||
|
@Excel(name = "应收状态", width = 14)
|
||||||
|
private String arBillStatus;
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package com.bms.price.domain.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import com.bms.common.annotation.Excel;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/** 产品+目的地维度毛利汇总行。 */
|
||||||
|
@Data
|
||||||
|
public class PrcProfitReportProductRow implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Excel(name = "产品代码", width = 16)
|
||||||
|
private String productCode;
|
||||||
|
@Excel(name = "产品名称", width = 24)
|
||||||
|
private String productName;
|
||||||
|
@Excel(name = "国家代码", width = 12)
|
||||||
|
private String countryCode;
|
||||||
|
@Excel(name = "国家/地区", width = 18)
|
||||||
|
private String countryName;
|
||||||
|
@Excel(name = "币种", width = 10)
|
||||||
|
private String currency;
|
||||||
|
@Excel(name = "订单数", width = 12)
|
||||||
|
private Long orderCount;
|
||||||
|
@Excel(name = "产品价(目录价)", scale = 4, width = 20)
|
||||||
|
private BigDecimal costAmount;
|
||||||
|
@Excel(name = "红线价", scale = 4, width = 16)
|
||||||
|
private BigDecimal redlineAmount;
|
||||||
|
@Excel(name = "红线毛利", scale = 4, width = 16)
|
||||||
|
private BigDecimal redlineGrossProfit;
|
||||||
|
@Excel(name = "红线毛利率(%)", scale = 4, width = 18)
|
||||||
|
private BigDecimal redlineGrossMarginRate;
|
||||||
|
@Excel(name = "销售价", scale = 4, width = 16)
|
||||||
|
private BigDecimal salesAmount;
|
||||||
|
@Excel(name = "销售毛利", scale = 4, width = 16)
|
||||||
|
private BigDecimal salesGrossProfit;
|
||||||
|
@Excel(name = "销售毛利率(%)", scale = 4, width = 18)
|
||||||
|
private BigDecimal salesGrossMarginRate;
|
||||||
|
@Excel(name = "提成金额", scale = 4, width = 16)
|
||||||
|
private BigDecimal commissionAmount;
|
||||||
|
@Excel(name = "未计算提成订单数", width = 20)
|
||||||
|
private Long commissionMissingOrderCount;
|
||||||
|
@Excel(name = "产品价或价格缺失订单数", width = 22)
|
||||||
|
private Long incompleteOrderCount;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.bms.price.domain.vo;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
import com.bms.common.core.domain.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/** 毛利报表查询条件。 */
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class PrcProfitReportQuery extends BaseEntity
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date beginBizDate;
|
||||||
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date endBizDate;
|
||||||
|
private String orderNo;
|
||||||
|
/** 订单号拆分后的多值查询条件。 */
|
||||||
|
private List<String> orderNos;
|
||||||
|
private String customerCode;
|
||||||
|
/** 客户代码拆分后的多值查询条件。 */
|
||||||
|
private List<String> customerCodes;
|
||||||
|
private String customerName;
|
||||||
|
/** 客户名称拆分后的多值查询条件。 */
|
||||||
|
private List<String> customerNames;
|
||||||
|
private String belongSalerCode;
|
||||||
|
private String productCode;
|
||||||
|
private String countryCode;
|
||||||
|
private String currency;
|
||||||
|
/** 是否按产品维度分组值下钻订单明细,允许分组字段为空。 */
|
||||||
|
private Boolean productDrilldown;
|
||||||
|
/** 订单明细弹窗的筛选类型:ALL、COMMISSION_MISSING、INCOMPLETE。 */
|
||||||
|
private String detailType;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.bms.price.domain.vo;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/** 毛利报表汇总指标。 */
|
||||||
|
@Data
|
||||||
|
public class PrcProfitReportSummary implements Serializable
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long orderCount;
|
||||||
|
private Long incompleteOrderCount;
|
||||||
|
private String currency;
|
||||||
|
private BigDecimal costAmount;
|
||||||
|
private BigDecimal redlineAmount;
|
||||||
|
private BigDecimal redlineGrossProfit;
|
||||||
|
private BigDecimal redlineGrossMarginRate;
|
||||||
|
private BigDecimal salesAmount;
|
||||||
|
private BigDecimal salesGrossProfit;
|
||||||
|
private BigDecimal salesGrossMarginRate;
|
||||||
|
private BigDecimal commissionAmount;
|
||||||
|
private Long commissionMissingOrderCount;
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package com.bms.price.mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.bms.price.domain.PrcProfitCommissionRate;
|
||||||
|
|
||||||
|
public interface PrcProfitCommissionRateMapper
|
||||||
|
{
|
||||||
|
List<PrcProfitCommissionRate> selectAll();
|
||||||
|
|
||||||
|
int upsert(PrcProfitCommissionRate rate);
|
||||||
|
|
||||||
|
int deleteById(Long rateId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.bms.price.mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportOrderRow;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportProductRow;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportQuery;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportSummary;
|
||||||
|
|
||||||
|
/** 毛利报表 mapper;金额事实取自 prc_income_bill。 */
|
||||||
|
public interface PrcProfitReportMapper
|
||||||
|
{
|
||||||
|
List<PrcProfitReportOrderRow> selectOrderRows(PrcProfitReportQuery query);
|
||||||
|
|
||||||
|
List<PrcProfitReportProductRow> selectProductRows(PrcProfitReportQuery query);
|
||||||
|
|
||||||
|
List<PrcProfitReportSummary> selectSummary(PrcProfitReportQuery query);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.bms.price.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.bms.price.domain.PrcProfitCommissionRate;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportOrderRow;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportProductRow;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportQuery;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportSummary;
|
||||||
|
|
||||||
|
/** 毛利报表服务。 */
|
||||||
|
public interface IPrcProfitReportService
|
||||||
|
{
|
||||||
|
List<PrcProfitReportOrderRow> selectOrderRows(PrcProfitReportQuery query);
|
||||||
|
|
||||||
|
List<PrcProfitReportProductRow> selectProductRows(PrcProfitReportQuery query);
|
||||||
|
|
||||||
|
List<PrcProfitReportSummary> selectSummary(PrcProfitReportQuery query);
|
||||||
|
|
||||||
|
List<PrcProfitCommissionRate> selectCommissionRates();
|
||||||
|
|
||||||
|
void saveCommissionRate(PrcProfitCommissionRate rate);
|
||||||
|
|
||||||
|
int deleteCommissionRate(Long rateId);
|
||||||
|
}
|
||||||
+139
@@ -0,0 +1,139 @@
|
|||||||
|
package com.bms.price.service.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import com.bms.common.annotation.DataScope;
|
||||||
|
import com.bms.common.exception.ServiceException;
|
||||||
|
import com.bms.common.utils.SecurityUtils;
|
||||||
|
import com.bms.price.domain.PrcProfitCommissionRate;
|
||||||
|
import com.bms.price.mapper.PrcProfitCommissionRateMapper;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportOrderRow;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportProductRow;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportQuery;
|
||||||
|
import com.bms.price.domain.vo.PrcProfitReportSummary;
|
||||||
|
import com.bms.price.mapper.PrcProfitReportMapper;
|
||||||
|
import com.bms.price.service.IPrcProfitReportService;
|
||||||
|
|
||||||
|
/** 毛利报表:以 prc_income_bill 为金额事实表,不关联应付模块。 */
|
||||||
|
@Service
|
||||||
|
public class PrcProfitReportServiceImpl implements IPrcProfitReportService
|
||||||
|
{
|
||||||
|
private static final int MULTI_VALUE_FILTER_LIMIT = 2000;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrcProfitReportMapper profitReportMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PrcProfitCommissionRateMapper commissionRateMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = "report:profit:list")
|
||||||
|
public List<PrcProfitReportOrderRow> selectOrderRows(PrcProfitReportQuery query)
|
||||||
|
{
|
||||||
|
normalizeMultiValueFilters(query);
|
||||||
|
return profitReportMapper.selectOrderRows(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = "report:profit:list")
|
||||||
|
public List<PrcProfitReportProductRow> selectProductRows(PrcProfitReportQuery query)
|
||||||
|
{
|
||||||
|
normalizeMultiValueFilters(query);
|
||||||
|
return profitReportMapper.selectProductRows(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DataScope(deptAlias = "su", deptField = "dept_id", userAlias = "su", userField = "user_id", permission = "report:profit:list")
|
||||||
|
public List<PrcProfitReportSummary> selectSummary(PrcProfitReportQuery query)
|
||||||
|
{
|
||||||
|
normalizeMultiValueFilters(query);
|
||||||
|
return profitReportMapper.selectSummary(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将报表的多行输入拆分为去重后的查询条件,支持换行、空格、逗号和分号分隔。 */
|
||||||
|
private void normalizeMultiValueFilters(PrcProfitReportQuery query)
|
||||||
|
{
|
||||||
|
if (query == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
query.setOrderNos(splitFilterValues(query.getOrderNo(), "订单号"));
|
||||||
|
query.setCustomerCodes(splitFilterValues(query.getCustomerCode(), "客户代码"));
|
||||||
|
query.setCustomerNames(splitFilterValues(query.getCustomerName(), "客户名称"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> splitFilterValues(String rawValue, String fieldName)
|
||||||
|
{
|
||||||
|
LinkedHashSet<String> values = new LinkedHashSet<>();
|
||||||
|
if (rawValue != null && !rawValue.trim().isEmpty())
|
||||||
|
{
|
||||||
|
for (String item : rawValue.split("[\\s,,;;]+"))
|
||||||
|
{
|
||||||
|
String value = item.trim();
|
||||||
|
if (!value.isEmpty())
|
||||||
|
{
|
||||||
|
values.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (values.size() > MULTI_VALUE_FILTER_LIMIT)
|
||||||
|
{
|
||||||
|
throw new ServiceException(fieldName + "最多输入 " + MULTI_VALUE_FILTER_LIMIT + " 个不重复值");
|
||||||
|
}
|
||||||
|
return new ArrayList<>(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PrcProfitCommissionRate> selectCommissionRates()
|
||||||
|
{
|
||||||
|
return commissionRateMapper.selectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void saveCommissionRate(PrcProfitCommissionRate rate)
|
||||||
|
{
|
||||||
|
if (rate == null || rate.getBelongSalerCode() == null || rate.getBelongSalerCode().trim().isEmpty())
|
||||||
|
{
|
||||||
|
throw new ServiceException("业务员编码不能为空");
|
||||||
|
}
|
||||||
|
BigDecimal commissionRate = rate.getCommissionRate();
|
||||||
|
if (commissionRate == null || commissionRate.compareTo(BigDecimal.ZERO) < 0
|
||||||
|
|| commissionRate.compareTo(new BigDecimal("100")) > 0)
|
||||||
|
{
|
||||||
|
throw new ServiceException("提成比例必须在 0 至 100 之间");
|
||||||
|
}
|
||||||
|
String status = rate.getStatus();
|
||||||
|
if (!"0".equals(status) && !"1".equals(status))
|
||||||
|
{
|
||||||
|
throw new ServiceException("提成比例状态不正确");
|
||||||
|
}
|
||||||
|
String salerCode = rate.getBelongSalerCode().trim();
|
||||||
|
String salerName = rate.getBelongSaler() == null ? null : rate.getBelongSaler().trim();
|
||||||
|
if (salerCode.length() > 64 || (salerName != null && salerName.length() > 64)
|
||||||
|
|| (rate.getRemark() != null && rate.getRemark().length() > 500))
|
||||||
|
{
|
||||||
|
throw new ServiceException("业务员编码、名称或备注超过最大长度");
|
||||||
|
}
|
||||||
|
rate.setBelongSalerCode(salerCode);
|
||||||
|
rate.setBelongSaler(salerName);
|
||||||
|
rate.setCreateBy(SecurityUtils.getUsername());
|
||||||
|
commissionRateMapper.upsert(rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public int deleteCommissionRate(Long rateId)
|
||||||
|
{
|
||||||
|
if (rateId == null)
|
||||||
|
{
|
||||||
|
throw new ServiceException("提成比例配置ID不能为空");
|
||||||
|
}
|
||||||
|
return commissionRateMapper.deleteById(rateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?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="com.bms.price.mapper.PrcProfitCommissionRateMapper">
|
||||||
|
<resultMap type="PrcProfitCommissionRate" id="PrcProfitCommissionRateResult">
|
||||||
|
<id property="rateId" column="rate_id"/>
|
||||||
|
<result property="belongSalerCode" column="belong_saler_code"/>
|
||||||
|
<result property="belongSaler" column="belong_saler"/>
|
||||||
|
<result property="commissionRate" column="commission_rate"/>
|
||||||
|
<result property="status" column="status"/>
|
||||||
|
<result property="remark" column="remark"/>
|
||||||
|
<result property="createBy" column="create_by"/>
|
||||||
|
<result property="createTime" column="create_time"/>
|
||||||
|
<result property="updateBy" column="update_by"/>
|
||||||
|
<result property="updateTime" column="update_time"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="selectAll" resultMap="PrcProfitCommissionRateResult">
|
||||||
|
select rate_id, belong_saler_code, belong_saler, commission_rate, status,
|
||||||
|
remark, create_by, create_time, update_by, update_time
|
||||||
|
from prc_profit_commission_rate
|
||||||
|
order by belong_saler_code
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<insert id="upsert" parameterType="PrcProfitCommissionRate">
|
||||||
|
insert into prc_profit_commission_rate (
|
||||||
|
belong_saler_code, belong_saler, commission_rate, status, remark, create_by, create_time
|
||||||
|
) values (
|
||||||
|
#{belongSalerCode}, #{belongSaler}, #{commissionRate}, #{status}, #{remark}, #{createBy}, sysdate()
|
||||||
|
)
|
||||||
|
on duplicate key update
|
||||||
|
belong_saler = values(belong_saler),
|
||||||
|
commission_rate = values(commission_rate),
|
||||||
|
status = values(status),
|
||||||
|
remark = values(remark),
|
||||||
|
update_by = values(create_by),
|
||||||
|
update_time = sysdate()
|
||||||
|
</insert>
|
||||||
|
|
||||||
|
<delete id="deleteById" parameterType="Long">
|
||||||
|
delete from prc_profit_commission_rate where rate_id = #{rateId}
|
||||||
|
</delete>
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,276 @@
|
|||||||
|
<?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="com.bms.price.mapper.PrcProfitReportMapper">
|
||||||
|
|
||||||
|
<resultMap type="PrcProfitReportOrderRow" id="PrcProfitReportOrderResult">
|
||||||
|
<result property="orderNo" column="order_no"/>
|
||||||
|
<result property="customerCode" column="customer_code"/>
|
||||||
|
<result property="customerName" column="customer_name"/>
|
||||||
|
<result property="belongSaler" column="belong_saler"/>
|
||||||
|
<result property="belongSalerCode" column="belong_saler_code"/>
|
||||||
|
<result property="productCode" column="product_code"/>
|
||||||
|
<result property="productName" column="product_name"/>
|
||||||
|
<result property="countryCode" column="country_code"/>
|
||||||
|
<result property="countryName" column="country_name"/>
|
||||||
|
<result property="currency" column="currency"/>
|
||||||
|
<result property="bizDate" column="biz_date"/>
|
||||||
|
<result property="costAmount" column="cost_amount"/>
|
||||||
|
<result property="redlineAmount" column="redline_amount"/>
|
||||||
|
<result property="redlineGrossProfit" column="redline_gross_profit"/>
|
||||||
|
<result property="redlineGrossMarginRate" column="redline_gross_margin_rate"/>
|
||||||
|
<result property="salesAmount" column="sales_amount"/>
|
||||||
|
<result property="salesGrossProfit" column="sales_gross_profit"/>
|
||||||
|
<result property="salesGrossMarginRate" column="sales_gross_margin_rate"/>
|
||||||
|
<result property="commissionRate" column="commission_rate"/>
|
||||||
|
<result property="commissionAmount" column="commission_amount"/>
|
||||||
|
<result property="arBillStatus" column="ar_bill_status"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<resultMap type="PrcProfitReportProductRow" id="PrcProfitReportProductResult">
|
||||||
|
<result property="productCode" column="product_code"/>
|
||||||
|
<result property="productName" column="product_name"/>
|
||||||
|
<result property="countryCode" column="country_code"/>
|
||||||
|
<result property="countryName" column="country_name"/>
|
||||||
|
<result property="currency" column="currency"/>
|
||||||
|
<result property="orderCount" column="order_count"/>
|
||||||
|
<result property="costAmount" column="cost_amount"/>
|
||||||
|
<result property="redlineAmount" column="redline_amount"/>
|
||||||
|
<result property="redlineGrossProfit" column="redline_gross_profit"/>
|
||||||
|
<result property="redlineGrossMarginRate" column="redline_gross_margin_rate"/>
|
||||||
|
<result property="salesAmount" column="sales_amount"/>
|
||||||
|
<result property="salesGrossProfit" column="sales_gross_profit"/>
|
||||||
|
<result property="salesGrossMarginRate" column="sales_gross_margin_rate"/>
|
||||||
|
<result property="commissionAmount" column="commission_amount"/>
|
||||||
|
<result property="commissionMissingOrderCount" column="commission_missing_order_count"/>
|
||||||
|
<result property="incompleteOrderCount" column="incomplete_order_count"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<resultMap type="PrcProfitReportSummary" id="PrcProfitReportSummaryResult">
|
||||||
|
<result property="orderCount" column="order_count"/>
|
||||||
|
<result property="incompleteOrderCount" column="incomplete_order_count"/>
|
||||||
|
<result property="currency" column="currency"/>
|
||||||
|
<result property="costAmount" column="cost_amount"/>
|
||||||
|
<result property="redlineAmount" column="redline_amount"/>
|
||||||
|
<result property="redlineGrossProfit" column="redline_gross_profit"/>
|
||||||
|
<result property="redlineGrossMarginRate" column="redline_gross_margin_rate"/>
|
||||||
|
<result property="salesAmount" column="sales_amount"/>
|
||||||
|
<result property="salesGrossProfit" column="sales_gross_profit"/>
|
||||||
|
<result property="salesGrossMarginRate" column="sales_gross_margin_rate"/>
|
||||||
|
<result property="commissionAmount" column="commission_amount"/>
|
||||||
|
<result property="commissionMissingOrderCount" column="commission_missing_order_count"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<sql id="profitReportFromAndWhere">
|
||||||
|
from prc_income_bill i
|
||||||
|
inner join prc_order_bill p on p.bill_id = i.bill_id
|
||||||
|
left join prc_profit_commission_rate cr
|
||||||
|
on cr.belong_saler_code = i.belong_saler_code and cr.status = '0'
|
||||||
|
<where>
|
||||||
|
<if test="beginBizDate != null">
|
||||||
|
and p.biz_date >= #{beginBizDate}
|
||||||
|
</if>
|
||||||
|
<if test="endBizDate != null">
|
||||||
|
and p.biz_date <= #{endBizDate}
|
||||||
|
</if>
|
||||||
|
<choose>
|
||||||
|
<when test="orderNos != null and orderNos.size() > 0">
|
||||||
|
and (
|
||||||
|
<foreach collection="orderNos" item="orderValue" separator=" or ">
|
||||||
|
p.order_no like concat('%', #{orderValue}, '%')
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</when>
|
||||||
|
<when test="orderNo != null and orderNo != ''">
|
||||||
|
and p.order_no like concat('%', #{orderNo}, '%')
|
||||||
|
</when>
|
||||||
|
</choose>
|
||||||
|
<choose>
|
||||||
|
<when test="customerCodes != null and customerCodes.size() > 0">
|
||||||
|
and p.customer_code in
|
||||||
|
<foreach collection="customerCodes" item="customerValue" open="(" separator="," close=")">
|
||||||
|
#{customerValue}
|
||||||
|
</foreach>
|
||||||
|
</when>
|
||||||
|
<when test="customerCode != null and customerCode != ''">
|
||||||
|
and p.customer_code = #{customerCode}
|
||||||
|
</when>
|
||||||
|
</choose>
|
||||||
|
<choose>
|
||||||
|
<when test="customerNames != null and customerNames.size() > 0">
|
||||||
|
and (
|
||||||
|
<foreach collection="customerNames" item="customerValue" separator=" or ">
|
||||||
|
p.customer_name like concat('%', #{customerValue}, '%')
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</when>
|
||||||
|
<when test="customerName != null and customerName != ''">
|
||||||
|
and p.customer_name like concat('%', #{customerName}, '%')
|
||||||
|
</when>
|
||||||
|
</choose>
|
||||||
|
<choose>
|
||||||
|
<when test="productDrilldown != null and productDrilldown">
|
||||||
|
and p.product_code <=> #{productCode}
|
||||||
|
and p.country_code <=> #{countryCode}
|
||||||
|
and i.currency <=> #{currency}
|
||||||
|
<if test="belongSalerCode != null and belongSalerCode != ''">
|
||||||
|
and i.belong_saler_code = #{belongSalerCode}
|
||||||
|
</if>
|
||||||
|
</when>
|
||||||
|
<otherwise>
|
||||||
|
<if test="belongSalerCode != null and belongSalerCode != ''">
|
||||||
|
and i.belong_saler_code = #{belongSalerCode}
|
||||||
|
</if>
|
||||||
|
<if test="productCode != null and productCode != ''">
|
||||||
|
and p.product_code = #{productCode}
|
||||||
|
</if>
|
||||||
|
<if test="countryCode != null and countryCode != ''">
|
||||||
|
and p.country_code = #{countryCode}
|
||||||
|
</if>
|
||||||
|
<if test="currency != null and currency != ''">
|
||||||
|
and i.currency = #{currency}
|
||||||
|
</if>
|
||||||
|
</otherwise>
|
||||||
|
</choose>
|
||||||
|
<choose>
|
||||||
|
<when test="detailType == 'COMMISSION_MISSING'">
|
||||||
|
and (cr.commission_rate is null or i.product_amount is null or i.sales_amount is null)
|
||||||
|
</when>
|
||||||
|
<when test="detailType == 'INCOMPLETE'">
|
||||||
|
and (i.product_amount is null or i.redline_amount is null or i.sales_amount is null)
|
||||||
|
</when>
|
||||||
|
</choose>
|
||||||
|
<if test="params != null and params.dataScope != null and params.dataScope != ''">
|
||||||
|
and exists (
|
||||||
|
select 1
|
||||||
|
from sys_user su
|
||||||
|
where su.del_flag = '0'
|
||||||
|
and (
|
||||||
|
(p.belong_saler_code is not null and p.belong_saler_code != ''
|
||||||
|
and (su.user_name = p.belong_saler_code collate utf8mb4_general_ci
|
||||||
|
or su.user_id = p.belong_saler_code + 0))
|
||||||
|
or
|
||||||
|
(p.settlement_man_code is not null and p.settlement_man_code != ''
|
||||||
|
and (su.user_name = p.settlement_man_code collate utf8mb4_general_ci
|
||||||
|
or su.user_id = p.settlement_man_code + 0))
|
||||||
|
)
|
||||||
|
${params.dataScope}
|
||||||
|
)
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<sql id="profitExpressions">
|
||||||
|
i.product_amount as cost_amount,
|
||||||
|
i.redline_amount,
|
||||||
|
case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount - i.product_amount else null end as redline_gross_profit,
|
||||||
|
case when i.product_amount is not null and i.redline_amount is not null and i.redline_amount != 0
|
||||||
|
then round((i.redline_amount - i.product_amount) * 100 / i.redline_amount, 4) else null end as redline_gross_margin_rate,
|
||||||
|
i.sales_amount,
|
||||||
|
case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount - i.product_amount else null end as sales_gross_profit,
|
||||||
|
case when i.product_amount is not null and i.sales_amount is not null and i.sales_amount != 0
|
||||||
|
then round((i.sales_amount - i.product_amount) * 100 / i.sales_amount, 4) else null end as sales_gross_margin_rate,
|
||||||
|
cr.commission_rate,
|
||||||
|
case when cr.commission_rate is not null and i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then round((i.sales_amount - i.product_amount) * cr.commission_rate / 100, 4) else null end as commission_amount
|
||||||
|
</sql>
|
||||||
|
|
||||||
|
<select id="selectOrderRows" resultMap="PrcProfitReportOrderResult">
|
||||||
|
select p.order_no, p.customer_code, p.customer_name,
|
||||||
|
i.belong_saler, i.belong_saler_code,
|
||||||
|
p.product_code, p.product_name, p.country_code, p.country_name,
|
||||||
|
i.currency, p.biz_date,
|
||||||
|
<include refid="profitExpressions"/>,
|
||||||
|
p.ar_bill_status
|
||||||
|
<include refid="profitReportFromAndWhere"/>
|
||||||
|
order by p.biz_date desc, p.bill_id desc
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectProductRows" resultMap="PrcProfitReportProductResult">
|
||||||
|
select p.product_code, p.product_name, p.country_code, p.country_name,
|
||||||
|
i.currency, count(*) as order_count,
|
||||||
|
sum(i.product_amount) as cost_amount,
|
||||||
|
sum(i.redline_amount) as redline_amount,
|
||||||
|
sum(case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount - i.product_amount else null end) as redline_gross_profit,
|
||||||
|
case when sum(case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount else null end) is null
|
||||||
|
or sum(case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount else null end) = 0
|
||||||
|
then null
|
||||||
|
else round(
|
||||||
|
sum(case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount - i.product_amount else null end) * 100 /
|
||||||
|
sum(case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount else null end), 4)
|
||||||
|
end as redline_gross_margin_rate,
|
||||||
|
sum(i.sales_amount) as sales_amount,
|
||||||
|
sum(case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount - i.product_amount else null end) as sales_gross_profit,
|
||||||
|
case when sum(case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount else null end) is null
|
||||||
|
or sum(case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount else null end) = 0
|
||||||
|
then null
|
||||||
|
else round(
|
||||||
|
sum(case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount - i.product_amount else null end) * 100 /
|
||||||
|
sum(case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount else null end), 4)
|
||||||
|
end as sales_gross_margin_rate,
|
||||||
|
sum(case when cr.commission_rate is not null and i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then (i.sales_amount - i.product_amount) * cr.commission_rate / 100 else null end) as commission_amount,
|
||||||
|
sum(case when cr.commission_rate is null or i.product_amount is null or i.sales_amount is null
|
||||||
|
then 1 else 0 end) as commission_missing_order_count,
|
||||||
|
sum(case when i.product_amount is null or i.redline_amount is null or i.sales_amount is null
|
||||||
|
then 1 else 0 end) as incomplete_order_count
|
||||||
|
<include refid="profitReportFromAndWhere"/>
|
||||||
|
group by p.product_code, p.product_name, p.country_code, p.country_name, i.currency
|
||||||
|
order by p.product_code, p.country_code, i.currency
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectSummary" resultMap="PrcProfitReportSummaryResult">
|
||||||
|
select i.currency, count(*) as order_count,
|
||||||
|
sum(case when i.product_amount is null or i.redline_amount is null or i.sales_amount is null
|
||||||
|
then 1 else 0 end) as incomplete_order_count,
|
||||||
|
sum(i.product_amount) as cost_amount,
|
||||||
|
sum(i.redline_amount) as redline_amount,
|
||||||
|
sum(case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount - i.product_amount else null end) as redline_gross_profit,
|
||||||
|
case when sum(case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount else null end) is null
|
||||||
|
or sum(case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount else null end) = 0
|
||||||
|
then null
|
||||||
|
else round(
|
||||||
|
sum(case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount - i.product_amount else null end) * 100 /
|
||||||
|
sum(case when i.product_amount is not null and i.redline_amount is not null
|
||||||
|
then i.redline_amount else null end), 4)
|
||||||
|
end as redline_gross_margin_rate,
|
||||||
|
sum(i.sales_amount) as sales_amount,
|
||||||
|
sum(case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount - i.product_amount else null end) as sales_gross_profit,
|
||||||
|
case when sum(case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount else null end) is null
|
||||||
|
or sum(case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount else null end) = 0
|
||||||
|
then null
|
||||||
|
else round(
|
||||||
|
sum(case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount - i.product_amount else null end) * 100 /
|
||||||
|
sum(case when i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then i.sales_amount else null end), 4)
|
||||||
|
end as sales_gross_margin_rate,
|
||||||
|
sum(case when cr.commission_rate is not null and i.product_amount is not null and i.sales_amount is not null
|
||||||
|
then (i.sales_amount - i.product_amount) * cr.commission_rate / 100 else null end) as commission_amount,
|
||||||
|
sum(case when cr.commission_rate is null or i.product_amount is null or i.sales_amount is null
|
||||||
|
then 1 else 0 end) as commission_missing_order_count
|
||||||
|
<include refid="profitReportFromAndWhere"/>
|
||||||
|
group by i.currency
|
||||||
|
order by i.currency
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@@ -956,6 +956,25 @@ CREATE TABLE `prc_income_bill` (
|
|||||||
CONSTRAINT `fk_income_bill_order_bill` FOREIGN KEY (`bill_id`) REFERENCES `prc_order_bill` (`bill_id`) ON DELETE CASCADE
|
CONSTRAINT `fk_income_bill_order_bill` FOREIGN KEY (`bill_id`) REFERENCES `prc_order_bill` (`bill_id`) ON DELETE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应收主表(订单计费与应收费用中间层,与订单一对一)';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='应收主表(订单计费与应收费用中间层,与订单一对一)';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 表结构:`prc_profit_commission_rate`
|
||||||
|
-- ----------------------------
|
||||||
|
CREATE TABLE `prc_profit_commission_rate` (
|
||||||
|
`rate_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '提成比例ID',
|
||||||
|
`belong_saler_code` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务员编码',
|
||||||
|
`belong_saler` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务员名称',
|
||||||
|
`commission_rate` decimal(7,4) NOT NULL COMMENT '提成比例(百分数,例如5表示5%)',
|
||||||
|
`status` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '状态:0正常 1停用',
|
||||||
|
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
|
||||||
|
`create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`update_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`rate_id`),
|
||||||
|
UNIQUE KEY `uk_profit_commission_saler` (`belong_saler_code`),
|
||||||
|
KEY `idx_profit_commission_status` (`status`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='毛利报表业务员固定提成比例';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- 表结构:`prc_income_bill_fee`
|
-- 表结构:`prc_income_bill_fee`
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@@ -1062,6 +1081,7 @@ CREATE TABLE `prc_order_bill` (
|
|||||||
UNIQUE KEY `uk_bill_order` (`order_no`),
|
UNIQUE KEY `uk_bill_order` (`order_no`),
|
||||||
UNIQUE KEY `uk_bill_key` (`bill_key`),
|
UNIQUE KEY `uk_bill_key` (`bill_key`),
|
||||||
KEY `idx_bill_customer` (`customer_code`,`biz_date`),
|
KEY `idx_bill_customer` (`customer_code`,`biz_date`),
|
||||||
|
KEY `idx_bill_biz_date_report` (`biz_date`,`bill_id`),
|
||||||
KEY `idx_bill_product` (`product_code`,`country_code`),
|
KEY `idx_bill_product` (`product_code`,`country_code`),
|
||||||
KEY `idx_bill_ar_status` (`customer_id`,`currency`,`ar_bill_status`,`biz_date`),
|
KEY `idx_bill_ar_status` (`customer_id`,`currency`,`ar_bill_status`,`biz_date`),
|
||||||
KEY `idx_bill_ar_async_cursor` (`customer_id`,`currency`,`ar_bill_status`,`create_time`,`bill_id`),
|
KEY `idx_bill_ar_async_cursor` (`customer_id`,`currency`,`ar_bill_status`,`create_time`,`bill_id`),
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
-- 毛利报表:提成比例配置表与计费日期查询索引。
|
||||||
|
-- 每位业务员维护一条固定比例;提成比例以百分数保存,例如 5 表示 5%。
|
||||||
|
CREATE TABLE IF NOT EXISTS `prc_profit_commission_rate` (
|
||||||
|
`rate_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '提成比例ID',
|
||||||
|
`belong_saler_code` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '业务员编码',
|
||||||
|
`belong_saler` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务员名称',
|
||||||
|
`commission_rate` decimal(7,4) NOT NULL COMMENT '提成比例(百分数,例如5表示5%)',
|
||||||
|
`status` char(1) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '状态:0正常 1停用',
|
||||||
|
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
|
||||||
|
`create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
|
||||||
|
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`update_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`rate_id`),
|
||||||
|
UNIQUE KEY `uk_profit_commission_saler` (`belong_saler_code`),
|
||||||
|
KEY `idx_profit_commission_status` (`status`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='毛利报表业务员固定提成比例';
|
||||||
|
|
||||||
|
SET @index_exists := (
|
||||||
|
SELECT COUNT(1) FROM information_schema.STATISTICS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'prc_order_bill'
|
||||||
|
AND INDEX_NAME = 'idx_bill_biz_date_report'
|
||||||
|
);
|
||||||
|
SET @sql := IF(@index_exists = 0,
|
||||||
|
'ALTER TABLE `prc_order_bill` ADD INDEX `idx_bill_biz_date_report` (`biz_date`, `bill_id`)',
|
||||||
|
'SELECT 1'
|
||||||
|
);
|
||||||
|
PREPARE stmt FROM @sql;
|
||||||
|
EXECUTE stmt;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
-- 报表中心 - 毛利分析及提成比例维护权限
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @report_menu_id := (
|
||||||
|
SELECT menu_id FROM sys_menu
|
||||||
|
WHERE parent_id = 0 AND menu_type = 'M' AND menu_name = N'报表中心'
|
||||||
|
ORDER BY menu_id LIMIT 1
|
||||||
|
);
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type,
|
||||||
|
visible, status, perms, icon, create_by, create_time, remark)
|
||||||
|
SELECT N'报表中心', 0, 8, 'report', NULL, 1, 0, 'M', '0', '0', '', 'chart', 'admin', sysdate(), N'经营分析报表'
|
||||||
|
FROM DUAL WHERE @report_menu_id IS NULL;
|
||||||
|
SET @report_menu_id := (
|
||||||
|
SELECT menu_id FROM sys_menu
|
||||||
|
WHERE parent_id = 0 AND menu_type = 'M' AND menu_name = N'报表中心'
|
||||||
|
ORDER BY menu_id LIMIT 1
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache,
|
||||||
|
menu_type, visible, status, perms, icon, create_by, create_time, remark)
|
||||||
|
SELECT N'毛利分析', @report_menu_id, 1, 'profit', 'report/profit/index', '', 'ProfitReport', 1, 0,
|
||||||
|
'C', '0', '0', 'report:profit:list', 'chart', 'admin', sysdate(), N'按产品和订单查看红线/销售毛利与提成'
|
||||||
|
FROM DUAL
|
||||||
|
WHERE @report_menu_id IS NOT NULL
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'report:profit:list');
|
||||||
|
SET @profit_menu_id := (SELECT menu_id FROM sys_menu WHERE perms = 'report:profit:list' ORDER BY menu_id LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type,
|
||||||
|
visible, status, perms, icon, create_by, create_time)
|
||||||
|
SELECT N'导出', @profit_menu_id, 1, '', NULL, 1, 0, 'F', '0', '0', 'report:profit:export', '#', 'admin', sysdate()
|
||||||
|
FROM DUAL WHERE @profit_menu_id IS NOT NULL
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'report:profit:export');
|
||||||
|
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type,
|
||||||
|
visible, status, perms, icon, create_by, create_time)
|
||||||
|
SELECT N'提成比例查询', @profit_menu_id, 2, '', NULL, 1, 0, 'F', '0', '0', 'report:profit:commission:list', '#', 'admin', sysdate()
|
||||||
|
FROM DUAL WHERE @profit_menu_id IS NOT NULL
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'report:profit:commission:list');
|
||||||
|
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type,
|
||||||
|
visible, status, perms, icon, create_by, create_time)
|
||||||
|
SELECT N'提成比例维护', @profit_menu_id, 3, '', NULL, 1, 0, 'F', '0', '0', 'report:profit:commission:save', '#', 'admin', sysdate()
|
||||||
|
FROM DUAL WHERE @profit_menu_id IS NOT NULL
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'report:profit:commission:save');
|
||||||
|
|
||||||
|
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type,
|
||||||
|
visible, status, perms, icon, create_by, create_time)
|
||||||
|
SELECT N'提成比例删除', @profit_menu_id, 4, '', NULL, 1, 0, 'F', '0', '0', 'report:profit:commission:remove', '#', 'admin', sysdate()
|
||||||
|
FROM DUAL WHERE @profit_menu_id IS NOT NULL
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'report:profit:commission:remove');
|
||||||
|
|
||||||
|
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||||
|
SELECT 1, m.menu_id
|
||||||
|
FROM sys_menu m
|
||||||
|
WHERE (m.menu_id = @report_menu_id OR m.menu_id = @profit_menu_id OR m.parent_id = @profit_menu_id)
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM sys_role_menu rm WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id);
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import request, { download } from '@/utils/request'
|
||||||
|
|
||||||
|
export function listProfitSummary(params) {
|
||||||
|
return request({
|
||||||
|
url: '/report/profit/summary',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listProfitOrders(params) {
|
||||||
|
return request({
|
||||||
|
url: '/report/profit/orders',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listProfitProducts(params) {
|
||||||
|
return request({
|
||||||
|
url: '/report/profit/products',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exportProfitOrders(params) {
|
||||||
|
return download('/report/profit/orders/export', params, '订单毛利报表.xlsx')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exportProfitProducts(params) {
|
||||||
|
return download('/report/profit/products/export', params, '产品毛利报表.xlsx')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listProfitCommissionRates() {
|
||||||
|
return request({
|
||||||
|
url: '/report/profit/commission-rates',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveProfitCommissionRate(data) {
|
||||||
|
return request({
|
||||||
|
url: '/report/profit/commission-rates',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteProfitCommissionRate(rateId) {
|
||||||
|
return request({
|
||||||
|
url: '/report/profit/commission-rates/' + rateId,
|
||||||
|
method: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,699 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-container profit-report">
|
||||||
|
<el-card shadow="never" class="search-card">
|
||||||
|
<el-form :model="query" label-width="88px" @submit.prevent>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :xs="24" :sm="12" :lg="8">
|
||||||
|
<el-form-item label="计费日期" class="filter-item">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
class="filter-control"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" :lg="8">
|
||||||
|
<el-form-item label="业务员编码" class="filter-item">
|
||||||
|
<el-input v-model="query.belongSalerCode" clearable placeholder="输入业务员编码" class="filter-control" @keyup.enter="handleSearch" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" :lg="8">
|
||||||
|
<el-form-item label="产品代码" class="filter-item">
|
||||||
|
<el-input v-model="query.productCode" clearable placeholder="输入产品代码" class="filter-control" @keyup.enter="handleSearch" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" :lg="8">
|
||||||
|
<el-form-item label="订单号" class="filter-item">
|
||||||
|
<el-input
|
||||||
|
v-model="query.orderNo"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
resize="vertical"
|
||||||
|
clearable
|
||||||
|
placeholder="输入订单号,多个值请换行"
|
||||||
|
class="filter-control multi-line-filter"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" :lg="8">
|
||||||
|
<el-form-item label="客户代码" class="filter-item">
|
||||||
|
<el-input
|
||||||
|
v-model="query.customerCode"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
resize="vertical"
|
||||||
|
clearable
|
||||||
|
placeholder="输入客户代码,多个值请换行"
|
||||||
|
class="filter-control multi-line-filter"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" :lg="8">
|
||||||
|
<el-form-item label="客户名称" class="filter-item">
|
||||||
|
<el-input
|
||||||
|
v-model="query.customerName"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
resize="vertical"
|
||||||
|
clearable
|
||||||
|
placeholder="输入客户名称,多个值请换行"
|
||||||
|
class="filter-control multi-line-filter"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" :lg="8" class="filter-actions-col">
|
||||||
|
<div class="filter-item filter-actions">
|
||||||
|
<el-button type="primary" icon="Search" @click="handleSearch">搜索</el-button>
|
||||||
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<div v-if="summaryList.length" class="summary-list">
|
||||||
|
<el-card v-for="summary in summaryList" :key="summary.currency" shadow="never" class="summary-card">
|
||||||
|
<template #header>
|
||||||
|
<span>{{ summary.currency || '未标币种' }} 汇总</span>
|
||||||
|
<span class="summary-subtitle">订单 {{ summary.orderCount || 0 }} 条</span>
|
||||||
|
</template>
|
||||||
|
<el-row :gutter="12" class="summary-grid">
|
||||||
|
<el-col :xs="12" :sm="8" :md="6">
|
||||||
|
<div class="metric-item"><div class="metric-label">红线价</div><div class="metric-value">{{ money(summary.redlineAmount) }}</div></div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="12" :sm="8" :md="6">
|
||||||
|
<div class="metric-item"><div class="metric-label">产品价</div><div class="metric-value">{{ money(summary.costAmount) }}</div></div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="12" :sm="8" :md="6">
|
||||||
|
<div class="metric-item"><div class="metric-label">销售价</div><div class="metric-value">{{ money(summary.salesAmount) }}</div></div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="12" :sm="8" :md="6">
|
||||||
|
<div class="metric-item"><div class="metric-label">红线毛利</div><div class="metric-value">{{ money(summary.redlineGrossProfit) }} <small>{{ rate(summary.redlineGrossMarginRate) }}</small></div></div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="12" :sm="8" :md="6">
|
||||||
|
<div class="metric-item"><div class="metric-label">销售毛利</div><div class="metric-value profit-value">{{ money(summary.salesGrossProfit) }} <small>{{ rate(summary.salesGrossMarginRate) }}</small></div></div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="12" :sm="8" :md="6">
|
||||||
|
<div class="metric-item"><div class="metric-label">提成金额</div><div class="metric-value">{{ money(summary.commissionAmount) }}</div></div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="12" :sm="8" :md="6">
|
||||||
|
<div class="metric-item"><div class="metric-label">价格缺失订单</div><div class="metric-value">{{ summary.incompleteOrderCount || 0 }}</div></div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="12" :sm="8" :md="6">
|
||||||
|
<div class="metric-item"><div class="metric-label">提成未计算订单</div><div class="metric-value">{{ summary.commissionMissingOrderCount || 0 }}</div></div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-card shadow="never" class="table-card">
|
||||||
|
<div class="table-heading">
|
||||||
|
<el-tabs v-model="dimension" @tab-change="handleDimensionChange">
|
||||||
|
<el-tab-pane label="产品维度" name="product" />
|
||||||
|
<el-tab-pane label="订单维度" name="order" />
|
||||||
|
</el-tabs>
|
||||||
|
<div class="table-toolbar">
|
||||||
|
<el-button
|
||||||
|
icon="Setting"
|
||||||
|
v-hasPermi="['report:profit:commission:list']"
|
||||||
|
@click="openCommissionRates"
|
||||||
|
>提成比例设置</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
icon="Download"
|
||||||
|
v-hasPermi="['report:profit:export']"
|
||||||
|
:loading="exporting"
|
||||||
|
@click="handleExport"
|
||||||
|
>导出当前维度</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-text-drag-copy="true" class="report-table-copy-area">
|
||||||
|
<el-table :key="dimension" v-loading="loading" :data="rows" border stripe :row-key="rowKey">
|
||||||
|
<template v-if="dimension === 'product'">
|
||||||
|
<el-table-column prop="productCode" label="产品代码" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="productName" label="产品名称" min-width="180" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="countryName" label="国家/地区" min-width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="countryCode" label="国家代码" width="100" align="center" />
|
||||||
|
<el-table-column prop="currency" label="币种" width="80" align="center" />
|
||||||
|
<el-table-column prop="orderCount" label="订单数" width="100" align="right">
|
||||||
|
<template #default="{ row }"><span class="success-value order-count-link" :class="{ 'is-disabled': !row.orderCount }" @click.stop="openOrderList(row, 'ALL', '订单数')">{{ row.orderCount }}</span></template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="costAmount" label="产品价" min-width="130" align="right"><template #default="{ row }">{{ money(row.costAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="redlineAmount" label="红线价" min-width="130" align="right"><template #default="{ row }">{{ money(row.redlineAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="redlineGrossProfit" label="红线毛利" min-width="130" align="right"><template #default="{ row }">{{ money(row.redlineGrossProfit) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="redlineGrossMarginRate" label="红线毛利率" min-width="120" align="right"><template #default="{ row }">{{ rate(row.redlineGrossMarginRate) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="salesAmount" label="销售价" min-width="130" align="right"><template #default="{ row }">{{ money(row.salesAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="salesGrossProfit" label="销售毛利" min-width="130" align="right"><template #default="{ row }">{{ money(row.salesGrossProfit) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="salesGrossMarginRate" label="销售毛利率" min-width="120" align="right"><template #default="{ row }">{{ rate(row.salesGrossMarginRate) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="commissionAmount" label="提成金额" min-width="120" align="right"><template #default="{ row }">{{ money(row.commissionAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="commissionMissingOrderCount" label="提成未计算订单" width="150" align="right">
|
||||||
|
<template #default="{ row }"><span class="success-value order-count-link" :class="{ 'is-disabled': !row.commissionMissingOrderCount }" @click.stop="openOrderList(row, 'COMMISSION_MISSING', '提成未计算订单')">{{ row.commissionMissingOrderCount }}</span></template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="incompleteOrderCount" label="价格缺失订单" width="130" align="right">
|
||||||
|
<template #default="{ row }"><span class="success-value order-count-link" :class="{ 'is-disabled': !row.incompleteOrderCount }" @click.stop="openOrderList(row, 'INCOMPLETE', '价格缺失订单')">{{ row.incompleteOrderCount }}</span></template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-table-column prop="bizDate" label="计费时间" width="170" align="center"><template #default="{ row }">{{ parseTime(row.bizDate) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="orderNo" label="订单号" min-width="160" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="customerCode" label="客户代码" width="120" />
|
||||||
|
<el-table-column prop="customerName" label="客户名称" min-width="160" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="belongSalerCode" label="业务员编码" min-width="150" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="belongSaler" label="业务员" min-width="110" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="productCode" label="产品代码" width="120" />
|
||||||
|
<el-table-column prop="productName" label="产品名称" min-width="160" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="countryName" label="国家/地区" min-width="110" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="currency" label="币种" width="80" align="center" />
|
||||||
|
<el-table-column prop="costAmount" label="产品价" min-width="130" align="right"><template #default="{ row }">{{ money(row.costAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="redlineAmount" label="红线价" min-width="130" align="right"><template #default="{ row }">{{ money(row.redlineAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="redlineGrossProfit" label="红线毛利" min-width="130" align="right"><template #default="{ row }">{{ money(row.redlineGrossProfit) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="redlineGrossMarginRate" label="红线毛利率" min-width="120" align="right"><template #default="{ row }">{{ rate(row.redlineGrossMarginRate) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="salesAmount" label="销售价" min-width="130" align="right"><template #default="{ row }">{{ money(row.salesAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="salesGrossProfit" label="销售毛利" min-width="130" align="right"><template #default="{ row }">{{ money(row.salesGrossProfit) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="salesGrossMarginRate" label="销售毛利率" min-width="120" align="right"><template #default="{ row }">{{ rate(row.salesGrossMarginRate) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="commissionRate" label="提成比例" width="110" align="right"><template #default="{ row }">{{ commissionRateFormat(row.commissionRate) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="commissionAmount" label="提成金额" min-width="120" align="right"><template #default="{ row }">{{ money(row.commissionAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="arBillStatus" label="应收状态" width="110" align="center">
|
||||||
|
<template #default="{ row }">{{ arBillStatusLabel(row.arBillStatus) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<template #empty><el-empty description="暂无符合条件的数据" /></template>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pagination
|
||||||
|
v-show="total > 0"
|
||||||
|
v-model:page="pageNum"
|
||||||
|
v-model:limit="pageSize"
|
||||||
|
:total="total"
|
||||||
|
@pagination="loadRows"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="orderListDialogVisible" :title="orderListTitle" width="1280px" append-to-body>
|
||||||
|
<el-table v-loading="orderListLoading" :data="orderListRows" border stripe>
|
||||||
|
<el-table-column prop="bizDate" label="计费时间" width="170" align="center"><template #default="{ row }">{{ parseTime(row.bizDate) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="orderNo" label="订单号" min-width="170" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="customerCode" label="客户代码" width="120" />
|
||||||
|
<el-table-column prop="customerName" label="客户名称" min-width="160" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="belongSalerCode" label="业务员编码" width="130" />
|
||||||
|
<el-table-column prop="productCode" label="产品代码" width="120" />
|
||||||
|
<el-table-column prop="productName" label="产品名称" min-width="160" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="countryName" label="国家/地区" width="110" />
|
||||||
|
<el-table-column prop="currency" label="币种" width="80" align="center" />
|
||||||
|
<el-table-column prop="costAmount" label="产品价" width="110" align="right"><template #default="{ row }">{{ money(row.costAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="redlineAmount" label="红线价" width="110" align="right"><template #default="{ row }">{{ money(row.redlineAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="salesAmount" label="销售价" width="110" align="right"><template #default="{ row }">{{ money(row.salesAmount) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="arBillStatus" label="应收状态" width="100" align="center"><template #default="{ row }">{{ arBillStatusLabel(row.arBillStatus) }}</template></el-table-column>
|
||||||
|
<template #empty><el-empty description="暂无符合条件的订单" /></template>
|
||||||
|
</el-table>
|
||||||
|
<pagination
|
||||||
|
v-show="orderListTotal > 0"
|
||||||
|
v-model:page="orderListPageNum"
|
||||||
|
v-model:limit="orderListPageSize"
|
||||||
|
:total="orderListTotal"
|
||||||
|
@pagination="loadOrderList"
|
||||||
|
/>
|
||||||
|
<template #footer><el-button @click="orderListDialogVisible = false">关闭</el-button></template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="commissionDialogVisible" title="业务员固定提成比例" width="760px" append-to-body>
|
||||||
|
<div class="commission-toolbar">
|
||||||
|
<span class="commission-tip">按业务员编码配置,比例输入百分数,例如 5 表示 5%。</span>
|
||||||
|
<el-button type="primary" plain icon="Plus" v-hasPermi="['report:profit:commission:save']" @click="openRateForm()">新增比例</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table v-loading="commissionLoading" :data="commissionRates" border stripe>
|
||||||
|
<el-table-column prop="belongSalerCode" label="业务员编码" min-width="150" />
|
||||||
|
<el-table-column prop="commissionRate" label="提成比例" width="130" align="right"><template #default="{ row }">{{ commissionRateFormat(row.commissionRate) }}</template></el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="90" align="center"><template #default="{ row }">{{ row.status === '0' ? '正常' : '停用' }}</template></el-table-column>
|
||||||
|
<el-table-column label="操作" width="130" fixed="right" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" v-hasPermi="['report:profit:commission:save']" @click="openRateForm(row)">编辑</el-button>
|
||||||
|
<el-button link type="danger" v-hasPermi="['report:profit:commission:remove']" @click="handleDeleteRate(row)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty><el-empty description="尚未配置业务员提成比例" /></template>
|
||||||
|
</el-table>
|
||||||
|
<template #footer><el-button @click="commissionDialogVisible = false">关闭</el-button></template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="rateFormVisible" :title="rateForm.rateId ? '编辑提成比例' : '新增提成比例'" width="520px" append-to-body>
|
||||||
|
<el-form ref="rateFormRef" :model="rateForm" :rules="rateRules" label-width="110px">
|
||||||
|
<el-form-item label="业务员编码" prop="belongSalerCode"><el-input v-model="rateForm.belongSalerCode" maxlength="64" :disabled="!!rateForm.rateId" /></el-form-item>
|
||||||
|
<el-form-item label="提成比例(%)" prop="commissionRate"><el-input-number v-model="rateForm.commissionRate" :min="0" :max="100" :precision="4" :step="0.01" controls-position="right" style="width: 100%" /></el-form-item>
|
||||||
|
<el-form-item label="状态" prop="status"><el-radio-group v-model="rateForm.status"><el-radio value="0">正常</el-radio><el-radio value="1">停用</el-radio></el-radio-group></el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark"><el-input v-model="rateForm.remark" type="textarea" :rows="2" maxlength="500" /></el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" :loading="rateSaving" @click="submitRateForm">保存</el-button>
|
||||||
|
<el-button @click="rateFormVisible = false">取消</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="ProfitReport">
|
||||||
|
import { deleteProfitCommissionRate, exportProfitOrders, exportProfitProducts, listProfitCommissionRates, listProfitOrders, listProfitProducts, listProfitSummary, saveProfitCommissionRate } from '@/api/report/profit'
|
||||||
|
import { parseTime } from '@/utils/bms'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
const dimension = ref('product')
|
||||||
|
const multiValueFilterLimit = 2000
|
||||||
|
const dateRange = ref([formatLocalDate(-30), formatLocalDate(0)])
|
||||||
|
const query = reactive({
|
||||||
|
orderNo: '',
|
||||||
|
customerCode: '',
|
||||||
|
customerName: '',
|
||||||
|
belongSalerCode: '',
|
||||||
|
productCode: ''
|
||||||
|
})
|
||||||
|
const summaryList = ref([])
|
||||||
|
const rows = ref([])
|
||||||
|
let rowsRequestVersion = 0
|
||||||
|
const loading = ref(false)
|
||||||
|
const exporting = ref(false)
|
||||||
|
const pageNum = ref(1)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const total = ref(0)
|
||||||
|
const orderListDialogVisible = ref(false)
|
||||||
|
const orderListTitle = ref('订单列表')
|
||||||
|
const orderListLoading = ref(false)
|
||||||
|
const orderListRows = ref([])
|
||||||
|
const orderListTotal = ref(0)
|
||||||
|
const orderListPageNum = ref(1)
|
||||||
|
const orderListPageSize = ref(10)
|
||||||
|
const orderListContext = ref(null)
|
||||||
|
let orderListRequestVersion = 0
|
||||||
|
const commissionDialogVisible = ref(false)
|
||||||
|
const commissionLoading = ref(false)
|
||||||
|
const commissionRates = ref([])
|
||||||
|
const rateFormVisible = ref(false)
|
||||||
|
const rateSaving = ref(false)
|
||||||
|
const rateFormRef = ref()
|
||||||
|
const rateForm = reactive(createRateForm())
|
||||||
|
const rateRules = {
|
||||||
|
belongSalerCode: [{ required: true, message: '请输入业务员编码', trigger: 'blur' }],
|
||||||
|
commissionRate: [{ required: true, message: '请输入提成比例', trigger: 'change' }],
|
||||||
|
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRateForm(row = {}) {
|
||||||
|
return {
|
||||||
|
rateId: row.rateId || undefined,
|
||||||
|
belongSalerCode: row.belongSalerCode || '',
|
||||||
|
belongSaler: row.belongSaler || '',
|
||||||
|
commissionRate: row.commissionRate === 0 ? 0 : (row.commissionRate || undefined),
|
||||||
|
status: row.status || '0',
|
||||||
|
remark: row.remark || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLocalDate(offset) {
|
||||||
|
const date = new Date()
|
||||||
|
date.setDate(date.getDate() + offset)
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
return `${year}-${month}-${day}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildQuery(paged = true) {
|
||||||
|
const params = { ...query }
|
||||||
|
if (dateRange.value?.[0]) params.beginBizDate = dateRange.value[0] + ' 00:00:00'
|
||||||
|
if (dateRange.value?.[1]) params.endBizDate = dateRange.value[1] + ' 23:59:59'
|
||||||
|
if (paged) {
|
||||||
|
params.pageNum = pageNum.value
|
||||||
|
params.pageSize = pageSize.value
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateMultiValueFilters(showMessage = true) {
|
||||||
|
const filters = [
|
||||||
|
['orderNo', '订单号'],
|
||||||
|
['customerCode', '客户代码'],
|
||||||
|
['customerName', '客户名称']
|
||||||
|
]
|
||||||
|
for (const [key, label] of filters) {
|
||||||
|
const values = new Set(String(query[key] || '').split(/[\s,,;;]+/).map(value => value.trim()).filter(Boolean))
|
||||||
|
if (values.size > multiValueFilterLimit) {
|
||||||
|
if (showMessage) ElMessage.warning(`${label}最多输入 ${multiValueFilterLimit} 个不重复值`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSummary() {
|
||||||
|
if (!validateMultiValueFilters(false)) return Promise.resolve()
|
||||||
|
return listProfitSummary(buildQuery(false)).then(res => {
|
||||||
|
summaryList.value = res.data || []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadRows() {
|
||||||
|
if (!validateMultiValueFilters()) return Promise.resolve()
|
||||||
|
const requestVersion = ++rowsRequestVersion
|
||||||
|
const requestDimension = dimension.value
|
||||||
|
loading.value = true
|
||||||
|
const api = requestDimension === 'product' ? listProfitProducts : listProfitOrders
|
||||||
|
return api(buildQuery()).then(res => {
|
||||||
|
if (requestVersion !== rowsRequestVersion || requestDimension !== dimension.value) return
|
||||||
|
rows.value = res.rows || []
|
||||||
|
total.value = res.total || 0
|
||||||
|
}).finally(() => {
|
||||||
|
if (requestVersion === rowsRequestVersion) loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
if (!validateMultiValueFilters()) return
|
||||||
|
pageNum.value = 1
|
||||||
|
Promise.all([loadSummary(), loadRows()])
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReset() {
|
||||||
|
Object.keys(query).forEach(key => { query[key] = '' })
|
||||||
|
dateRange.value = [formatLocalDate(-30), formatLocalDate(0)]
|
||||||
|
handleSearch()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDimensionChange() {
|
||||||
|
pageNum.value = 1
|
||||||
|
rows.value = []
|
||||||
|
total.value = 0
|
||||||
|
loadRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openOrderList(row, detailType, label) {
|
||||||
|
if (!validateMultiValueFilters()) return
|
||||||
|
const count = detailType === 'ALL' ? row.orderCount : detailType === 'COMMISSION_MISSING' ? row.commissionMissingOrderCount : row.incompleteOrderCount
|
||||||
|
if (!count) return
|
||||||
|
orderListTitle.value = `${label}订单列表`
|
||||||
|
orderListContext.value = {
|
||||||
|
productDrilldown: true,
|
||||||
|
detailType,
|
||||||
|
productCode: row.productCode,
|
||||||
|
countryCode: row.countryCode,
|
||||||
|
currency: row.currency
|
||||||
|
}
|
||||||
|
orderListPageNum.value = 1
|
||||||
|
orderListTotal.value = 0
|
||||||
|
orderListRows.value = []
|
||||||
|
orderListDialogVisible.value = true
|
||||||
|
loadOrderList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadOrderList() {
|
||||||
|
if (!orderListContext.value) return
|
||||||
|
if (!validateMultiValueFilters()) return
|
||||||
|
const requestVersion = ++orderListRequestVersion
|
||||||
|
orderListLoading.value = true
|
||||||
|
const params = {
|
||||||
|
...buildQuery(false),
|
||||||
|
...orderListContext.value,
|
||||||
|
pageNum: orderListPageNum.value,
|
||||||
|
pageSize: orderListPageSize.value
|
||||||
|
}
|
||||||
|
listProfitOrders(params).then(res => {
|
||||||
|
if (requestVersion !== orderListRequestVersion) return
|
||||||
|
orderListRows.value = res.rows || []
|
||||||
|
orderListTotal.value = res.total || 0
|
||||||
|
}).finally(() => {
|
||||||
|
if (requestVersion === orderListRequestVersion) orderListLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function rowKey(row) {
|
||||||
|
return dimension.value === 'product'
|
||||||
|
? [row.productCode, row.countryCode, row.currency].join('|')
|
||||||
|
: row.orderNo
|
||||||
|
}
|
||||||
|
|
||||||
|
function money(value) {
|
||||||
|
if (value === null || value === undefined || value === '') return '-'
|
||||||
|
const number = Number(value)
|
||||||
|
return Number.isFinite(number) ? number.toFixed(2) : '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function rate(value) {
|
||||||
|
if (value === null || value === undefined || value === '') return '-'
|
||||||
|
const number = Number(value)
|
||||||
|
return Number.isFinite(number) ? number.toFixed(2) + '%' : '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function commissionRateFormat(value) {
|
||||||
|
if (value === null || value === undefined || value === '') return '-'
|
||||||
|
const number = Number(value)
|
||||||
|
return Number.isFinite(number) ? number.toFixed(4) + '%' : '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function arBillStatusLabel(value) {
|
||||||
|
const labels = {
|
||||||
|
UNBILLED: '未出账',
|
||||||
|
PARTIAL: '部分出账',
|
||||||
|
BILLED: '已出账'
|
||||||
|
}
|
||||||
|
return labels[value] || value || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleExport() {
|
||||||
|
if (!validateMultiValueFilters()) return
|
||||||
|
exporting.value = true
|
||||||
|
const exporter = dimension.value === 'product' ? exportProfitProducts : exportProfitOrders
|
||||||
|
exporter(buildQuery(false)).finally(() => { exporting.value = false })
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCommissionRates() {
|
||||||
|
commissionDialogVisible.value = true
|
||||||
|
commissionLoading.value = true
|
||||||
|
listProfitCommissionRates().then(res => {
|
||||||
|
commissionRates.value = res.data || []
|
||||||
|
}).finally(() => {
|
||||||
|
commissionLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRateForm(row) {
|
||||||
|
Object.assign(rateForm, createRateForm(row))
|
||||||
|
rateFormVisible.value = true
|
||||||
|
nextTick(() => rateFormRef.value?.clearValidate())
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitRateForm() {
|
||||||
|
rateFormRef.value.validate(valid => {
|
||||||
|
if (!valid) return
|
||||||
|
rateSaving.value = true
|
||||||
|
saveProfitCommissionRate({ ...rateForm }).then(() => {
|
||||||
|
ElMessage.success('提成比例已保存')
|
||||||
|
rateFormVisible.value = false
|
||||||
|
openCommissionRates()
|
||||||
|
Promise.all([loadSummary(), loadRows()])
|
||||||
|
}).finally(() => {
|
||||||
|
rateSaving.value = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDeleteRate(row) {
|
||||||
|
ElMessageBox.confirm(`确定删除业务员 ${row.belongSalerCode} 的提成比例吗?`, '提示', { type: 'warning' }).then(() => {
|
||||||
|
return deleteProfitCommissionRate(row.rateId)
|
||||||
|
}).then(() => {
|
||||||
|
ElMessage.success('提成比例已删除')
|
||||||
|
openCommissionRates()
|
||||||
|
Promise.all([loadSummary(), loadRows()])
|
||||||
|
}).catch(error => {
|
||||||
|
if (error !== 'cancel' && error !== 'close' && error?.action !== 'cancel' && error?.action !== 'close') {
|
||||||
|
ElMessage.error(error?.message || '删除提成比例失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSearch()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.search-card,
|
||||||
|
.table-card {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card :deep(.el-card__body) {
|
||||||
|
padding: 18px 20px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-item {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-item :deep(.el-form-item__content) {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-control {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-actions-col {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(560px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card :deep(.el-card__header) {
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card :deep(.el-card__body) {
|
||||||
|
padding: 14px 16px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-grid {
|
||||||
|
margin-bottom: -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-grid :deep(.el-col) {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-subtitle {
|
||||||
|
float: right;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-label {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-item {
|
||||||
|
min-height: 68px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--el-fill-color-lighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-value {
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-value small {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profit-value {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-value {
|
||||||
|
color: var(--el-color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-count-link {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-count-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-count-link.is-disabled {
|
||||||
|
cursor: default;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-heading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
border-bottom: 1px solid var(--el-border-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-heading :deep(.el-tabs) {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-heading :deep(.el-tabs__header) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-heading :deep(.el-tabs__nav-wrap::after) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-toolbar {
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
gap: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commission-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commission-tip {
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.summary-list {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card :deep(.el-card__body),
|
||||||
|
.summary-card :deep(.el-card__body) {
|
||||||
|
padding-right: 12px;
|
||||||
|
padding-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-heading {
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-toolbar {
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user