Merge branch 'dev'
# Conflicts: # frontend/components.d.ts
Showing
18 changed files
with
3333 additions
and
81 deletions
| 1 | +package com.apple.erp.controller; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.request.RebateAddReq; | ||
| 4 | +import com.apple.erp.dto.request.RebateQueryReq; | ||
| 5 | +import com.apple.erp.dto.request.RebateUpdateReq; | ||
| 6 | +import com.apple.erp.dto.response.ApiRes; | ||
| 7 | +import com.apple.erp.dto.response.RebateRes; | ||
| 8 | +import com.apple.erp.entity.Rebate; | ||
| 9 | +import com.apple.erp.service.RebateService; | ||
| 10 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 11 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 12 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 13 | +import lombok.extern.slf4j.Slf4j; | ||
| 14 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 15 | +import org.springframework.validation.annotation.Validated; | ||
| 16 | +import org.springframework.web.bind.annotation.*; | ||
| 17 | + | ||
| 18 | +import javax.validation.Valid; | ||
| 19 | +import java.math.BigDecimal; | ||
| 20 | +import java.util.List; | ||
| 21 | +import java.util.Map; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * 返利台账明细管理控制器 | ||
| 25 | + * | ||
| 26 | + * @author Apple ERP Team | ||
| 27 | + * @version 1.0.0 | ||
| 28 | + * @since 2024-01-01 | ||
| 29 | + */ | ||
| 30 | +@Slf4j | ||
| 31 | +@Tag(name = "返利台账明细管理") | ||
| 32 | +@RestController | ||
| 33 | +@RequestMapping("/api/rebate") | ||
| 34 | +@Validated | ||
| 35 | +public class RebateController { | ||
| 36 | + | ||
| 37 | + @Autowired | ||
| 38 | + private RebateService rebateService; | ||
| 39 | + | ||
| 40 | + @Operation(summary = "分页查询返利明细列表") | ||
| 41 | + @PostMapping("/page") | ||
| 42 | + public ApiRes<IPage<RebateRes>> getRebatePage(@Valid @RequestBody RebateQueryReq queryReq) { | ||
| 43 | + log.info("分页查询返利明细列表,参数:{}", queryReq); | ||
| 44 | + try { | ||
| 45 | + IPage<RebateRes> result = rebateService.getRebatePage(queryReq); | ||
| 46 | + return ApiRes.success(result); | ||
| 47 | + } catch (Exception e) { | ||
| 48 | + log.error("分页查询返利明细列表失败", e); | ||
| 49 | + return ApiRes.error(e.getMessage()); | ||
| 50 | + } | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + @Operation(summary = "根据ID查询返利详情") | ||
| 54 | + @GetMapping("/{rebateId}") | ||
| 55 | + public ApiRes<RebateRes> getRebateById(@PathVariable Long rebateId) { | ||
| 56 | + log.info("查询返利详情,ID:{}", rebateId); | ||
| 57 | + try { | ||
| 58 | + RebateRes result = rebateService.getRebateById(rebateId); | ||
| 59 | + if (result == null) { | ||
| 60 | + return ApiRes.error("返利记录不存在"); | ||
| 61 | + } | ||
| 62 | + return ApiRes.success(result); | ||
| 63 | + } catch (Exception e) { | ||
| 64 | + log.error("查询返利详情失败", e); | ||
| 65 | + return ApiRes.error(e.getMessage()); | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + @Operation(summary = "新增返利") | ||
| 70 | + @PostMapping("/add") | ||
| 71 | + public ApiRes<Void> addRebate(@Valid @RequestBody RebateAddReq addReq) { | ||
| 72 | + log.info("新增返利,参数:{}", addReq); | ||
| 73 | + try { | ||
| 74 | + boolean result = rebateService.addRebate(addReq); | ||
| 75 | + return result ? ApiRes.success("操作成功", null) : ApiRes.error("新增返利失败"); | ||
| 76 | + } catch (Exception e) { | ||
| 77 | + log.error("新增返利失败", e); | ||
| 78 | + return ApiRes.error(e.getMessage()); | ||
| 79 | + } | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + @Operation(summary = "更新返利") | ||
| 83 | + @PostMapping("/update") | ||
| 84 | + public ApiRes<Void> updateRebate(@Valid @RequestBody RebateUpdateReq updateReq) { | ||
| 85 | + log.info("更新返利,参数:{}", updateReq); | ||
| 86 | + try { | ||
| 87 | + boolean result = rebateService.updateRebate(updateReq); | ||
| 88 | + return result ? ApiRes.success("操作成功", null) : ApiRes.error("更新返利失败"); | ||
| 89 | + } catch (Exception e) { | ||
| 90 | + log.error("更新返利失败", e); | ||
| 91 | + return ApiRes.error(e.getMessage()); | ||
| 92 | + } | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + @Operation(summary = "删除返利") | ||
| 96 | + @DeleteMapping("/{rebateId}") | ||
| 97 | + public ApiRes<Void> deleteRebate(@PathVariable Long rebateId) { | ||
| 98 | + log.info("删除返利,ID:{}", rebateId); | ||
| 99 | + try { | ||
| 100 | + boolean result = rebateService.deleteRebate(rebateId); | ||
| 101 | + return result ? ApiRes.success("操作成功", null) : ApiRes.error("删除返利失败"); | ||
| 102 | + } catch (Exception e) { | ||
| 103 | + log.error("删除返利失败", e); | ||
| 104 | + return ApiRes.error(e.getMessage()); | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + @Operation(summary = "批量删除返利") | ||
| 109 | + @PostMapping("/batchDelete") | ||
| 110 | + public ApiRes<Void> batchDeleteRebate(@RequestBody List<Long> rebateIds) { | ||
| 111 | + log.info("批量删除返利,IDs:{}", rebateIds); | ||
| 112 | + try { | ||
| 113 | + boolean result = rebateService.batchDeleteRebate(rebateIds); | ||
| 114 | + return result ? ApiRes.success("操作成功", null) : ApiRes.error("批量删除返利失败"); | ||
| 115 | + } catch (Exception e) { | ||
| 116 | + log.error("批量删除返利失败", e); | ||
| 117 | + return ApiRes.error(e.getMessage()); | ||
| 118 | + } | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + @Operation(summary = "标记返利为已计算") | ||
| 122 | + @PostMapping("/markCalculated/{rebateId}") | ||
| 123 | + public ApiRes<Void> markRebateCalculated(@PathVariable Long rebateId) { | ||
| 124 | + log.info("标记返利为已计算,ID:{}", rebateId); | ||
| 125 | + try { | ||
| 126 | + Rebate rebate = rebateService.getById(rebateId); | ||
| 127 | + if (rebate == null) { | ||
| 128 | + return ApiRes.error("返利记录不存在"); | ||
| 129 | + } | ||
| 130 | + rebate.setCalcFlag(1); | ||
| 131 | + boolean result = rebateService.updateById(rebate); | ||
| 132 | + return result ? ApiRes.success("操作成功", null) : ApiRes.error("标记失败"); | ||
| 133 | + } catch (Exception e) { | ||
| 134 | + log.error("标记返利计算状态失败", e); | ||
| 135 | + return ApiRes.error(e.getMessage()); | ||
| 136 | + } | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + @Operation(summary = "批量标记返利为已计算") | ||
| 140 | + @PostMapping("/batchMarkCalculated") | ||
| 141 | + public ApiRes<Void> batchMarkRebateCalculated(@RequestBody List<Long> rebateIds) { | ||
| 142 | + log.info("批量标记返利为已计算,IDs:{}", rebateIds); | ||
| 143 | + try { | ||
| 144 | + List<Rebate> rebates = rebateService.listByIds(rebateIds); | ||
| 145 | + rebates.forEach(rebate -> rebate.setCalcFlag(1)); | ||
| 146 | + boolean result = rebateService.updateBatchById(rebates); | ||
| 147 | + return result ? ApiRes.success("操作成功", null) : ApiRes.error("批量标记失败"); | ||
| 148 | + } catch (Exception e) { | ||
| 149 | + log.error("批量标记返利计算状态失败", e); | ||
| 150 | + return ApiRes.error(e.getMessage()); | ||
| 151 | + } | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + @Operation(summary = "根据经销商编码统计返利总金额") | ||
| 155 | + @GetMapping("/sumByDealer/{dealerCode}") | ||
| 156 | + public ApiRes<BigDecimal> sumRebateAmountByDealer(@PathVariable String dealerCode, | ||
| 157 | + @RequestParam(required = false) Integer operateType) { | ||
| 158 | + log.info("统计经销商返利总金额,经销商编码:{},操作类型:{}", dealerCode, operateType); | ||
| 159 | + try { | ||
| 160 | + BigDecimal result = rebateService.sumRebateAmountByDealer(dealerCode, operateType); | ||
| 161 | + return ApiRes.success(result); | ||
| 162 | + } catch (Exception e) { | ||
| 163 | + log.error("统计经销商返利总金额失败", e); | ||
| 164 | + return ApiRes.error(e.getMessage()); | ||
| 165 | + } | ||
| 166 | + } | ||
| 167 | + | ||
| 168 | + @Operation(summary = "根据产品编码统计返利总金额") | ||
| 169 | + @GetMapping("/sumByProduct/{productCode}") | ||
| 170 | + public ApiRes<BigDecimal> sumRebateAmountByProduct(@PathVariable String productCode, | ||
| 171 | + @RequestParam(required = false) Integer operateType) { | ||
| 172 | + log.info("统计产品返利总金额,产品编码:{},操作类型:{}", productCode, operateType); | ||
| 173 | + try { | ||
| 174 | + BigDecimal result = rebateService.sumRebateAmountByProduct(productCode, operateType); | ||
| 175 | + return ApiRes.success(result); | ||
| 176 | + } catch (Exception e) { | ||
| 177 | + log.error("统计产品返利总金额失败", e); | ||
| 178 | + return ApiRes.error(e.getMessage()); | ||
| 179 | + } | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + @Operation(summary = "统计各操作类型的返利数量") | ||
| 183 | + @GetMapping("/countByOperateType") | ||
| 184 | + public ApiRes<List<Map<String, Object>>> countRebateByOperateType() { | ||
| 185 | + log.info("统计各操作类型的返利数量"); | ||
| 186 | + try { | ||
| 187 | + List<Map<String, Object>> result = rebateService.countRebateByOperateType(); | ||
| 188 | + return ApiRes.success(result); | ||
| 189 | + } catch (Exception e) { | ||
| 190 | + log.error("统计各操作类型的返利数量失败", e); | ||
| 191 | + return ApiRes.error(e.getMessage()); | ||
| 192 | + } | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + @Operation(summary = "查询未计算的返利明细列表") | ||
| 196 | + @GetMapping("/unCalc") | ||
| 197 | + public ApiRes<List<RebateRes>> getUnCalcRebates() { | ||
| 198 | + log.info("查询未计算的返利明细列表"); | ||
| 199 | + try { | ||
| 200 | + List<RebateRes> result = rebateService.getUnCalcRebates(); | ||
| 201 | + return ApiRes.success(result); | ||
| 202 | + } catch (Exception e) { | ||
| 203 | + log.error("查询未计算的返利明细列表失败", e); | ||
| 204 | + return ApiRes.error(e.getMessage()); | ||
| 205 | + } | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + @Operation(summary = "根据返利编号查询返利") | ||
| 209 | + @GetMapping("/byNo/{rebateNo}") | ||
| 210 | + public ApiRes<RebateRes> getRebateByNo(@PathVariable String rebateNo) { | ||
| 211 | + log.info("根据返利编号查询返利,编号:{}", rebateNo); | ||
| 212 | + try { | ||
| 213 | + RebateRes result = rebateService.getRebateByNo(rebateNo); | ||
| 214 | + if (result == null) { | ||
| 215 | + return ApiRes.error("返利记录不存在"); | ||
| 216 | + } | ||
| 217 | + return ApiRes.success(result); | ||
| 218 | + } catch (Exception e) { | ||
| 219 | + log.error("根据返利编号查询返利失败", e); | ||
| 220 | + return ApiRes.error(e.getMessage()); | ||
| 221 | + } | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + @Operation(summary = "生成返利编号") | ||
| 225 | + @GetMapping("/generateNo") | ||
| 226 | + public ApiRes<String> generateRebateNo() { | ||
| 227 | + log.info("生成返利编号"); | ||
| 228 | + try { | ||
| 229 | + String result = rebateService.generateRebateNo(); | ||
| 230 | + return ApiRes.success(result); | ||
| 231 | + } catch (Exception e) { | ||
| 232 | + log.error("生成返利编号失败", e); | ||
| 233 | + return ApiRes.error(e.getMessage()); | ||
| 234 | + } | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + @Operation(summary = "获取返利月度统计数据") | ||
| 238 | + @GetMapping("/monthlyStats") | ||
| 239 | + public ApiRes<List<Map<String, Object>>> getRebateMonthlyStats(@RequestParam(required = false) Integer year) { | ||
| 240 | + log.info("获取返利月度统计数据,年份:{}", year); | ||
| 241 | + try { | ||
| 242 | + if (year == null) { | ||
| 243 | + year = java.time.LocalDate.now().getYear(); | ||
| 244 | + } | ||
| 245 | + List<Map<String, Object>> result = rebateService.getRebateMonthlyStats(year); | ||
| 246 | + return ApiRes.success(result); | ||
| 247 | + } catch (Exception e) { | ||
| 248 | + log.error("获取返利月度统计数据失败", e); | ||
| 249 | + return ApiRes.error(e.getMessage()); | ||
| 250 | + } | ||
| 251 | + } | ||
| 252 | + | ||
| 253 | + @Operation(summary = "获取返利状态统计数据") | ||
| 254 | + @GetMapping("/statusStats") | ||
| 255 | + public ApiRes<Map<String, Object>> getRebateStatusStats() { | ||
| 256 | + log.info("获取返利状态统计数据"); | ||
| 257 | + try { | ||
| 258 | + Map<String, Object> result = rebateService.getRebateStatusStats(); | ||
| 259 | + return ApiRes.success(result); | ||
| 260 | + } catch (Exception e) { | ||
| 261 | + log.error("获取返利状态统计数据失败", e); | ||
| 262 | + return ApiRes.error(e.getMessage()); | ||
| 263 | + } | ||
| 264 | + } | ||
| 265 | + | ||
| 266 | + @Operation(summary = "获取返利趋势统计数据") | ||
| 267 | + @GetMapping("/trendStats") | ||
| 268 | + public ApiRes<List<Map<String, Object>>> getRebateTrendStats( | ||
| 269 | + @RequestParam(required = false) String startDate, | ||
| 270 | + @RequestParam(required = false) String endDate) { | ||
| 271 | + log.info("获取返利趋势统计数据,开始日期:{},结束日期:{}", startDate, endDate); | ||
| 272 | + try { | ||
| 273 | + List<Map<String, Object>> result = rebateService.getRebateTrendStats(startDate, endDate); | ||
| 274 | + return ApiRes.success(result); | ||
| 275 | + } catch (Exception e) { | ||
| 276 | + log.error("获取返利趋势统计数据失败", e); | ||
| 277 | + return ApiRes.error(e.getMessage()); | ||
| 278 | + } | ||
| 279 | + } | ||
| 280 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.*; | ||
| 6 | +import java.math.BigDecimal; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 返利新增请求DTO | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP | ||
| 12 | + */ | ||
| 13 | +@Data | ||
| 14 | +public class RebateAddReq { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 返利明细编号 | ||
| 18 | + */ | ||
| 19 | + @Size(max = 30, message = "返利明细编号不能超过30个字符") | ||
| 20 | + private String rebateNo; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 关联订单编号 | ||
| 24 | + */ | ||
| 25 | + @NotBlank(message = "关联订单编号不能为空") | ||
| 26 | + @Size(max = 30, message = "订单编号不能超过30个字符") | ||
| 27 | + private String orderNo; | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * 经销商编码 | ||
| 31 | + */ | ||
| 32 | + @NotBlank(message = "经销商编码不能为空") | ||
| 33 | + @Size(max = 20, message = "经销商编码不能超过20个字符") | ||
| 34 | + private String dealerCode; | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 经销商名称 | ||
| 38 | + */ | ||
| 39 | + @NotBlank(message = "经销商名称不能为空") | ||
| 40 | + @Size(max = 100, message = "经销商名称不能超过100个字符") | ||
| 41 | + private String dealerName; | ||
| 42 | + | ||
| 43 | + /** | ||
| 44 | + * 产品编码 | ||
| 45 | + */ | ||
| 46 | + @NotBlank(message = "产品编码不能为空") | ||
| 47 | + @Size(max = 30, message = "产品编码不能超过30个字符") | ||
| 48 | + private String productCode; | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 返利金额 | ||
| 52 | + */ | ||
| 53 | + @NotNull(message = "返利金额不能为空") | ||
| 54 | + @DecimalMin(value = "0", message = "返利金额不能小于0") | ||
| 55 | + private BigDecimal rebateAmount; | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 返利记录日期 | ||
| 59 | + */ | ||
| 60 | + @NotBlank(message = "返利记录日期不能为空") | ||
| 61 | + private String rebateDate; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 操作类型:1-新增,2-扣减 | ||
| 65 | + */ | ||
| 66 | + @NotNull(message = "操作类型不能为空") | ||
| 67 | + @Min(value = 1, message = "操作类型值无效") | ||
| 68 | + @Max(value = 2, message = "操作类型值无效") | ||
| 69 | + private Integer operateType; | ||
| 70 | + | ||
| 71 | + /** | ||
| 72 | + * 返利计算状态:0-未计算,1-已计算 | ||
| 73 | + */ | ||
| 74 | + private Integer calcFlag; | ||
| 75 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * 返利台账明细查询请求DTO | ||
| 7 | + * | ||
| 8 | + * @author Apple ERP Team | ||
| 9 | + * @version 1.0.0 | ||
| 10 | + * @since 2024-01-01 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class RebateQueryReq { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 返利明细编号 | ||
| 17 | + */ | ||
| 18 | + private String rebateNo; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 关联订单编号 | ||
| 22 | + */ | ||
| 23 | + private String orderNo; | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 经销商编码 | ||
| 27 | + */ | ||
| 28 | + private String dealerCode; | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 经销商名称 | ||
| 32 | + */ | ||
| 33 | + private String dealerName; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 产品编码 | ||
| 37 | + */ | ||
| 38 | + private String productCode; | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 操作类型:1-新增,2-扣减 | ||
| 42 | + */ | ||
| 43 | + private Integer operateType; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 返利计算状态:0-未计算,1-已计算 | ||
| 47 | + */ | ||
| 48 | + private Integer calcFlag; | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 返利记录开始日期 | ||
| 52 | + */ | ||
| 53 | + private String rebateStartDate; | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 返利记录结束日期 | ||
| 57 | + */ | ||
| 58 | + private String rebateEndDate; | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * 页码 | ||
| 62 | + */ | ||
| 63 | + private Integer pageNum = 1; | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * 页面大小 | ||
| 67 | + */ | ||
| 68 | + private Integer pageSize = 10; | ||
| 69 | +} |
| 1 | +package com.apple.erp.dto.request; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.*; | ||
| 6 | +import java.math.BigDecimal; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 返利更新请求DTO | ||
| 10 | + * | ||
| 11 | + * @author Apple ERP | ||
| 12 | + */ | ||
| 13 | +@Data | ||
| 14 | +public class RebateUpdateReq { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 返利明细ID | ||
| 18 | + */ | ||
| 19 | + @NotNull(message = "返利明细ID不能为空") | ||
| 20 | + private Long rebateId; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 返利明细编号 | ||
| 24 | + */ | ||
| 25 | + @Size(max = 30, message = "返利明细编号不能超过30个字符") | ||
| 26 | + private String rebateNo; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 关联订单编号 | ||
| 30 | + */ | ||
| 31 | + @NotBlank(message = "关联订单编号不能为空") | ||
| 32 | + @Size(max = 30, message = "订单编号不能超过30个字符") | ||
| 33 | + private String orderNo; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 经销商编码 | ||
| 37 | + */ | ||
| 38 | + @NotBlank(message = "经销商编码不能为空") | ||
| 39 | + @Size(max = 20, message = "经销商编码不能超过20个字符") | ||
| 40 | + private String dealerCode; | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 经销商名称 | ||
| 44 | + */ | ||
| 45 | + @NotBlank(message = "经销商名称不能为空") | ||
| 46 | + @Size(max = 100, message = "经销商名称不能超过100个字符") | ||
| 47 | + private String dealerName; | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 产品编码 | ||
| 51 | + */ | ||
| 52 | + @NotBlank(message = "产品编码不能为空") | ||
| 53 | + @Size(max = 30, message = "产品编码不能超过30个字符") | ||
| 54 | + private String productCode; | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * 返利金额 | ||
| 58 | + */ | ||
| 59 | + @NotNull(message = "返利金额不能为空") | ||
| 60 | + @DecimalMin(value = "0", message = "返利金额不能小于0") | ||
| 61 | + private BigDecimal rebateAmount; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 返利记录日期 | ||
| 65 | + */ | ||
| 66 | + @NotBlank(message = "返利记录日期不能为空") | ||
| 67 | + private String rebateDate; | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * 操作类型:1-新增,2-扣减 | ||
| 71 | + */ | ||
| 72 | + @NotNull(message = "操作类型不能为空") | ||
| 73 | + @Min(value = 1, message = "操作类型值无效") | ||
| 74 | + @Max(value = 2, message = "操作类型值无效") | ||
| 75 | + private Integer operateType; | ||
| 76 | + | ||
| 77 | + /** | ||
| 78 | + * 返利计算状态:0-未计算,1-已计算 | ||
| 79 | + */ | ||
| 80 | + private Integer calcFlag; | ||
| 81 | +} |
| 1 | +package com.apple.erp.dto.response; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import java.math.BigDecimal; | ||
| 6 | +import java.time.LocalDate; | ||
| 7 | +import java.time.LocalDateTime; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 返利响应DTO | ||
| 11 | + * | ||
| 12 | + * @author Apple ERP | ||
| 13 | + */ | ||
| 14 | +@Data | ||
| 15 | +public class RebateRes { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 返利明细ID | ||
| 19 | + */ | ||
| 20 | + private Long rebateId; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 返利明细编号 | ||
| 24 | + */ | ||
| 25 | + private String rebateNo; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 关联订单编号 | ||
| 29 | + */ | ||
| 30 | + private String orderNo; | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 经销商编码 | ||
| 34 | + */ | ||
| 35 | + private String dealerCode; | ||
| 36 | + | ||
| 37 | + /** | ||
| 38 | + * 经销商名称 | ||
| 39 | + */ | ||
| 40 | + private String dealerName; | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 产品编码 | ||
| 44 | + */ | ||
| 45 | + private String productCode; | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * 返利金额 | ||
| 49 | + */ | ||
| 50 | + private BigDecimal rebateAmount; | ||
| 51 | + | ||
| 52 | + /** | ||
| 53 | + * 返利记录日期 | ||
| 54 | + */ | ||
| 55 | + private LocalDate rebateDate; | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 操作类型:1-新增,2-扣减 | ||
| 59 | + */ | ||
| 60 | + private Integer operateType; | ||
| 61 | + | ||
| 62 | + /** | ||
| 63 | + * 操作类型文本 | ||
| 64 | + */ | ||
| 65 | + private String operateTypeText; | ||
| 66 | + | ||
| 67 | + /** | ||
| 68 | + * 返利计算状态:0-未计算,1-已计算 | ||
| 69 | + */ | ||
| 70 | + private Integer calcFlag; | ||
| 71 | + | ||
| 72 | + /** | ||
| 73 | + * 计算状态文本 | ||
| 74 | + */ | ||
| 75 | + private String calcFlagText; | ||
| 76 | + | ||
| 77 | + /** | ||
| 78 | + * 数据上传时间 | ||
| 79 | + */ | ||
| 80 | + private LocalDateTime uploadTime; | ||
| 81 | + | ||
| 82 | + /** | ||
| 83 | + * 创建时间 | ||
| 84 | + */ | ||
| 85 | + private LocalDateTime createTime; | ||
| 86 | + | ||
| 87 | + /** | ||
| 88 | + * 更新时间 | ||
| 89 | + */ | ||
| 90 | + private LocalDateTime updateTime; | ||
| 91 | + | ||
| 92 | + /** | ||
| 93 | + * 创建者 | ||
| 94 | + */ | ||
| 95 | + private String createBy; | ||
| 96 | + | ||
| 97 | + /** | ||
| 98 | + * 更新者 | ||
| 99 | + */ | ||
| 100 | + private String updateBy; | ||
| 101 | +} |
| 1 | +package com.apple.erp.entity; | ||
| 2 | + | ||
| 3 | +import com.baomidou.mybatisplus.annotation.*; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.EqualsAndHashCode; | ||
| 6 | + | ||
| 7 | +import java.math.BigDecimal; | ||
| 8 | +import java.time.LocalDate; | ||
| 9 | +import java.time.LocalDateTime; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 返利实体类 | ||
| 13 | + * | ||
| 14 | + * @author Apple ERP | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@EqualsAndHashCode(callSuper = false) | ||
| 18 | +@TableName("t_rebate_detail") | ||
| 19 | +public class Rebate { | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 返利明细ID | ||
| 23 | + */ | ||
| 24 | + @TableId(value = "rebate_id", type = IdType.AUTO) | ||
| 25 | + private Long rebateId; | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 返利明细编号 | ||
| 29 | + */ | ||
| 30 | + @TableField("rebate_no") | ||
| 31 | + private String rebateNo; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 关联订单编号 | ||
| 35 | + */ | ||
| 36 | + @TableField("order_no") | ||
| 37 | + private String orderNo; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 经销商编码 | ||
| 41 | + */ | ||
| 42 | + @TableField("dealer_code") | ||
| 43 | + private String dealerCode; | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * 经销商名称 | ||
| 47 | + */ | ||
| 48 | + @TableField("dealer_name") | ||
| 49 | + private String dealerName; | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 产品编码 | ||
| 53 | + */ | ||
| 54 | + @TableField("product_code") | ||
| 55 | + private String productCode; | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 返利金额 | ||
| 59 | + */ | ||
| 60 | + @TableField("rebate_amount") | ||
| 61 | + private BigDecimal rebateAmount; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 返利记录日期 | ||
| 65 | + */ | ||
| 66 | + @TableField("rebate_date") | ||
| 67 | + private LocalDate rebateDate; | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * 操作类型:1-新增,2-扣减 | ||
| 71 | + */ | ||
| 72 | + @TableField("operate_type") | ||
| 73 | + private Integer operateType; | ||
| 74 | + | ||
| 75 | + /** | ||
| 76 | + * 返利计算状态:0-未计算,1-已计算 | ||
| 77 | + */ | ||
| 78 | + @TableField("calc_flag") | ||
| 79 | + private Integer calcFlag; | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * 数据上传时间 | ||
| 83 | + */ | ||
| 84 | + @TableField("upload_time") | ||
| 85 | + private LocalDateTime uploadTime; | ||
| 86 | + | ||
| 87 | + /** | ||
| 88 | + * 创建时间 | ||
| 89 | + */ | ||
| 90 | + @TableField(value = "create_time", fill = FieldFill.INSERT) | ||
| 91 | + private LocalDateTime createTime; | ||
| 92 | + | ||
| 93 | + /** | ||
| 94 | + * 更新时间 | ||
| 95 | + */ | ||
| 96 | + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) | ||
| 97 | + private LocalDateTime updateTime; | ||
| 98 | + | ||
| 99 | + /** | ||
| 100 | + * 创建者 | ||
| 101 | + */ | ||
| 102 | + @TableField(value = "create_by", fill = FieldFill.INSERT) | ||
| 103 | + private String createBy; | ||
| 104 | + | ||
| 105 | + /** | ||
| 106 | + * 更新者 | ||
| 107 | + */ | ||
| 108 | + @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE) | ||
| 109 | + private String updateBy; | ||
| 110 | + | ||
| 111 | + /** | ||
| 112 | + * 删除标志:0-存在,2-删除 | ||
| 113 | + */ | ||
| 114 | + @TableField("del_flag") | ||
| 115 | + @TableLogic(value = "0", delval = "2") | ||
| 116 | + private String delFlag; | ||
| 117 | +} |
| 1 | +package com.apple.erp.mapper; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.entity.Rebate; | ||
| 4 | +import com.baomidou.mybatisplus.core.mapper.BaseMapper; | ||
| 5 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 6 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||
| 7 | +import org.apache.ibatis.annotations.Mapper; | ||
| 8 | +import org.apache.ibatis.annotations.Param; | ||
| 9 | + | ||
| 10 | +import java.math.BigDecimal; | ||
| 11 | +import java.util.List; | ||
| 12 | +import java.util.Map; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * 返利台账明细Mapper接口 | ||
| 16 | + * | ||
| 17 | + * @author Apple ERP Team | ||
| 18 | + * @version 1.0.0 | ||
| 19 | + * @since 2024-01-01 | ||
| 20 | + */ | ||
| 21 | +@Mapper | ||
| 22 | +public interface RebateMapper extends BaseMapper<Rebate> { | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 分页查询返利明细列表 | ||
| 26 | + * | ||
| 27 | + * @param page 分页参数 | ||
| 28 | + * @param params 查询参数 | ||
| 29 | + * @return 返利明细分页列表 | ||
| 30 | + */ | ||
| 31 | + IPage<Rebate> selectRebatePage(Page<Rebate> page, @Param("params") Map<String, Object> params); | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 根据经销商编码统计返利总金额 | ||
| 35 | + * | ||
| 36 | + * @param dealerCode 经销商编码 | ||
| 37 | + * @param operateType 操作类型 | ||
| 38 | + * @return 返利总金额 | ||
| 39 | + */ | ||
| 40 | + BigDecimal sumRebateAmountByDealer(@Param("dealerCode") String dealerCode, @Param("operateType") Integer operateType); | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 根据产品编码统计返利总金额 | ||
| 44 | + * | ||
| 45 | + * @param productCode 产品编码 | ||
| 46 | + * @param operateType 操作类型 | ||
| 47 | + * @return 返利总金额 | ||
| 48 | + */ | ||
| 49 | + BigDecimal sumRebateAmountByProduct(@Param("productCode") String productCode, @Param("operateType") Integer operateType); | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * 统计各操作类型的返利数量 | ||
| 53 | + * | ||
| 54 | + * @return 操作类型统计 | ||
| 55 | + */ | ||
| 56 | + List<Map<String, Object>> countRebateByOperateType(); | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 查询未计算的返利明细列表 | ||
| 60 | + * | ||
| 61 | + * @return 未计算返利明细列表 | ||
| 62 | + */ | ||
| 63 | + List<Rebate> selectUnCalcRebates(); | ||
| 64 | + | ||
| 65 | + /** | ||
| 66 | + * 根据返利编号查询返利明细 | ||
| 67 | + * | ||
| 68 | + * @param rebateNo 返利编号 | ||
| 69 | + * @return 返利明细信息 | ||
| 70 | + */ | ||
| 71 | + Rebate selectByRebateNo(@Param("rebateNo") String rebateNo); | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 获取返利月度统计数据 | ||
| 75 | + * | ||
| 76 | + * @param year 年份 | ||
| 77 | + * @return 月度统计数据 | ||
| 78 | + */ | ||
| 79 | + List<Map<String, Object>> getRebateMonthlyStats(@Param("year") Integer year); | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * 获取返利状态统计数据 | ||
| 83 | + * | ||
| 84 | + * @return 状态统计数据 | ||
| 85 | + */ | ||
| 86 | + Map<String, Object> getRebateStatusStats(); | ||
| 87 | + | ||
| 88 | + /** | ||
| 89 | + * 获取返利趋势统计数据 | ||
| 90 | + * | ||
| 91 | + * @param startDate 开始日期 | ||
| 92 | + * @param endDate 结束日期 | ||
| 93 | + * @return 趋势统计数据 | ||
| 94 | + */ | ||
| 95 | + List<Map<String, Object>> getRebateTrendStats(@Param("startDate") String startDate, @Param("endDate") String endDate); | ||
| 96 | +} |
| 1 | +package com.apple.erp.service; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.request.RebateAddReq; | ||
| 4 | +import com.apple.erp.dto.request.RebateQueryReq; | ||
| 5 | +import com.apple.erp.dto.request.RebateUpdateReq; | ||
| 6 | +import com.apple.erp.dto.response.RebateRes; | ||
| 7 | +import com.apple.erp.entity.Rebate; | ||
| 8 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 9 | +import com.baomidou.mybatisplus.extension.service.IService; | ||
| 10 | + | ||
| 11 | +import java.math.BigDecimal; | ||
| 12 | +import java.util.List; | ||
| 13 | +import java.util.Map; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 返利台账明细服务接口 | ||
| 17 | + * | ||
| 18 | + * @author Apple ERP Team | ||
| 19 | + * @version 1.0.0 | ||
| 20 | + * @since 2024-01-01 | ||
| 21 | + */ | ||
| 22 | +public interface RebateService extends IService<Rebate> { | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 分页查询返利明细列表 | ||
| 26 | + * | ||
| 27 | + * @param queryReq 查询请求 | ||
| 28 | + * @return 返利明细分页列表 | ||
| 29 | + */ | ||
| 30 | + IPage<RebateRes> getRebatePage(RebateQueryReq queryReq); | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 根据ID查询返利明细详情 | ||
| 34 | + * | ||
| 35 | + * @param rebateId 返利明细ID | ||
| 36 | + * @return 返利明细详情 | ||
| 37 | + */ | ||
| 38 | + RebateRes getRebateById(Long rebateId); | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 新增返利明细 | ||
| 42 | + * | ||
| 43 | + * @param addReq 新增请求 | ||
| 44 | + * @return 是否成功 | ||
| 45 | + */ | ||
| 46 | + boolean addRebate(RebateAddReq addReq); | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 更新返利明细 | ||
| 50 | + * | ||
| 51 | + * @param updateReq 更新请求 | ||
| 52 | + * @return 是否成功 | ||
| 53 | + */ | ||
| 54 | + boolean updateRebate(RebateUpdateReq updateReq); | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * 删除返利明细 | ||
| 58 | + * | ||
| 59 | + * @param rebateId 返利明细ID | ||
| 60 | + * @return 是否成功 | ||
| 61 | + */ | ||
| 62 | + boolean deleteRebate(Long rebateId); | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 批量删除返利明细 | ||
| 66 | + * | ||
| 67 | + * @param rebateIds 返利明细ID列表 | ||
| 68 | + * @return 是否成功 | ||
| 69 | + */ | ||
| 70 | + boolean batchDeleteRebate(List<Long> rebateIds); | ||
| 71 | + | ||
| 72 | + /** | ||
| 73 | + * 根据经销商编码统计返利总金额 | ||
| 74 | + * | ||
| 75 | + * @param dealerCode 经销商编码 | ||
| 76 | + * @param operateType 操作类型(可选) | ||
| 77 | + * @return 返利总金额 | ||
| 78 | + */ | ||
| 79 | + BigDecimal sumRebateAmountByDealer(String dealerCode, Integer operateType); | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * 根据产品编码统计返利总金额 | ||
| 83 | + * | ||
| 84 | + * @param productCode 产品编码 | ||
| 85 | + * @param operateType 操作类型(可选) | ||
| 86 | + * @return 返利总金额 | ||
| 87 | + */ | ||
| 88 | + BigDecimal sumRebateAmountByProduct(String productCode, Integer operateType); | ||
| 89 | + | ||
| 90 | + /** | ||
| 91 | + * 统计各操作类型的返利数量 | ||
| 92 | + * | ||
| 93 | + * @return 操作类型统计 | ||
| 94 | + */ | ||
| 95 | + List<Map<String, Object>> countRebateByOperateType(); | ||
| 96 | + | ||
| 97 | + /** | ||
| 98 | + * 查询未计算的返利明细列表 | ||
| 99 | + * | ||
| 100 | + * @return 未计算返利明细列表 | ||
| 101 | + */ | ||
| 102 | + List<RebateRes> getUnCalcRebates(); | ||
| 103 | + | ||
| 104 | + /** | ||
| 105 | + * 根据返利编号查询返利明细 | ||
| 106 | + * | ||
| 107 | + * @param rebateNo 返利编号 | ||
| 108 | + * @return 返利明细信息 | ||
| 109 | + */ | ||
| 110 | + RebateRes getRebateByNo(String rebateNo); | ||
| 111 | + | ||
| 112 | + /** | ||
| 113 | + * 生成返利编号 | ||
| 114 | + * | ||
| 115 | + * @return 返利编号 | ||
| 116 | + */ | ||
| 117 | + String generateRebateNo(); | ||
| 118 | + | ||
| 119 | + /** | ||
| 120 | + * 获取返利月度统计数据 | ||
| 121 | + * | ||
| 122 | + * @param year 年份 | ||
| 123 | + * @return 月度统计数据 | ||
| 124 | + */ | ||
| 125 | + List<Map<String, Object>> getRebateMonthlyStats(Integer year); | ||
| 126 | + | ||
| 127 | + /** | ||
| 128 | + * 获取返利状态统计数据 | ||
| 129 | + * | ||
| 130 | + * @return 状态统计数据 | ||
| 131 | + */ | ||
| 132 | + Map<String, Object> getRebateStatusStats(); | ||
| 133 | + | ||
| 134 | + /** | ||
| 135 | + * 获取返利趋势统计数据 | ||
| 136 | + * | ||
| 137 | + * @param startDate 开始日期 | ||
| 138 | + * @param endDate 结束日期 | ||
| 139 | + * @return 趋势统计数据 | ||
| 140 | + */ | ||
| 141 | + List<Map<String, Object>> getRebateTrendStats(String startDate, String endDate); | ||
| 142 | +} |
| 1 | +package com.apple.erp.service.impl; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.request.RebateAddReq; | ||
| 4 | +import com.apple.erp.dto.request.RebateQueryReq; | ||
| 5 | +import com.apple.erp.dto.request.RebateUpdateReq; | ||
| 6 | +import com.apple.erp.dto.response.RebateRes; | ||
| 7 | +import com.apple.erp.entity.Rebate; | ||
| 8 | +import com.apple.erp.mapper.RebateMapper; | ||
| 9 | +import com.apple.erp.service.RebateService; | ||
| 10 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||
| 11 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 12 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||
| 13 | +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||
| 14 | +import lombok.extern.slf4j.Slf4j; | ||
| 15 | +import org.springframework.beans.BeanUtils; | ||
| 16 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 17 | +import org.springframework.stereotype.Service; | ||
| 18 | +import org.springframework.transaction.annotation.Transactional; | ||
| 19 | + | ||
| 20 | +import java.math.BigDecimal; | ||
| 21 | +import java.time.LocalDate; | ||
| 22 | +import java.time.LocalDateTime; | ||
| 23 | +import java.time.format.DateTimeFormatter; | ||
| 24 | +import java.util.*; | ||
| 25 | +import java.util.stream.Collectors; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * 返利台账明细服务实现类 | ||
| 29 | + * | ||
| 30 | + * @author Apple ERP Team | ||
| 31 | + * @version 1.0.0 | ||
| 32 | + * @since 2024-01-01 | ||
| 33 | + */ | ||
| 34 | +@Slf4j | ||
| 35 | +@Service | ||
| 36 | +public class RebateServiceImpl extends ServiceImpl<RebateMapper, Rebate> implements RebateService { | ||
| 37 | + | ||
| 38 | + @Autowired | ||
| 39 | + private RebateMapper rebateMapper; | ||
| 40 | + | ||
| 41 | + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); | ||
| 42 | + | ||
| 43 | + @Override | ||
| 44 | + public IPage<RebateRes> getRebatePage(RebateQueryReq queryReq) { | ||
| 45 | + // 构建查询参数 | ||
| 46 | + Map<String, Object> params = buildQueryParams(queryReq); | ||
| 47 | + | ||
| 48 | + // 创建分页对象 | ||
| 49 | + Page<Rebate> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize()); | ||
| 50 | + | ||
| 51 | + // 分页查询 | ||
| 52 | + IPage<Rebate> rebatePage = rebateMapper.selectRebatePage(page, params); | ||
| 53 | + | ||
| 54 | + // 转换为响应DTO | ||
| 55 | + Page<RebateRes> resultPage = new Page<>(rebatePage.getCurrent(), rebatePage.getSize(), rebatePage.getTotal()); | ||
| 56 | + List<RebateRes> rebateResList = rebatePage.getRecords().stream() | ||
| 57 | + .map(this::convertToRebateRes) | ||
| 58 | + .collect(Collectors.toList()); | ||
| 59 | + resultPage.setRecords(rebateResList); | ||
| 60 | + | ||
| 61 | + return resultPage; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + @Override | ||
| 65 | + public RebateRes getRebateById(Long rebateId) { | ||
| 66 | + Rebate rebate = getById(rebateId); | ||
| 67 | + return rebate != null ? convertToRebateRes(rebate) : null; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Override | ||
| 71 | + @Transactional(rollbackFor = Exception.class) | ||
| 72 | + public boolean addRebate(RebateAddReq addReq) { | ||
| 73 | + Rebate rebate = new Rebate(); | ||
| 74 | + BeanUtils.copyProperties(addReq, rebate); | ||
| 75 | + | ||
| 76 | + // 设置返利编号(如果未提供) | ||
| 77 | + if (addReq.getRebateNo() == null || addReq.getRebateNo().isEmpty()) { | ||
| 78 | + rebate.setRebateNo(generateRebateNo()); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + // 转换日期字符串 | ||
| 82 | + if (addReq.getRebateDate() != null && !addReq.getRebateDate().isEmpty()) { | ||
| 83 | + rebate.setRebateDate(LocalDate.parse(addReq.getRebateDate(), DATE_FORMATTER)); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + // 设置上传时间 | ||
| 87 | + rebate.setUploadTime(LocalDateTime.now()); | ||
| 88 | + | ||
| 89 | + // 默认计算状态为未计算 | ||
| 90 | + if (rebate.getCalcFlag() == null) { | ||
| 91 | + rebate.setCalcFlag(0); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + return save(rebate); | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + @Override | ||
| 98 | + @Transactional(rollbackFor = Exception.class) | ||
| 99 | + public boolean updateRebate(RebateUpdateReq updateReq) { | ||
| 100 | + Rebate rebate = getById(updateReq.getRebateId()); | ||
| 101 | + if (rebate == null) { | ||
| 102 | + throw new RuntimeException("返利记录不存在"); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + BeanUtils.copyProperties(updateReq, rebate); | ||
| 106 | + | ||
| 107 | + // 转换日期字符串 | ||
| 108 | + if (updateReq.getRebateDate() != null && !updateReq.getRebateDate().isEmpty()) { | ||
| 109 | + rebate.setRebateDate(LocalDate.parse(updateReq.getRebateDate(), DATE_FORMATTER)); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + return updateById(rebate); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + @Override | ||
| 116 | + @Transactional(rollbackFor = Exception.class) | ||
| 117 | + public boolean deleteRebate(Long rebateId) { | ||
| 118 | + return removeById(rebateId); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + @Override | ||
| 122 | + @Transactional(rollbackFor = Exception.class) | ||
| 123 | + public boolean batchDeleteRebate(List<Long> rebateIds) { | ||
| 124 | + return removeByIds(rebateIds); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + @Override | ||
| 128 | + public BigDecimal sumRebateAmountByDealer(String dealerCode, Integer operateType) { | ||
| 129 | + BigDecimal amount = rebateMapper.sumRebateAmountByDealer(dealerCode, operateType); | ||
| 130 | + return amount != null ? amount : BigDecimal.ZERO; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + @Override | ||
| 134 | + public BigDecimal sumRebateAmountByProduct(String productCode, Integer operateType) { | ||
| 135 | + BigDecimal amount = rebateMapper.sumRebateAmountByProduct(productCode, operateType); | ||
| 136 | + return amount != null ? amount : BigDecimal.ZERO; | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + @Override | ||
| 140 | + public List<Map<String, Object>> countRebateByOperateType() { | ||
| 141 | + return rebateMapper.countRebateByOperateType(); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + @Override | ||
| 145 | + public List<RebateRes> getUnCalcRebates() { | ||
| 146 | + List<Rebate> rebates = rebateMapper.selectUnCalcRebates(); | ||
| 147 | + return rebates.stream() | ||
| 148 | + .map(this::convertToRebateRes) | ||
| 149 | + .collect(Collectors.toList()); | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + @Override | ||
| 153 | + public RebateRes getRebateByNo(String rebateNo) { | ||
| 154 | + Rebate rebate = rebateMapper.selectByRebateNo(rebateNo); | ||
| 155 | + return rebate != null ? convertToRebateRes(rebate) : null; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + @Override | ||
| 159 | + public String generateRebateNo() { | ||
| 160 | + String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); | ||
| 161 | + String prefix = "RB" + dateStr; | ||
| 162 | + | ||
| 163 | + // 查询当天已有的返利编号数量 | ||
| 164 | + LambdaQueryWrapper<Rebate> wrapper = new LambdaQueryWrapper<>(); | ||
| 165 | + wrapper.likeRight(Rebate::getRebateNo, prefix); | ||
| 166 | + long count = count(wrapper); | ||
| 167 | + | ||
| 168 | + // 生成4位序号 | ||
| 169 | + String sequence = String.format("%04d", count + 1); | ||
| 170 | + | ||
| 171 | + return prefix + sequence; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + /** | ||
| 175 | + * 构建查询参数 | ||
| 176 | + */ | ||
| 177 | + private Map<String, Object> buildQueryParams(RebateQueryReq queryReq) { | ||
| 178 | + Map<String, Object> params = new HashMap<>(); | ||
| 179 | + | ||
| 180 | + if (queryReq.getRebateNo() != null && !queryReq.getRebateNo().isEmpty()) { | ||
| 181 | + params.put("rebateNo", queryReq.getRebateNo()); | ||
| 182 | + } | ||
| 183 | + if (queryReq.getOrderNo() != null && !queryReq.getOrderNo().isEmpty()) { | ||
| 184 | + params.put("orderNo", queryReq.getOrderNo()); | ||
| 185 | + } | ||
| 186 | + if (queryReq.getDealerCode() != null && !queryReq.getDealerCode().isEmpty()) { | ||
| 187 | + params.put("dealerCode", queryReq.getDealerCode()); | ||
| 188 | + } | ||
| 189 | + if (queryReq.getDealerName() != null && !queryReq.getDealerName().isEmpty()) { | ||
| 190 | + params.put("dealerName", queryReq.getDealerName()); | ||
| 191 | + } | ||
| 192 | + if (queryReq.getProductCode() != null && !queryReq.getProductCode().isEmpty()) { | ||
| 193 | + params.put("productCode", queryReq.getProductCode()); | ||
| 194 | + } | ||
| 195 | + if (queryReq.getOperateType() != null) { | ||
| 196 | + params.put("operateType", queryReq.getOperateType()); | ||
| 197 | + } | ||
| 198 | + if (queryReq.getCalcFlag() != null) { | ||
| 199 | + params.put("calcFlag", queryReq.getCalcFlag()); | ||
| 200 | + } | ||
| 201 | + if (queryReq.getRebateStartDate() != null && !queryReq.getRebateStartDate().isEmpty()) { | ||
| 202 | + params.put("rebateStartDate", queryReq.getRebateStartDate()); | ||
| 203 | + } | ||
| 204 | + if (queryReq.getRebateEndDate() != null && !queryReq.getRebateEndDate().isEmpty()) { | ||
| 205 | + params.put("rebateEndDate", queryReq.getRebateEndDate()); | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + return params; | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + /** | ||
| 212 | + * 转换为响应DTO | ||
| 213 | + */ | ||
| 214 | + private RebateRes convertToRebateRes(Rebate rebate) { | ||
| 215 | + RebateRes rebateRes = new RebateRes(); | ||
| 216 | + BeanUtils.copyProperties(rebate, rebateRes); | ||
| 217 | + | ||
| 218 | + // 设置操作类型文本 | ||
| 219 | + rebateRes.setOperateTypeText(getOperateTypeText(rebate.getOperateType())); | ||
| 220 | + | ||
| 221 | + // 设置计算状态文本 | ||
| 222 | + rebateRes.setCalcFlagText(getCalcFlagText(rebate.getCalcFlag())); | ||
| 223 | + | ||
| 224 | + return rebateRes; | ||
| 225 | + } | ||
| 226 | + | ||
| 227 | + /** | ||
| 228 | + * 获取操作类型文本 | ||
| 229 | + */ | ||
| 230 | + private String getOperateTypeText(Integer operateType) { | ||
| 231 | + if (operateType == null) { | ||
| 232 | + return ""; | ||
| 233 | + } | ||
| 234 | + switch (operateType) { | ||
| 235 | + case 1: | ||
| 236 | + return "新增"; | ||
| 237 | + case 2: | ||
| 238 | + return "扣减"; | ||
| 239 | + default: | ||
| 240 | + return "未知"; | ||
| 241 | + } | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + /** | ||
| 245 | + * 获取计算状态文本 | ||
| 246 | + */ | ||
| 247 | + private String getCalcFlagText(Integer calcFlag) { | ||
| 248 | + if (calcFlag == null) { | ||
| 249 | + return ""; | ||
| 250 | + } | ||
| 251 | + switch (calcFlag) { | ||
| 252 | + case 0: | ||
| 253 | + return "未计算"; | ||
| 254 | + case 1: | ||
| 255 | + return "已计算"; | ||
| 256 | + default: | ||
| 257 | + return "未知"; | ||
| 258 | + } | ||
| 259 | + } | ||
| 260 | + | ||
| 261 | + @Override | ||
| 262 | + public List<Map<String, Object>> getRebateMonthlyStats(Integer year) { | ||
| 263 | + log.info("获取返利月度统计数据,年份:{}", year); | ||
| 264 | + return rebateMapper.getRebateMonthlyStats(year); | ||
| 265 | + } | ||
| 266 | + | ||
| 267 | + @Override | ||
| 268 | + public Map<String, Object> getRebateStatusStats() { | ||
| 269 | + log.info("获取返利状态统计数据"); | ||
| 270 | + return rebateMapper.getRebateStatusStats(); | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + @Override | ||
| 274 | + public List<Map<String, Object>> getRebateTrendStats(String startDate, String endDate) { | ||
| 275 | + log.info("获取返利趋势统计数据,开始日期:{},结束日期:{}", startDate, endDate); | ||
| 276 | + return rebateMapper.getRebateTrendStats(startDate, endDate); | ||
| 277 | + } | ||
| 278 | +} |
| ... | @@ -17,8 +17,8 @@ spring: | ... | @@ -17,8 +17,8 @@ spring: |
| 17 | type: com.alibaba.druid.pool.DruidDataSource | 17 | type: com.alibaba.druid.pool.DruidDataSource |
| 18 | driver-class-name: com.mysql.cj.jdbc.Driver | 18 | driver-class-name: com.mysql.cj.jdbc.Driver |
| 19 | url: jdbc:mysql://localhost:3306/apple_erp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true | 19 | url: jdbc:mysql://localhost:3306/apple_erp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true |
| 20 | - username: root | 20 | + username: apple_erp_app |
| 21 | - password: MySQL@123 | 21 | + password: StrongPassw0rd! |
| 22 | druid: | 22 | druid: |
| 23 | # 初始连接数 | 23 | # 初始连接数 |
| 24 | initial-size: 5 | 24 | initial-size: 5 |
| ... | @@ -70,7 +70,7 @@ spring: | ... | @@ -70,7 +70,7 @@ spring: |
| 70 | redis: | 70 | redis: |
| 71 | host: localhost | 71 | host: localhost |
| 72 | port: 6379 | 72 | port: 6379 |
| 73 | - password: | 73 | + password: 123456 |
| 74 | database: 0 | 74 | database: 0 |
| 75 | timeout: 10000ms | 75 | timeout: 10000ms |
| 76 | lettuce: | 76 | lettuce: | ... | ... |
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | ||
| 3 | +<mapper namespace="com.apple.erp.mapper.RebateMapper"> | ||
| 4 | + | ||
| 5 | + <!-- 返利明细基础字段 --> | ||
| 6 | + <sql id="baseColumns"> | ||
| 7 | + rebate_id, rebate_no, order_no, dealer_code, dealer_name, product_code, | ||
| 8 | + rebate_amount, rebate_date, operate_type, calc_flag, upload_time, | ||
| 9 | + create_by, create_time, update_by, update_time | ||
| 10 | + </sql> | ||
| 11 | + | ||
| 12 | + <!-- 分页查询返利明细列表 --> | ||
| 13 | + <select id="selectRebatePage" resultType="com.apple.erp.entity.Rebate"> | ||
| 14 | + SELECT | ||
| 15 | + <include refid="baseColumns"/> | ||
| 16 | + FROM t_rebate_detail | ||
| 17 | + WHERE del_flag = '0' | ||
| 18 | + <if test="params.rebateNo != null and params.rebateNo != ''"> | ||
| 19 | + AND rebate_no LIKE CONCAT('%', #{params.rebateNo}, '%') | ||
| 20 | + </if> | ||
| 21 | + <if test="params.orderNo != null and params.orderNo != ''"> | ||
| 22 | + AND order_no LIKE CONCAT('%', #{params.orderNo}, '%') | ||
| 23 | + </if> | ||
| 24 | + <if test="params.dealerCode != null and params.dealerCode != ''"> | ||
| 25 | + AND dealer_code = #{params.dealerCode} | ||
| 26 | + </if> | ||
| 27 | + <if test="params.dealerName != null and params.dealerName != ''"> | ||
| 28 | + AND dealer_name LIKE CONCAT('%', #{params.dealerName}, '%') | ||
| 29 | + </if> | ||
| 30 | + <if test="params.productCode != null and params.productCode != ''"> | ||
| 31 | + AND product_code = #{params.productCode} | ||
| 32 | + </if> | ||
| 33 | + <if test="params.operateType != null"> | ||
| 34 | + AND operate_type = #{params.operateType} | ||
| 35 | + </if> | ||
| 36 | + <if test="params.calcFlag != null"> | ||
| 37 | + AND calc_flag = #{params.calcFlag} | ||
| 38 | + </if> | ||
| 39 | + <if test="params.rebateStartDate != null and params.rebateStartDate != ''"> | ||
| 40 | + AND rebate_date >= #{params.rebateStartDate} | ||
| 41 | + </if> | ||
| 42 | + <if test="params.rebateEndDate != null and params.rebateEndDate != ''"> | ||
| 43 | + AND rebate_date <= #{params.rebateEndDate} | ||
| 44 | + </if> | ||
| 45 | + ORDER BY create_time DESC | ||
| 46 | + </select> | ||
| 47 | + | ||
| 48 | + <!-- 根据经销商编码统计返利总金额 --> | ||
| 49 | + <select id="sumRebateAmountByDealer" resultType="java.math.BigDecimal"> | ||
| 50 | + SELECT COALESCE(SUM(rebate_amount), 0) | ||
| 51 | + FROM t_rebate_detail | ||
| 52 | + WHERE del_flag = '0' | ||
| 53 | + AND dealer_code = #{dealerCode} | ||
| 54 | + <if test="operateType != null"> | ||
| 55 | + AND operate_type = #{operateType} | ||
| 56 | + </if> | ||
| 57 | + </select> | ||
| 58 | + | ||
| 59 | + <!-- 根据产品编码统计返利总金额 --> | ||
| 60 | + <select id="sumRebateAmountByProduct" resultType="java.math.BigDecimal"> | ||
| 61 | + SELECT COALESCE(SUM(rebate_amount), 0) | ||
| 62 | + FROM t_rebate_detail | ||
| 63 | + WHERE del_flag = '0' | ||
| 64 | + AND product_code = #{productCode} | ||
| 65 | + <if test="operateType != null"> | ||
| 66 | + AND operate_type = #{operateType} | ||
| 67 | + </if> | ||
| 68 | + </select> | ||
| 69 | + | ||
| 70 | + <!-- 统计各操作类型的返利数量 --> | ||
| 71 | + <select id="countRebateByOperateType" resultType="java.util.Map"> | ||
| 72 | + SELECT | ||
| 73 | + operate_type, | ||
| 74 | + COUNT(*) as count, | ||
| 75 | + COALESCE(SUM(rebate_amount), 0) as totalAmount | ||
| 76 | + FROM t_rebate_detail | ||
| 77 | + WHERE del_flag = '0' | ||
| 78 | + GROUP BY operate_type | ||
| 79 | + </select> | ||
| 80 | + | ||
| 81 | + <!-- 查询未计算的返利明细列表 --> | ||
| 82 | + <select id="selectUnCalcRebates" resultType="com.apple.erp.entity.Rebate"> | ||
| 83 | + SELECT | ||
| 84 | + <include refid="baseColumns"/> | ||
| 85 | + FROM t_rebate_detail | ||
| 86 | + WHERE del_flag = '0' | ||
| 87 | + AND calc_flag = 0 | ||
| 88 | + ORDER BY create_time ASC | ||
| 89 | + </select> | ||
| 90 | + | ||
| 91 | + <!-- 根据返利编号查询返利明细 --> | ||
| 92 | + <select id="selectByRebateNo" resultType="com.apple.erp.entity.Rebate"> | ||
| 93 | + SELECT | ||
| 94 | + <include refid="baseColumns"/> | ||
| 95 | + FROM t_rebate_detail | ||
| 96 | + WHERE del_flag = '0' | ||
| 97 | + AND rebate_no = #{rebateNo} | ||
| 98 | + </select> | ||
| 99 | + | ||
| 100 | + <!-- 获取返利月度统计数据 --> | ||
| 101 | + <select id="getRebateMonthlyStats" resultType="java.util.Map"> | ||
| 102 | + SELECT | ||
| 103 | + MONTH(rebate_date) as month, | ||
| 104 | + COALESCE(SUM(CASE WHEN operate_type = 1 THEN rebate_amount ELSE -rebate_amount END), 0) as totalAmount, | ||
| 105 | + COALESCE(SUM(CASE WHEN calc_flag = 1 AND operate_type = 1 THEN rebate_amount | ||
| 106 | + WHEN calc_flag = 1 AND operate_type = 2 THEN -rebate_amount | ||
| 107 | + ELSE 0 END), 0) as calculatedAmount, | ||
| 108 | + COUNT(*) as totalCount, | ||
| 109 | + COUNT(CASE WHEN calc_flag = 1 THEN 1 END) as calculatedCount | ||
| 110 | + FROM t_rebate_detail | ||
| 111 | + WHERE del_flag = '0' | ||
| 112 | + AND YEAR(rebate_date) = #{year} | ||
| 113 | + GROUP BY MONTH(rebate_date) | ||
| 114 | + ORDER BY month | ||
| 115 | + </select> | ||
| 116 | + | ||
| 117 | + <!-- 获取返利状态统计数据 --> | ||
| 118 | + <select id="getRebateStatusStats" resultType="java.util.Map"> | ||
| 119 | + SELECT | ||
| 120 | + COUNT(CASE WHEN calc_flag = 1 THEN 1 END) as calculatedCount, | ||
| 121 | + COUNT(CASE WHEN calc_flag = 0 THEN 1 END) as unCalculatedCount, | ||
| 122 | + COUNT(*) as totalCount, | ||
| 123 | + COALESCE(SUM(CASE WHEN calc_flag = 1 AND operate_type = 1 THEN rebate_amount | ||
| 124 | + WHEN calc_flag = 1 AND operate_type = 2 THEN -rebate_amount | ||
| 125 | + ELSE 0 END), 0) as calculatedAmount, | ||
| 126 | + COALESCE(SUM(CASE WHEN operate_type = 1 THEN rebate_amount ELSE -rebate_amount END), 0) as totalAmount | ||
| 127 | + FROM t_rebate_detail | ||
| 128 | + WHERE del_flag = '0' | ||
| 129 | + </select> | ||
| 130 | + | ||
| 131 | + <!-- 获取返利趋势统计数据 --> | ||
| 132 | + <select id="getRebateTrendStats" resultType="java.util.Map"> | ||
| 133 | + SELECT | ||
| 134 | + DATE(rebate_date) as date, | ||
| 135 | + COALESCE(SUM(CASE WHEN operate_type = 1 THEN rebate_amount ELSE -rebate_amount END), 0) as totalAmount, | ||
| 136 | + COALESCE(SUM(CASE WHEN calc_flag = 1 AND operate_type = 1 THEN rebate_amount | ||
| 137 | + WHEN calc_flag = 1 AND operate_type = 2 THEN -rebate_amount | ||
| 138 | + ELSE 0 END), 0) as calculatedAmount, | ||
| 139 | + COUNT(*) as totalCount, | ||
| 140 | + COUNT(CASE WHEN calc_flag = 1 THEN 1 END) as calculatedCount | ||
| 141 | + FROM t_rebate_detail | ||
| 142 | + WHERE del_flag = '0' | ||
| 143 | + <if test="startDate != null and startDate != ''"> | ||
| 144 | + AND rebate_date >= #{startDate} | ||
| 145 | + </if> | ||
| 146 | + <if test="endDate != null and endDate != ''"> | ||
| 147 | + AND rebate_date <= #{endDate} | ||
| 148 | + </if> | ||
| 149 | + GROUP BY DATE(rebate_date) | ||
| 150 | + ORDER BY date | ||
| 151 | + </select> | ||
| 152 | + | ||
| 153 | +</mapper> |
| ... | @@ -51,7 +51,8 @@ | ... | @@ -51,7 +51,8 @@ |
| 51 | </select> | 51 | </select> |
| 52 | 52 | ||
| 53 | <select id="selectMenuTreeByRoleId" parameterType="Long" resultMap="SysMenuResult"> | 53 | <select id="selectMenuTreeByRoleId" parameterType="Long" resultMap="SysMenuResult"> |
| 54 | - select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.menu_type, m.status, m.perms, m.icon, m.create_by, m.create_time, m.update_by, m.update_time, m.del_flag | 54 | + select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.menu_type, m.status, m.perms, m.icon, m.create_by, m.create_time, m.update_by, m.update_time, m.del_flag, |
| 55 | + m.sort | ||
| 55 | from t_sys_menu m | 56 | from t_sys_menu m |
| 56 | left join t_sys_role_menu rm on m.menu_id = rm.menu_id | 57 | left join t_sys_role_menu rm on m.menu_id = rm.menu_id |
| 57 | where rm.role_id = #{roleId} and m.status = 1 and m.del_flag = '0' | 58 | where rm.role_id = #{roleId} and m.status = 1 and m.del_flag = '0' | ... | ... |
frontend/src/api/rebate.ts
0 → 100644
| 1 | +import request from '@/utils/request' | ||
| 2 | + | ||
| 3 | +// 返利台账明细类型定义 | ||
| 4 | +export interface Rebate { | ||
| 5 | + rebateId?: number | ||
| 6 | + rebateNo?: string | ||
| 7 | + orderNo: string | ||
| 8 | + dealerCode: string | ||
| 9 | + dealerName: string | ||
| 10 | + productCode: string | ||
| 11 | + rebateAmount: number | ||
| 12 | + rebateDate: string | ||
| 13 | + operateType: number | ||
| 14 | + operateTypeText?: string | ||
| 15 | + calcFlag?: number | ||
| 16 | + calcFlagText?: string | ||
| 17 | + uploadTime?: string | ||
| 18 | + createTime?: string | ||
| 19 | + updateTime?: string | ||
| 20 | + createBy?: string | ||
| 21 | + updateBy?: string | ||
| 22 | +} | ||
| 23 | + | ||
| 24 | +// 返利查询参数 | ||
| 25 | +export interface RebateSearchParams { | ||
| 26 | + pageNum: number | ||
| 27 | + pageSize: number | ||
| 28 | + rebateNo?: string | ||
| 29 | + orderNo?: string | ||
| 30 | + dealerCode?: string | ||
| 31 | + dealerName?: string | ||
| 32 | + productCode?: string | ||
| 33 | + operateType?: number | ||
| 34 | + calcFlag?: number | ||
| 35 | + rebateStartDate?: string | ||
| 36 | + rebateEndDate?: string | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +// 返利新增参数 | ||
| 40 | +export interface RebateAddParams { | ||
| 41 | + rebateNo?: string | ||
| 42 | + orderNo: string | ||
| 43 | + dealerCode: string | ||
| 44 | + dealerName: string | ||
| 45 | + productCode: string | ||
| 46 | + rebateAmount: number | ||
| 47 | + rebateDate: string | ||
| 48 | + operateType: number | ||
| 49 | + calcFlag?: number | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +// 返利更新参数 | ||
| 53 | +export interface RebateUpdateParams extends RebateAddParams { | ||
| 54 | + rebateId: number | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +// 分页查询返利列表 | ||
| 58 | +export const getRebatePage = (params: RebateSearchParams) => { | ||
| 59 | + return request({ | ||
| 60 | + url: '/api/rebate/page', | ||
| 61 | + method: 'post', | ||
| 62 | + data: params | ||
| 63 | + }) | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +// 根据ID查询返利详情 | ||
| 67 | +export const getRebateById = (rebateId: number) => { | ||
| 68 | + return request({ | ||
| 69 | + url: `/api/rebate/${rebateId}`, | ||
| 70 | + method: 'get' | ||
| 71 | + }) | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +// 新增返利 | ||
| 75 | +export const addRebate = (data: RebateAddParams) => { | ||
| 76 | + return request({ | ||
| 77 | + url: '/api/rebate/add', | ||
| 78 | + method: 'post', | ||
| 79 | + data | ||
| 80 | + }) | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +// 更新返利 | ||
| 84 | +export const updateRebate = (data: RebateUpdateParams) => { | ||
| 85 | + return request({ | ||
| 86 | + url: '/api/rebate/update', | ||
| 87 | + method: 'post', | ||
| 88 | + data | ||
| 89 | + }) | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | +// 删除返利 | ||
| 93 | +export const deleteRebate = (rebateId: number) => { | ||
| 94 | + return request({ | ||
| 95 | + url: `/api/rebate/${rebateId}`, | ||
| 96 | + method: 'delete' | ||
| 97 | + }) | ||
| 98 | +} | ||
| 99 | + | ||
| 100 | +// 批量删除返利 | ||
| 101 | +export const batchDeleteRebate = (rebateIds: number[]) => { | ||
| 102 | + return request({ | ||
| 103 | + url: '/api/rebate/batchDelete', | ||
| 104 | + method: 'post', | ||
| 105 | + data: rebateIds | ||
| 106 | + }) | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +// 标记返利为已计算 | ||
| 110 | +export const markRebateCalculated = (rebateId: number) => { | ||
| 111 | + return request({ | ||
| 112 | + url: `/api/rebate/markCalculated/${rebateId}`, | ||
| 113 | + method: 'post' | ||
| 114 | + }) | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +// 批量标记返利为已计算 | ||
| 118 | +export const batchMarkRebateCalculated = (rebateIds: number[]) => { | ||
| 119 | + return request({ | ||
| 120 | + url: '/api/rebate/batchMarkCalculated', | ||
| 121 | + method: 'post', | ||
| 122 | + data: rebateIds | ||
| 123 | + }) | ||
| 124 | +} | ||
| 125 | + | ||
| 126 | +// 根据经销商编码统计返利总金额 | ||
| 127 | +export const sumRebateAmountByDealer = (dealerCode: string, operateType?: number) => { | ||
| 128 | + return request({ | ||
| 129 | + url: `/api/rebate/sumByDealer/${dealerCode}`, | ||
| 130 | + method: 'get', | ||
| 131 | + params: { operateType } | ||
| 132 | + }) | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +// 根据产品编码统计返利总金额 | ||
| 136 | +export const sumRebateAmountByProduct = (productCode: string, operateType?: number) => { | ||
| 137 | + return request({ | ||
| 138 | + url: `/api/rebate/sumByProduct/${productCode}`, | ||
| 139 | + method: 'get', | ||
| 140 | + params: { operateType } | ||
| 141 | + }) | ||
| 142 | +} | ||
| 143 | + | ||
| 144 | +// 统计各操作类型的返利数量 | ||
| 145 | +export const countRebateByOperateType = () => { | ||
| 146 | + return request({ | ||
| 147 | + url: '/api/rebate/countByOperateType', | ||
| 148 | + method: 'get' | ||
| 149 | + }) | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +// 查询未计算的返利明细列表 | ||
| 153 | +export const getUnCalcRebates = () => { | ||
| 154 | + return request({ | ||
| 155 | + url: '/api/rebate/unCalc', | ||
| 156 | + method: 'get' | ||
| 157 | + }) | ||
| 158 | +} | ||
| 159 | + | ||
| 160 | +// 根据返利编号查询返利 | ||
| 161 | +export const getRebateByNo = (rebateNo: string) => { | ||
| 162 | + return request({ | ||
| 163 | + url: `/api/rebate/byNo/${rebateNo}`, | ||
| 164 | + method: 'get' | ||
| 165 | + }) | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +// 生成返利编号 | ||
| 169 | +export const generateRebateNo = () => { | ||
| 170 | + return request({ | ||
| 171 | + url: '/api/rebate/generateNo', | ||
| 172 | + method: 'get' | ||
| 173 | + }) | ||
| 174 | +} | ||
| 175 | + | ||
| 176 | +// 导出返利数据 | ||
| 177 | +export const exportRebate = (params: RebateSearchParams) => { | ||
| 178 | + return request({ | ||
| 179 | + url: '/api/rebate/export', | ||
| 180 | + method: 'post', | ||
| 181 | + data: params, | ||
| 182 | + responseType: 'blob' | ||
| 183 | + }) | ||
| 184 | +} | ||
| 185 | + | ||
| 186 | +// 获取返利月度统计数据 | ||
| 187 | +export const getRebateMonthlyStats = (year?: number) => { | ||
| 188 | + return request({ | ||
| 189 | + url: '/api/rebate/monthlyStats', | ||
| 190 | + method: 'get', | ||
| 191 | + params: { year: year || new Date().getFullYear() } | ||
| 192 | + }) | ||
| 193 | +} | ||
| 194 | + | ||
| 195 | +// 获取返利状态统计数据 | ||
| 196 | +export const getRebateStatusStats = () => { | ||
| 197 | + return request({ | ||
| 198 | + url: '/api/rebate/statusStats', | ||
| 199 | + method: 'get' | ||
| 200 | + }) | ||
| 201 | +} | ||
| 202 | + | ||
| 203 | +// 获取返利趋势统计数据 | ||
| 204 | +export const getRebateTrendStats = (startDate?: string, endDate?: string) => { | ||
| 205 | + return request({ | ||
| 206 | + url: '/api/rebate/trendStats', | ||
| 207 | + method: 'get', | ||
| 208 | + params: { startDate, endDate } | ||
| 209 | + }) | ||
| 210 | +} | ||
| 211 | + | ||
| 212 | +export default { | ||
| 213 | + getRebatePage, | ||
| 214 | + getRebateById, | ||
| 215 | + addRebate, | ||
| 216 | + updateRebate, | ||
| 217 | + deleteRebate, | ||
| 218 | + batchDeleteRebate, | ||
| 219 | + markRebateCalculated, | ||
| 220 | + batchMarkRebateCalculated, | ||
| 221 | + sumRebateAmountByDealer, | ||
| 222 | + sumRebateAmountByProduct, | ||
| 223 | + countRebateByOperateType, | ||
| 224 | + getUnCalcRebates, | ||
| 225 | + getRebateByNo, | ||
| 226 | + generateRebateNo, | ||
| 227 | + exportRebate, | ||
| 228 | + getRebateMonthlyStats, | ||
| 229 | + getRebateStatusStats, | ||
| 230 | + getRebateTrendStats | ||
| 231 | +} |
| ... | @@ -146,6 +146,7 @@ const menuItems = ref([ | ... | @@ -146,6 +146,7 @@ const menuItems = ref([ |
| 146 | { name: '发票管理', path: '/main/invoice', icon: '🧾' }, | 146 | { name: '发票管理', path: '/main/invoice', icon: '🧾' }, |
| 147 | { name: '产品管理', path: '/main/product', icon: '📦' }, | 147 | { name: '产品管理', path: '/main/product', icon: '📦' }, |
| 148 | { name: '经销商管理', path: '/main/dealer', icon: '🏢' }, | 148 | { name: '经销商管理', path: '/main/dealer', icon: '🏢' }, |
| 149 | + { name: '返利管理', path: '/main/rebate', icon: '💰' }, | ||
| 149 | { | 150 | { |
| 150 | name: '系统设置', | 151 | name: '系统设置', |
| 151 | icon: '⚙️', | 152 | icon: '⚙️', | ... | ... |
| ... | @@ -99,24 +99,6 @@ const staticRoutes: RouteRecordRaw[] = [ | ... | @@ -99,24 +99,6 @@ const staticRoutes: RouteRecordRaw[] = [ |
| 99 | } | 99 | } |
| 100 | }, | 100 | }, |
| 101 | { | 101 | { |
| 102 | - path: 'dicts', | ||
| 103 | - name: 'Dicts', | ||
| 104 | - component: () => import('@/views/dicts/index.vue'), | ||
| 105 | - meta: { | ||
| 106 | - title: '字典管理', | ||
| 107 | - requiresAuth: true | ||
| 108 | - } | ||
| 109 | - }, | ||
| 110 | - { | ||
| 111 | - path: 'logs', | ||
| 112 | - name: 'Logs', | ||
| 113 | - component: () => import('@/views/logs/index.vue'), | ||
| 114 | - meta: { | ||
| 115 | - title: '日志管理', | ||
| 116 | - requiresAuth: true | ||
| 117 | - } | ||
| 118 | - }, | ||
| 119 | - { | ||
| 120 | path: 'product', | 102 | path: 'product', |
| 121 | name: 'Product', | 103 | name: 'Product', |
| 122 | component: () => import('@/views/product/index.vue'), | 104 | component: () => import('@/views/product/index.vue'), |
| ... | @@ -162,6 +144,15 @@ const staticRoutes: RouteRecordRaw[] = [ | ... | @@ -162,6 +144,15 @@ const staticRoutes: RouteRecordRaw[] = [ |
| 162 | } | 144 | } |
| 163 | }, | 145 | }, |
| 164 | { | 146 | { |
| 147 | + path: 'rebate', | ||
| 148 | + name: 'Rebate', | ||
| 149 | + component: () => import('@/views/rebate/index.vue'), | ||
| 150 | + meta: { | ||
| 151 | + title: '返利管理', | ||
| 152 | + requiresAuth: true | ||
| 153 | + } | ||
| 154 | + }, | ||
| 155 | + { | ||
| 165 | path: 'settings', | 156 | path: 'settings', |
| 166 | name: 'Settings', | 157 | name: 'Settings', |
| 167 | component: () => import('@/views/settings/index.vue'), | 158 | component: () => import('@/views/settings/index.vue'), | ... | ... |
| ... | @@ -93,27 +93,27 @@ | ... | @@ -93,27 +93,27 @@ |
| 93 | <th>出库日期</th> | 93 | <th>出库日期</th> |
| 94 | <th>关联订单编号</th> | 94 | <th>关联订单编号</th> |
| 95 | <th>出库状态</th> | 95 | <th>出库状态</th> |
| 96 | - <th>出库时间</th> | 96 | + <th>创建时间</th> |
| 97 | <th>操作</th> | 97 | <th>操作</th> |
| 98 | </tr> | 98 | </tr> |
| 99 | </thead> | 99 | </thead> |
| 100 | <tbody> | 100 | <tbody> |
| 101 | - <tr v-for="order in orderList" :key="order.deliveryId"> | 101 | + <tr v-for="delivery in deliveryList" :key="delivery.deliveryId"> |
| 102 | - <td>{{ order.deliveryId }}</td> | 102 | + <td>{{ delivery.deliveryId }}</td> |
| 103 | - <td>{{ order.deliveryNo }}</td> | 103 | + <td>{{ delivery.deliveryNo }}</td> |
| 104 | - <td>{{ order.dealerCode }}</td> | 104 | + <td>{{ delivery.dealerCode }}</td> |
| 105 | - <td>{{ order.dealerName }}</td> | 105 | + <td>{{ delivery.dealerName }}</td> |
| 106 | - <td>{{ formatDate(order.deliveryDate) }}</td> | 106 | + <td>{{ formatDate(delivery.deliveryDate) }}</td> |
| 107 | - <td>{{ order.orderNo }}</td> | 107 | + <td>{{ delivery.orderNo }}</td> |
| 108 | <td> | 108 | <td> |
| 109 | - <span :class="getDeliveryStatusClass(order.deliveryStatus)"> | 109 | + <span :class="getDeliveryStatusClass(delivery.deliveryStatus)"> |
| 110 | - {{ getDeliveryStatusText(order.deliveryStatus) }} | 110 | + {{ getDeliveryStatusText(delivery.deliveryStatus) }} |
| 111 | </span> | 111 | </span> |
| 112 | </td> | 112 | </td> |
| 113 | - <td>{{ formatTime(order.uploadTime || '') }}</td> | 113 | + <td>{{ formatTime(delivery.createTime || '') }}</td> |
| 114 | <td class="action-col"> | 114 | <td class="action-col"> |
| 115 | - <button @click="handleView(order)" class="view-btn">👁️ 查看</button> | 115 | + <button @click="handleView(delivery)" class="view-btn">👁️ 查看</button> |
| 116 | - <button @click="handleDelete(order)" class="delete-btn">🗑️ 删除</button> | 116 | + <button @click="handleDelete(delivery)" class="delete-btn">🗑️ 删除</button> |
| 117 | </td> | 117 | </td> |
| 118 | </tr> | 118 | </tr> |
| 119 | </tbody> | 119 | </tbody> |
| ... | @@ -173,44 +173,48 @@ | ... | @@ -173,44 +173,48 @@ |
| 173 | <button @click="closeDetailDialog" class="close-btn">×</button> | 173 | <button @click="closeDetailDialog" class="close-btn">×</button> |
| 174 | </div> | 174 | </div> |
| 175 | <div class="dialog-body"> | 175 | <div class="dialog-body"> |
| 176 | - <div v-if="orderDetail" class="detail-content"> | 176 | + <div v-if="deliveryDetail" class="detail-content"> |
| 177 | <div class="detail-section"> | 177 | <div class="detail-section"> |
| 178 | - <h4>订单基本信息</h4> | 178 | + <h4>出库基本信息</h4> |
| 179 | <div class="detail-grid"> | 179 | <div class="detail-grid"> |
| 180 | <div class="detail-item"> | 180 | <div class="detail-item"> |
| 181 | - <label>订单编号:</label> | 181 | + <label>出库单号:</label> |
| 182 | - <span>{{ orderDetail.orderNo }}</span> | 182 | + <span>{{ deliveryDetail.deliveryNo }}</span> |
| 183 | + </div> | ||
| 184 | + <div class="detail-item"> | ||
| 185 | + <label>关联订单编号:</label> | ||
| 186 | + <span>{{ deliveryDetail.orderNo }}</span> | ||
| 183 | </div> | 187 | </div> |
| 184 | <div class="detail-item"> | 188 | <div class="detail-item"> |
| 185 | <label>经销商编码:</label> | 189 | <label>经销商编码:</label> |
| 186 | - <span>{{ orderDetail.dealerCode }}</span> | 190 | + <span>{{ deliveryDetail.dealerCode }}</span> |
| 187 | </div> | 191 | </div> |
| 188 | <div class="detail-item"> | 192 | <div class="detail-item"> |
| 189 | <label>经销商名称:</label> | 193 | <label>经销商名称:</label> |
| 190 | - <span>{{ orderDetail.dealerName }}</span> | 194 | + <span>{{ deliveryDetail.dealerName }}</span> |
| 191 | </div> | 195 | </div> |
| 192 | <div class="detail-item"> | 196 | <div class="detail-item"> |
| 193 | - <label>订单日期:</label> | 197 | + <label>出库日期:</label> |
| 194 | - <span>{{ formatDate(orderDetail.orderDate) }}</span> | 198 | + <span>{{ formatDate(deliveryDetail.deliveryDate) }}</span> |
| 195 | </div> | 199 | </div> |
| 196 | <div class="detail-item"> | 200 | <div class="detail-item"> |
| 197 | - <label>订单总金额:</label> | 201 | + <label>出库总金额:</label> |
| 198 | - <span>{{ formatCurrency(orderDetail.totalAmount) }}</span> | 202 | + <span>{{ formatCurrency(deliveryDetail.totalAmount) }}</span> |
| 199 | </div> | 203 | </div> |
| 200 | <div class="detail-item"> | 204 | <div class="detail-item"> |
| 201 | <label>出库状态:</label> | 205 | <label>出库状态:</label> |
| 202 | - <span :class="getDeliveryStatusClass(orderDetail.deliveryStatus)"> | 206 | + <span :class="getDeliveryStatusClass(deliveryDetail.deliveryStatus)"> |
| 203 | - {{ getDeliveryStatusText(orderDetail.deliveryStatus) }} | 207 | + {{ getDeliveryStatusText(deliveryDetail.deliveryStatus) }} |
| 204 | </span> | 208 | </span> |
| 205 | </div> | 209 | </div> |
| 206 | <div class="detail-item"> | 210 | <div class="detail-item"> |
| 207 | - <label>出库时间:</label> | 211 | + <label>创建时间:</label> |
| 208 | - <span>{{ formatTime(orderDetail.uploadTime || '') }}</span> | 212 | + <span>{{ formatTime(deliveryDetail.createTime || '') }}</span> |
| 209 | </div> | 213 | </div> |
| 210 | </div> | 214 | </div> |
| 211 | </div> | 215 | </div> |
| 212 | 216 | ||
| 213 | - <div class="detail-section" v-if="orderDetail.orderItems && orderDetail.orderItems.length > 0"> | 217 | + <div class="detail-section" v-if="deliveryDetail.deliveryItems && deliveryDetail.deliveryItems.length > 0"> |
| 214 | <h4>出库明细</h4> | 218 | <h4>出库明细</h4> |
| 215 | <table class="detail-table"> | 219 | <table class="detail-table"> |
| 216 | <thead> | 220 | <thead> |
| ... | @@ -218,17 +222,17 @@ | ... | @@ -218,17 +222,17 @@ |
| 218 | <th>产品编码</th> | 222 | <th>产品编码</th> |
| 219 | <th>产品名称</th> | 223 | <th>产品名称</th> |
| 220 | <th>产品型号</th> | 224 | <th>产品型号</th> |
| 221 | - <th>数量</th> | 225 | + <th>出库数量</th> |
| 222 | - <th>单价</th> | 226 | + <th>出库单价</th> |
| 223 | - <th>总价</th> | 227 | + <th>出库总价</th> |
| 224 | </tr> | 228 | </tr> |
| 225 | </thead> | 229 | </thead> |
| 226 | <tbody> | 230 | <tbody> |
| 227 | - <tr v-for="item in orderDetail.orderItems" :key="item.itemId"> | 231 | + <tr v-for="item in deliveryDetail.deliveryItems" :key="item.itemId"> |
| 228 | <td>{{ item.productCode }}</td> | 232 | <td>{{ item.productCode }}</td> |
| 229 | <td>{{ item.productName }}</td> | 233 | <td>{{ item.productName }}</td> |
| 230 | <td>{{ item.productModel }}</td> | 234 | <td>{{ item.productModel }}</td> |
| 231 | - <td>{{ item.quantity }}</td> | 235 | + <td>{{ item.productQty }}</td> |
| 232 | <td>{{ formatCurrency(item.unitPrice) }}</td> | 236 | <td>{{ formatCurrency(item.unitPrice) }}</td> |
| 233 | <td>{{ formatCurrency(item.totalPrice) }}</td> | 237 | <td>{{ formatCurrency(item.totalPrice) }}</td> |
| 234 | </tr> | 238 | </tr> |
| ... | @@ -415,9 +419,9 @@ import { deliveryApi, type DeliveryAddReq, type DeliveryInfo, type DeliveryQuery | ... | @@ -415,9 +419,9 @@ import { deliveryApi, type DeliveryAddReq, type DeliveryInfo, type DeliveryQuery |
| 415 | 419 | ||
| 416 | // 响应式数据 | 420 | // 响应式数据 |
| 417 | const loading = ref(false) | 421 | const loading = ref(false) |
| 418 | -const orderList = ref<DeliveryInfo[]>([]) | 422 | +const deliveryList = ref<DeliveryInfo[]>([]) |
| 419 | const showDetailDialog = ref(false) | 423 | const showDetailDialog = ref(false) |
| 420 | -const orderDetail = ref<DeliveryInfo | null>(null) | 424 | +const deliveryDetail = ref<DeliveryInfo | null>(null) |
| 421 | 425 | ||
| 422 | // 新增出库相关数据 | 426 | // 新增出库相关数据 |
| 423 | const showAddDialog = ref(false) | 427 | const showAddDialog = ref(false) |
| ... | @@ -435,6 +439,7 @@ const addFormData = reactive({ | ... | @@ -435,6 +439,7 @@ const addFormData = reactive({ |
| 435 | 439 | ||
| 436 | // 搜索参数 | 440 | // 搜索参数 |
| 437 | const searchParams = reactive({ | 441 | const searchParams = reactive({ |
| 442 | + orderNo: '', | ||
| 438 | deliveryNo: '', | 443 | deliveryNo: '', |
| 439 | dealerCode: '', | 444 | dealerCode: '', |
| 440 | dealerName: '', | 445 | dealerName: '', |
| ... | @@ -443,6 +448,8 @@ const searchParams = reactive({ | ... | @@ -443,6 +448,8 @@ const searchParams = reactive({ |
| 443 | dataSource: '', | 448 | dataSource: '', |
| 444 | deliveryStartDate: '', | 449 | deliveryStartDate: '', |
| 445 | deliveryEndDate: '', | 450 | deliveryEndDate: '', |
| 451 | + startDate: '', | ||
| 452 | + endDate: '', | ||
| 446 | pageNum: 1, | 453 | pageNum: 1, |
| 447 | pageSize: 10 | 454 | pageSize: 10 |
| 448 | }) | 455 | }) |
| ... | @@ -511,7 +518,7 @@ const getPageNumbers = () => { | ... | @@ -511,7 +518,7 @@ const getPageNumbers = () => { |
| 511 | } | 518 | } |
| 512 | 519 | ||
| 513 | // 方法 | 520 | // 方法 |
| 514 | -const fetchOrders = async () => { | 521 | +const fetchDeliveries = async () => { |
| 515 | try { | 522 | try { |
| 516 | loading.value = true | 523 | loading.value = true |
| 517 | const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize } | 524 | const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize } |
| ... | @@ -520,12 +527,12 @@ const fetchOrders = async () => { | ... | @@ -520,12 +527,12 @@ const fetchOrders = async () => { |
| 520 | 527 | ||
| 521 | // 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data | 528 | // 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data |
| 522 | if (response && response.records) { | 529 | if (response && response.records) { |
| 523 | - orderList.value = response.records || [] | 530 | + deliveryList.value = response.records || [] |
| 524 | pagination.total = response.total || 0 | 531 | pagination.total = response.total || 0 |
| 525 | pagination.pageNum = response.current || 1 | 532 | pagination.pageNum = response.current || 1 |
| 526 | pagination.pageSize = response.size || 10 | 533 | pagination.pageSize = response.size || 10 |
| 527 | } else { | 534 | } else { |
| 528 | - orderList.value = [] | 535 | + deliveryList.value = [] |
| 529 | pagination.total = 0 | 536 | pagination.total = 0 |
| 530 | showMessage('获取出库列表失败', 'error') | 537 | showMessage('获取出库列表失败', 'error') |
| 531 | } | 538 | } |
| ... | @@ -539,7 +546,7 @@ const fetchOrders = async () => { | ... | @@ -539,7 +546,7 @@ const fetchOrders = async () => { |
| 539 | 546 | ||
| 540 | const handleSearch = () => { | 547 | const handleSearch = () => { |
| 541 | pagination.pageNum = 1 | 548 | pagination.pageNum = 1 |
| 542 | - fetchOrders() | 549 | + fetchDeliveries() |
| 543 | } | 550 | } |
| 544 | 551 | ||
| 545 | const handleReset = () => { | 552 | const handleReset = () => { |
| ... | @@ -552,33 +559,33 @@ const handleReset = () => { | ... | @@ -552,33 +559,33 @@ const handleReset = () => { |
| 552 | endDate: '' | 559 | endDate: '' |
| 553 | }) | 560 | }) |
| 554 | pagination.pageNum = 1 | 561 | pagination.pageNum = 1 |
| 555 | - fetchOrders() | 562 | + fetchDeliveries() |
| 556 | } | 563 | } |
| 557 | 564 | ||
| 558 | -const handleView = async (order: DeliveryInfo) => { | 565 | +const handleView = async (delivery: DeliveryInfo) => { |
| 559 | try { | 566 | try { |
| 560 | - const response = await deliveryApi.getDeliveryDetail(order.deliveryId) as any | 567 | + const response = await deliveryApi.getDeliveryDetail(delivery.deliveryId) as any |
| 561 | console.log('出库查询详情API响应:', response) | 568 | console.log('出库查询详情API响应:', response) |
| 562 | 569 | ||
| 563 | // 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data | 570 | // 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data |
| 564 | if (response && response.deliveryId) { | 571 | if (response && response.deliveryId) { |
| 565 | - orderDetail.value = response | 572 | + deliveryDetail.value = response |
| 566 | showDetailDialog.value = true | 573 | showDetailDialog.value = true |
| 567 | } else { | 574 | } else { |
| 568 | - showMessage('获取订单详情失败', 'error') | 575 | + showMessage('获取出库详情失败', 'error') |
| 569 | } | 576 | } |
| 570 | } catch (error) { | 577 | } catch (error) { |
| 571 | - console.error('获取订单详情失败:', error) | 578 | + console.error('获取出库详情失败:', error) |
| 572 | - showMessage('获取订单详情失败, 请重试', 'error') | 579 | + showMessage('获取出库详情失败, 请重试', 'error') |
| 573 | } | 580 | } |
| 574 | } | 581 | } |
| 575 | 582 | ||
| 576 | -const handleDelete = async (order: DeliveryInfo) => { | 583 | +const handleDelete = async (delivery: DeliveryInfo) => { |
| 577 | - if (confirm(`确定要删除出库单"${order.deliveryNo}"吗?`)) { | 584 | + if (confirm(`确定要删除出库单"${delivery.deliveryNo}"吗?`)) { |
| 578 | try { | 585 | try { |
| 579 | - await deliveryApi.deleteDelivery(order.deliveryId) | 586 | + await deliveryApi.deleteDelivery(delivery.deliveryId) |
| 580 | showMessage('删除出库单成功', 'success') | 587 | showMessage('删除出库单成功', 'success') |
| 581 | - fetchOrders() // 刷新列表 | 588 | + fetchDeliveries() // 刷新列表 |
| 582 | } catch (error) { | 589 | } catch (error) { |
| 583 | console.error('删除出库单失败:', error) | 590 | console.error('删除出库单失败:', error) |
| 584 | showMessage('删除出库单失败, 请重试', 'error') | 591 | showMessage('删除出库单失败, 请重试', 'error') |
| ... | @@ -655,16 +662,16 @@ const handleSubmitAdd = async () => { | ... | @@ -655,16 +662,16 @@ const handleSubmitAdd = async () => { |
| 655 | deliveryItems: addFormData.deliveryItems.map(item => ({ | 662 | deliveryItems: addFormData.deliveryItems.map(item => ({ |
| 656 | productCode: item.productCode, | 663 | productCode: item.productCode, |
| 657 | productName: item.productName, | 664 | productName: item.productName, |
| 658 | - deliveryQty: item.deliveryQty, | 665 | + productQty: item.deliveryQty, |
| 659 | - deliveryPrice: item.deliveryPrice, | 666 | + unitPrice: item.deliveryPrice, |
| 660 | - deliveryAmount: item.deliveryAmount | 667 | + totalPrice: item.deliveryAmount |
| 661 | })) | 668 | })) |
| 662 | } | 669 | } |
| 663 | 670 | ||
| 664 | await deliveryApi.addDelivery(deliveryData) | 671 | await deliveryApi.addDelivery(deliveryData) |
| 665 | showMessage('新增出库成功', 'success') | 672 | showMessage('新增出库成功', 'success') |
| 666 | closeAddDialog() | 673 | closeAddDialog() |
| 667 | - fetchOrders() // 刷新列表 | 674 | + fetchDeliveries() // 刷新列表 |
| 668 | } catch (error) { | 675 | } catch (error) { |
| 669 | console.error('新增出库失败:', error) | 676 | console.error('新增出库失败:', error) |
| 670 | showMessage('新增出库失败, 请重试', 'error') | 677 | showMessage('新增出库失败, 请重试', 'error') |
| ... | @@ -676,13 +683,13 @@ const handleSubmitAdd = async () => { | ... | @@ -676,13 +683,13 @@ const handleSubmitAdd = async () => { |
| 676 | const handlePageChange = (page: number) => { | 683 | const handlePageChange = (page: number) => { |
| 677 | if (page >= 1 && page <= totalPages.value) { | 684 | if (page >= 1 && page <= totalPages.value) { |
| 678 | pagination.pageNum = page | 685 | pagination.pageNum = page |
| 679 | - fetchOrders() | 686 | + fetchDeliveries() |
| 680 | } | 687 | } |
| 681 | } | 688 | } |
| 682 | 689 | ||
| 683 | const handlePageSizeChange = () => { | 690 | const handlePageSizeChange = () => { |
| 684 | pagination.pageNum = 1 | 691 | pagination.pageNum = 1 |
| 685 | - fetchOrders() | 692 | + fetchDeliveries() |
| 686 | } | 693 | } |
| 687 | 694 | ||
| 688 | const handleTableSearch = () => { | 695 | const handleTableSearch = () => { |
| ... | @@ -690,7 +697,7 @@ const handleTableSearch = () => { | ... | @@ -690,7 +697,7 @@ const handleTableSearch = () => { |
| 690 | } | 697 | } |
| 691 | 698 | ||
| 692 | const handleTableRefresh = () => { | 699 | const handleTableRefresh = () => { |
| 693 | - fetchOrders() | 700 | + fetchDeliveries() |
| 694 | } | 701 | } |
| 695 | 702 | ||
| 696 | const handleTableExport = () => { | 703 | const handleTableExport = () => { |
| ... | @@ -703,12 +710,12 @@ const handleTableViewToggle = () => { | ... | @@ -703,12 +710,12 @@ const handleTableViewToggle = () => { |
| 703 | 710 | ||
| 704 | const closeDetailDialog = () => { | 711 | const closeDetailDialog = () => { |
| 705 | showDetailDialog.value = false | 712 | showDetailDialog.value = false |
| 706 | - orderDetail.value = null | 713 | + deliveryDetail.value = null |
| 707 | } | 714 | } |
| 708 | 715 | ||
| 709 | // 生命周期 | 716 | // 生命周期 |
| 710 | onMounted(() => { | 717 | onMounted(() => { |
| 711 | - fetchOrders() | 718 | + fetchDeliveries() |
| 712 | }) | 719 | }) |
| 713 | </script> | 720 | </script> |
| 714 | 721 | ... | ... |
frontend/src/views/rebate/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="rebate-page"> | ||
| 3 | + <!-- 搜索区域 --> | ||
| 4 | + <div class="search-section"> | ||
| 5 | + <div class="search-form"> | ||
| 6 | + <div class="search-row"> | ||
| 7 | + <div class="search-item"> | ||
| 8 | + <span class="search-label">经销商名称</span> | ||
| 9 | + <el-input | ||
| 10 | + v-model="searchForm.dealerName" | ||
| 11 | + placeholder="请输入经销商名称" | ||
| 12 | + clearable | ||
| 13 | + size="small" | ||
| 14 | + style="width: 160px" | ||
| 15 | + /> | ||
| 16 | + </div> | ||
| 17 | + <div class="search-item"> | ||
| 18 | + <span class="search-label">返利编号</span> | ||
| 19 | + <el-input | ||
| 20 | + v-model="searchForm.rebateNo" | ||
| 21 | + placeholder="请输入返利编号" | ||
| 22 | + clearable | ||
| 23 | + size="small" | ||
| 24 | + style="width: 160px" | ||
| 25 | + /> | ||
| 26 | + </div> | ||
| 27 | + <div class="search-item"> | ||
| 28 | + <span class="search-label">日期</span> | ||
| 29 | + <el-date-picker | ||
| 30 | + v-model="dateRange" | ||
| 31 | + type="daterange" | ||
| 32 | + range-separator="至" | ||
| 33 | + start-placeholder="开始日期" | ||
| 34 | + end-placeholder="结束日期" | ||
| 35 | + size="small" | ||
| 36 | + format="YYYY-MM-DD" | ||
| 37 | + value-format="YYYY-MM-DD" | ||
| 38 | + @change="handleDateRangeChange" | ||
| 39 | + style="width: 240px" | ||
| 40 | + /> | ||
| 41 | + </div> | ||
| 42 | + <div class="search-actions"> | ||
| 43 | + <el-button type="primary" size="small" @click="handleSearch" :loading="loading"> | ||
| 44 | + 查询 | ||
| 45 | + </el-button> | ||
| 46 | + <el-button size="small" @click="handleReset"> | ||
| 47 | + 重置 | ||
| 48 | + </el-button> | ||
| 49 | + </div> | ||
| 50 | + </div> | ||
| 51 | + </div> | ||
| 52 | + </div> | ||
| 53 | + | ||
| 54 | + <!-- 图表区域 --> | ||
| 55 | + <div class="charts-section"> | ||
| 56 | + <div class="charts-container"> | ||
| 57 | + <!-- 左侧趋势图 --> | ||
| 58 | + <div class="chart-card trend-chart"> | ||
| 59 | + <div class="chart-header"> | ||
| 60 | + <h3 class="chart-title">返利统计</h3> | ||
| 61 | + <div class="chart-legend"> | ||
| 62 | + <div class="legend-item"> | ||
| 63 | + <span class="legend-dot" style="background-color: #4CAF50;"></span> | ||
| 64 | + <span>返利金额</span> | ||
| 65 | + </div> | ||
| 66 | + <div class="legend-item"> | ||
| 67 | + <span class="legend-dot" style="background-color: #2196F3;"></span> | ||
| 68 | + <span>已审核返利</span> | ||
| 69 | + </div> | ||
| 70 | + </div> | ||
| 71 | + </div> | ||
| 72 | + <div class="chart-content"> | ||
| 73 | + <div ref="trendChart" class="chart"></div> | ||
| 74 | + </div> | ||
| 75 | + </div> | ||
| 76 | + | ||
| 77 | + <!-- 右侧饼图 --> | ||
| 78 | + <div class="chart-card pie-chart"> | ||
| 79 | + <div class="chart-header"> | ||
| 80 | + <h3 class="chart-title">计算统计</h3> | ||
| 81 | + <div class="chart-legend"> | ||
| 82 | + <div class="legend-item"> | ||
| 83 | + <span class="legend-dot" style="background-color: #2196F3;"></span> | ||
| 84 | + <span>已计算</span> | ||
| 85 | + </div> | ||
| 86 | + <div class="legend-item"> | ||
| 87 | + <span class="legend-dot" style="background-color: #4CAF50;"></span> | ||
| 88 | + <span>未计算</span> | ||
| 89 | + </div> | ||
| 90 | + </div> | ||
| 91 | + </div> | ||
| 92 | + <div class="chart-content"> | ||
| 93 | + <div ref="pieChart" class="chart"></div> | ||
| 94 | + </div> | ||
| 95 | + </div> | ||
| 96 | + </div> | ||
| 97 | + </div> | ||
| 98 | + | ||
| 99 | + <!-- 数据表格 --> | ||
| 100 | + <div class="table-section"> | ||
| 101 | + <div class="table-header"> | ||
| 102 | + <div class="table-title"> | ||
| 103 | + 返利记录 | ||
| 104 | + </div> | ||
| 105 | + <div class="table-info"> | ||
| 106 | + 数据状态: {{ loading ? '加载中...' : `共${rebateList.length}条记录` }} | ||
| 107 | + </div> | ||
| 108 | + </div> | ||
| 109 | + | ||
| 110 | + <el-table | ||
| 111 | + v-loading="loading" | ||
| 112 | + :data="rebateList" | ||
| 113 | + style="width: 100%" | ||
| 114 | + :header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 'normal' }" | ||
| 115 | + :empty-text="rebateList.length === 0 ? '暂无数据' : ''" | ||
| 116 | + size="small" | ||
| 117 | + > | ||
| 118 | + <el-table-column prop="rebateNo" label="返利编号" width="140" align="center" /> | ||
| 119 | + <el-table-column prop="orderNo" label="订单编号" width="140" align="center" /> | ||
| 120 | + <el-table-column prop="dealerCode" label="经销商代码" width="120" align="center" /> | ||
| 121 | + <el-table-column prop="dealerName" label="经销商名称" min-width="150" /> | ||
| 122 | + <el-table-column prop="productCode" label="产品编码" width="120" align="center" /> | ||
| 123 | + <el-table-column prop="rebateAmount" label="返利金额" width="120" align="right"> | ||
| 124 | + <template #default="{ row }"> | ||
| 125 | + <span class="amount-text">{{ formatAmount(row.rebateAmount) }}</span> | ||
| 126 | + </template> | ||
| 127 | + </el-table-column> | ||
| 128 | + <el-table-column prop="rebateDate" label="返利日期" width="120" align="center" /> | ||
| 129 | + <el-table-column prop="operateTypeText" label="操作类型" width="100" align="center"> | ||
| 130 | + <template #default="{ row }"> | ||
| 131 | + <el-tag :type="getOperateTypeTagType(row.operateType)" size="small"> | ||
| 132 | + {{ row.operateTypeText || getOperateTypeText(row.operateType) }} | ||
| 133 | + </el-tag> | ||
| 134 | + </template> | ||
| 135 | + </el-table-column> | ||
| 136 | + <el-table-column prop="calcFlagText" label="计算状态" width="100" align="center"> | ||
| 137 | + <template #default="{ row }"> | ||
| 138 | + <el-tag :type="getCalcFlagTagType(row.calcFlag)" size="small"> | ||
| 139 | + {{ row.calcFlagText || getCalcFlagText(row.calcFlag) }} | ||
| 140 | + </el-tag> | ||
| 141 | + </template> | ||
| 142 | + </el-table-column> | ||
| 143 | + <el-table-column prop="updateTime" label="更新时间" width="150" align="center"> | ||
| 144 | + <template #default="{ row }"> | ||
| 145 | + {{ formatDate(row.updateTime) }} | ||
| 146 | + </template> | ||
| 147 | + </el-table-column> | ||
| 148 | + <el-table-column label="操作" width="80" align="center"> | ||
| 149 | + <template #default="{ row }"> | ||
| 150 | + <el-button type="primary" size="small" link @click="handleViewDetail(row)"> | ||
| 151 | + 详情 | ||
| 152 | + </el-button> | ||
| 153 | + </template> | ||
| 154 | + </el-table-column> | ||
| 155 | + </el-table> | ||
| 156 | + | ||
| 157 | + <!-- 分页 --> | ||
| 158 | + <div class="pagination-wrapper"> | ||
| 159 | + <div class="pagination-info"> | ||
| 160 | + 共 {{ pagination.total }} 条,{{ pagination.pageSize }}/页 | ||
| 161 | + </div> | ||
| 162 | + <el-pagination | ||
| 163 | + v-model:current-page="pagination.pageNum" | ||
| 164 | + v-model:page-size="pagination.pageSize" | ||
| 165 | + :page-sizes="[10, 20, 50, 100]" | ||
| 166 | + :total="pagination.total" | ||
| 167 | + layout="sizes, prev, pager, next" | ||
| 168 | + @size-change="handleSizeChange" | ||
| 169 | + @current-change="handleCurrentChange" | ||
| 170 | + small | ||
| 171 | + /> | ||
| 172 | + <div class="pagination-jump"> | ||
| 173 | + 前往 | ||
| 174 | + <el-input | ||
| 175 | + v-model="jumpPage" | ||
| 176 | + size="small" | ||
| 177 | + style="width: 50px; margin: 0 8px;" | ||
| 178 | + @keyup.enter="handleJumpPage" | ||
| 179 | + /> | ||
| 180 | + 页 | ||
| 181 | + </div> | ||
| 182 | + </div> | ||
| 183 | + </div> | ||
| 184 | + | ||
| 185 | + <!-- 返利详情对话框 --> | ||
| 186 | + <el-dialog | ||
| 187 | + v-model="detailDialogVisible" | ||
| 188 | + title="返利详情" | ||
| 189 | + width="800px" | ||
| 190 | + :close-on-click-modal="false" | ||
| 191 | + > | ||
| 192 | + <div v-if="rebateDetail" class="rebate-detail"> | ||
| 193 | + <el-descriptions :column="2" border> | ||
| 194 | + <el-descriptions-item label="返利编号"> | ||
| 195 | + {{ rebateDetail.rebateNo }} | ||
| 196 | + </el-descriptions-item> | ||
| 197 | + <el-descriptions-item label="订单编号"> | ||
| 198 | + {{ rebateDetail.orderNo }} | ||
| 199 | + </el-descriptions-item> | ||
| 200 | + <el-descriptions-item label="经销商代码"> | ||
| 201 | + {{ rebateDetail.dealerCode }} | ||
| 202 | + </el-descriptions-item> | ||
| 203 | + <el-descriptions-item label="经销商名称"> | ||
| 204 | + {{ rebateDetail.dealerName }} | ||
| 205 | + </el-descriptions-item> | ||
| 206 | + <el-descriptions-item label="产品编码"> | ||
| 207 | + {{ rebateDetail.productCode }} | ||
| 208 | + </el-descriptions-item> | ||
| 209 | + <el-descriptions-item label="返利金额"> | ||
| 210 | + <span class="amount-text">¥{{ formatAmount(rebateDetail.rebateAmount) }}</span> | ||
| 211 | + </el-descriptions-item> | ||
| 212 | + <el-descriptions-item label="返利日期"> | ||
| 213 | + {{ rebateDetail.rebateDate }} | ||
| 214 | + </el-descriptions-item> | ||
| 215 | + <el-descriptions-item label="操作类型"> | ||
| 216 | + <el-tag :type="getOperateTypeTagType(rebateDetail.operateType)"> | ||
| 217 | + {{ rebateDetail.operateTypeText || getOperateTypeText(rebateDetail.operateType) }} | ||
| 218 | + </el-tag> | ||
| 219 | + </el-descriptions-item> | ||
| 220 | + <el-descriptions-item label="计算状态"> | ||
| 221 | + <el-tag :type="getCalcFlagTagType(rebateDetail.calcFlag)"> | ||
| 222 | + {{ rebateDetail.calcFlagText || getCalcFlagText(rebateDetail.calcFlag) }} | ||
| 223 | + </el-tag> | ||
| 224 | + </el-descriptions-item> | ||
| 225 | + <el-descriptions-item label="创建时间"> | ||
| 226 | + {{ rebateDetail.createTime ? formatDate(rebateDetail.createTime) : '-' }} | ||
| 227 | + </el-descriptions-item> | ||
| 228 | + <el-descriptions-item label="更新时间"> | ||
| 229 | + {{ rebateDetail.updateTime ? formatDate(rebateDetail.updateTime) : '-' }} | ||
| 230 | + </el-descriptions-item> | ||
| 231 | + <el-descriptions-item label="上传时间"> | ||
| 232 | + {{ rebateDetail.uploadTime ? formatDate(rebateDetail.uploadTime) : '-' }} | ||
| 233 | + </el-descriptions-item> | ||
| 234 | + </el-descriptions> | ||
| 235 | + </div> | ||
| 236 | + | ||
| 237 | + <template #footer> | ||
| 238 | + <el-button @click="detailDialogVisible = false">关闭</el-button> | ||
| 239 | + </template> | ||
| 240 | + </el-dialog> | ||
| 241 | + | ||
| 242 | + <!-- 新增/编辑返利对话框 --> | ||
| 243 | + <el-dialog | ||
| 244 | + v-model="formDialogVisible" | ||
| 245 | + :title="isEdit ? '编辑返利' : '新增返利'" | ||
| 246 | + width="600px" | ||
| 247 | + :close-on-click-modal="false" | ||
| 248 | + > | ||
| 249 | + <el-form | ||
| 250 | + ref="rebateFormRef" | ||
| 251 | + :model="rebateForm" | ||
| 252 | + :rules="rebateFormRules" | ||
| 253 | + label-width="100px" | ||
| 254 | + > | ||
| 255 | + <el-form-item label="经销商" prop="dealerName"> | ||
| 256 | + <el-input v-model="rebateForm.dealerName" placeholder="请输入经销商名称" /> | ||
| 257 | + </el-form-item> | ||
| 258 | + | ||
| 259 | + <el-form-item label="产品名称" prop="productName"> | ||
| 260 | + <el-input v-model="rebateForm.productName" placeholder="请输入产品名称" /> | ||
| 261 | + </el-form-item> | ||
| 262 | + | ||
| 263 | + <el-form-item label="产品型号"> | ||
| 264 | + <el-input v-model="rebateForm.productModel" placeholder="请输入产品型号" /> | ||
| 265 | + </el-form-item> | ||
| 266 | + | ||
| 267 | + <el-form-item label="返利类型" prop="rebateType"> | ||
| 268 | + <el-select v-model="rebateForm.rebateType" placeholder="请选择返利类型" style="width: 100%"> | ||
| 269 | + <el-option label="销量返利" :value="1" /> | ||
| 270 | + <el-option label="业绩返利" :value="2" /> | ||
| 271 | + <el-option label="年度返利" :value="3" /> | ||
| 272 | + <el-option label="特殊返利" :value="4" /> | ||
| 273 | + </el-select> | ||
| 274 | + </el-form-item> | ||
| 275 | + | ||
| 276 | + <el-form-item label="返利政策" prop="policyName"> | ||
| 277 | + <el-input v-model="rebateForm.policyName" placeholder="请输入返利政策名称" /> | ||
| 278 | + </el-form-item> | ||
| 279 | + | ||
| 280 | + <el-form-item label="计算基数" prop="calculationBase"> | ||
| 281 | + <el-input-number | ||
| 282 | + v-model="rebateForm.calculationBase" | ||
| 283 | + :precision="2" | ||
| 284 | + :min="0" | ||
| 285 | + style="width: 100%" | ||
| 286 | + placeholder="请输入计算基数" | ||
| 287 | + /> | ||
| 288 | + </el-form-item> | ||
| 289 | + | ||
| 290 | + <el-form-item label="返利比例(%)" prop="rebateRate"> | ||
| 291 | + <el-input-number | ||
| 292 | + v-model="rebateForm.rebateRate" | ||
| 293 | + :precision="2" | ||
| 294 | + :min="0" | ||
| 295 | + :max="100" | ||
| 296 | + style="width: 100%" | ||
| 297 | + placeholder="请输入返利比例" | ||
| 298 | + /> | ||
| 299 | + </el-form-item> | ||
| 300 | + | ||
| 301 | + <el-form-item label="返利金额" prop="rebateAmount"> | ||
| 302 | + <el-input-number | ||
| 303 | + v-model="rebateForm.rebateAmount" | ||
| 304 | + :precision="2" | ||
| 305 | + :min="0" | ||
| 306 | + style="width: 100%" | ||
| 307 | + placeholder="请输入返利金额" | ||
| 308 | + /> | ||
| 309 | + </el-form-item> | ||
| 310 | + | ||
| 311 | + <el-form-item label="返利周期" prop="periodRange"> | ||
| 312 | + <el-date-picker | ||
| 313 | + v-model="periodRange" | ||
| 314 | + type="datetimerange" | ||
| 315 | + range-separator="至" | ||
| 316 | + start-placeholder="开始时间" | ||
| 317 | + end-placeholder="结束时间" | ||
| 318 | + format="YYYY-MM-DD HH:mm:ss" | ||
| 319 | + value-format="YYYY-MM-DD HH:mm:ss" | ||
| 320 | + style="width: 100%" | ||
| 321 | + /> | ||
| 322 | + </el-form-item> | ||
| 323 | + | ||
| 324 | + <el-form-item label="申请备注"> | ||
| 325 | + <el-input | ||
| 326 | + v-model="rebateForm.applyRemark" | ||
| 327 | + type="textarea" | ||
| 328 | + :rows="3" | ||
| 329 | + placeholder="请输入申请备注" | ||
| 330 | + /> | ||
| 331 | + </el-form-item> | ||
| 332 | + </el-form> | ||
| 333 | + | ||
| 334 | + <template #footer> | ||
| 335 | + <el-button @click="formDialogVisible = false">取消</el-button> | ||
| 336 | + <el-button type="primary" @click="handleSubmit" :loading="submitLoading"> | ||
| 337 | + 确定 | ||
| 338 | + </el-button> | ||
| 339 | + </template> | ||
| 340 | + </el-dialog> | ||
| 341 | + | ||
| 342 | + <!-- 审核对话框 --> | ||
| 343 | + <el-dialog | ||
| 344 | + v-model="auditDialogVisible" | ||
| 345 | + title="审核返利" | ||
| 346 | + width="500px" | ||
| 347 | + :close-on-click-modal="false" | ||
| 348 | + > | ||
| 349 | + <el-form | ||
| 350 | + ref="auditFormRef" | ||
| 351 | + :model="auditForm" | ||
| 352 | + :rules="auditFormRules" | ||
| 353 | + label-width="100px" | ||
| 354 | + > | ||
| 355 | + <el-form-item label="审核结果" prop="status"> | ||
| 356 | + <el-radio-group v-model="auditForm.status"> | ||
| 357 | + <el-radio :label="1">审核通过</el-radio> | ||
| 358 | + <el-radio :label="3">审核拒绝</el-radio> | ||
| 359 | + </el-radio-group> | ||
| 360 | + </el-form-item> | ||
| 361 | + | ||
| 362 | + <el-form-item label="审核备注"> | ||
| 363 | + <el-input | ||
| 364 | + v-model="auditForm.auditRemark" | ||
| 365 | + type="textarea" | ||
| 366 | + :rows="4" | ||
| 367 | + placeholder="请输入审核备注" | ||
| 368 | + /> | ||
| 369 | + </el-form-item> | ||
| 370 | + </el-form> | ||
| 371 | + | ||
| 372 | + <template #footer> | ||
| 373 | + <el-button @click="auditDialogVisible = false">取消</el-button> | ||
| 374 | + <el-button type="primary" @click="handleSubmitAudit" :loading="auditLoading"> | ||
| 375 | + 确定 | ||
| 376 | + </el-button> | ||
| 377 | + </template> | ||
| 378 | + </el-dialog> | ||
| 379 | + </div> | ||
| 380 | +</template> | ||
| 381 | + | ||
| 382 | +<script setup lang="ts"> | ||
| 383 | +import { ref, reactive, onMounted } from 'vue' | ||
| 384 | +import { ElMessage, ElMessageBox } from 'element-plus' | ||
| 385 | +import { Search, Refresh, Plus, Check, Close, Money, Delete, Download } from '@element-plus/icons-vue' | ||
| 386 | +import * as echarts from 'echarts' | ||
| 387 | +import rebateApi, { type Rebate, type RebateSearchParams } from '@/api/rebate' | ||
| 388 | +import { formatDate } from '@/utils/index' | ||
| 389 | + | ||
| 390 | +// 响应式数据 | ||
| 391 | +const loading = ref(false) | ||
| 392 | +const submitLoading = ref(false) | ||
| 393 | +const auditLoading = ref(false) | ||
| 394 | +const rebateList = ref<Rebate[]>([]) | ||
| 395 | +const selectedRebates = ref<Rebate[]>([]) | ||
| 396 | +const rebateDetail = ref<Rebate | null>(null) | ||
| 397 | + | ||
| 398 | +// 图表引用 | ||
| 399 | +const trendChart = ref<HTMLElement>() | ||
| 400 | +const pieChart = ref<HTMLElement>() | ||
| 401 | + | ||
| 402 | +// 日期范围 | ||
| 403 | +const dateRange = ref<[string, string] | null>(null) | ||
| 404 | + | ||
| 405 | +// 对话框显示状态 | ||
| 406 | +const detailDialogVisible = ref(false) | ||
| 407 | +const formDialogVisible = ref(false) | ||
| 408 | +const auditDialogVisible = ref(false) | ||
| 409 | +const isEdit = ref(false) | ||
| 410 | + | ||
| 411 | +// 分页数据 | ||
| 412 | +const pagination = reactive({ | ||
| 413 | + pageNum: 1, | ||
| 414 | + pageSize: 10, | ||
| 415 | + total: 0 | ||
| 416 | +}) | ||
| 417 | + | ||
| 418 | +// 页面跳转 | ||
| 419 | +const jumpPage = ref<number | string>('') | ||
| 420 | + | ||
| 421 | +// 搜索表单 | ||
| 422 | +const searchForm = reactive<RebateSearchParams>({ | ||
| 423 | + pageNum: 1, | ||
| 424 | + pageSize: 10, | ||
| 425 | + rebateNo: '', | ||
| 426 | + dealerCode: '', | ||
| 427 | + dealerName: '', | ||
| 428 | + productCode: '', | ||
| 429 | + operateType: undefined, | ||
| 430 | + calcFlag: undefined, | ||
| 431 | + rebateStartDate: '', | ||
| 432 | + rebateEndDate: '' | ||
| 433 | +}) | ||
| 434 | + | ||
| 435 | +// 日期范围 | ||
| 436 | +const applyDateRange = ref<[string, string] | null>(null) | ||
| 437 | +const periodRange = ref<[string, string] | null>(null) | ||
| 438 | + | ||
| 439 | +// 返利表单 | ||
| 440 | +const rebateFormRef = ref() | ||
| 441 | +const rebateForm = reactive({ | ||
| 442 | + rebateId: undefined, | ||
| 443 | + dealerId: 1, // 这里应该从经销商选择器获取 | ||
| 444 | + dealerName: '', | ||
| 445 | + productId: 1, // 这里应该从产品选择器获取 | ||
| 446 | + productName: '', | ||
| 447 | + productModel: '', | ||
| 448 | + rebateType: undefined, | ||
| 449 | + policyName: '', | ||
| 450 | + calculationBase: undefined, | ||
| 451 | + rebateRate: undefined, | ||
| 452 | + rebateAmount: undefined, | ||
| 453 | + periodStart: '', | ||
| 454 | + periodEnd: '', | ||
| 455 | + applyRemark: '' | ||
| 456 | +}) | ||
| 457 | + | ||
| 458 | +// 审核表单 | ||
| 459 | +const auditFormRef = ref() | ||
| 460 | +const auditForm = reactive({ | ||
| 461 | + rebateId: undefined as number | undefined, | ||
| 462 | + status: undefined as number | undefined, | ||
| 463 | + auditRemark: '' | ||
| 464 | +}) | ||
| 465 | + | ||
| 466 | +// 表单验证规则 | ||
| 467 | +const rebateFormRules = { | ||
| 468 | + dealerName: [ | ||
| 469 | + { required: true, message: '请输入经销商名称', trigger: 'blur' } | ||
| 470 | + ], | ||
| 471 | + productName: [ | ||
| 472 | + { required: true, message: '请输入产品名称', trigger: 'blur' } | ||
| 473 | + ], | ||
| 474 | + rebateType: [ | ||
| 475 | + { required: true, message: '请选择返利类型', trigger: 'change' } | ||
| 476 | + ], | ||
| 477 | + policyName: [ | ||
| 478 | + { required: true, message: '请输入返利政策名称', trigger: 'blur' } | ||
| 479 | + ], | ||
| 480 | + calculationBase: [ | ||
| 481 | + { required: true, message: '请输入计算基数', trigger: 'blur' } | ||
| 482 | + ], | ||
| 483 | + rebateRate: [ | ||
| 484 | + { required: true, message: '请输入返利比例', trigger: 'blur' } | ||
| 485 | + ], | ||
| 486 | + rebateAmount: [ | ||
| 487 | + { required: true, message: '请输入返利金额', trigger: 'blur' } | ||
| 488 | + ] | ||
| 489 | +} | ||
| 490 | + | ||
| 491 | +const auditFormRules = { | ||
| 492 | + status: [ | ||
| 493 | + { required: true, message: '请选择审核结果', trigger: 'change' } | ||
| 494 | + ] | ||
| 495 | +} | ||
| 496 | + | ||
| 497 | +// 获取返利类型标签类型 | ||
| 498 | +const getRebateTypeTagType = (rebateType: number) => { | ||
| 499 | + switch (rebateType) { | ||
| 500 | + case 1: | ||
| 501 | + return 'primary' | ||
| 502 | + case 2: | ||
| 503 | + return 'success' | ||
| 504 | + case 3: | ||
| 505 | + return 'warning' | ||
| 506 | + case 4: | ||
| 507 | + return 'danger' | ||
| 508 | + default: | ||
| 509 | + return 'info' | ||
| 510 | + } | ||
| 511 | +} | ||
| 512 | + | ||
| 513 | +// 获取状态标签类型 | ||
| 514 | +const getStatusTagType = (status: number) => { | ||
| 515 | + switch (status) { | ||
| 516 | + case 0: | ||
| 517 | + return 'warning' | ||
| 518 | + case 1: | ||
| 519 | + return 'success' | ||
| 520 | + case 2: | ||
| 521 | + return 'primary' | ||
| 522 | + case 3: | ||
| 523 | + return 'danger' | ||
| 524 | + default: | ||
| 525 | + return 'info' | ||
| 526 | + } | ||
| 527 | +} | ||
| 528 | + | ||
| 529 | +// 格式化金额 | ||
| 530 | +const formatAmount = (amount: number) => { | ||
| 531 | + return amount ? amount.toLocaleString('zh-CN', { minimumFractionDigits: 2 }) : '0.00' | ||
| 532 | +} | ||
| 533 | + | ||
| 534 | +// 获取操作类型文本 | ||
| 535 | +const getOperateTypeText = (operateType: number) => { | ||
| 536 | + switch (operateType) { | ||
| 537 | + case 1: return '新增' | ||
| 538 | + case 2: return '修改' | ||
| 539 | + case 3: return '删除' | ||
| 540 | + default: return '未知' | ||
| 541 | + } | ||
| 542 | +} | ||
| 543 | + | ||
| 544 | +// 获取操作类型标签类型 | ||
| 545 | +const getOperateTypeTagType = (operateType: number) => { | ||
| 546 | + switch (operateType) { | ||
| 547 | + case 1: return 'success' | ||
| 548 | + case 2: return 'warning' | ||
| 549 | + case 3: return 'danger' | ||
| 550 | + default: return 'info' | ||
| 551 | + } | ||
| 552 | +} | ||
| 553 | + | ||
| 554 | +// 获取计算状态文本 | ||
| 555 | +const getCalcFlagText = (calcFlag: number | undefined) => { | ||
| 556 | + switch (calcFlag) { | ||
| 557 | + case 0: return '未计算' | ||
| 558 | + case 1: return '已计算' | ||
| 559 | + default: return '未知' | ||
| 560 | + } | ||
| 561 | +} | ||
| 562 | + | ||
| 563 | +// 获取计算状态标签类型 | ||
| 564 | +const getCalcFlagTagType = (calcFlag: number | undefined) => { | ||
| 565 | + switch (calcFlag) { | ||
| 566 | + case 0: return 'warning' | ||
| 567 | + case 1: return 'success' | ||
| 568 | + default: return 'info' | ||
| 569 | + } | ||
| 570 | +} | ||
| 571 | + | ||
| 572 | +// 获取返利列表 | ||
| 573 | +const getRebateList = async () => { | ||
| 574 | + try { | ||
| 575 | + loading.value = true | ||
| 576 | + const params = { | ||
| 577 | + ...searchForm, | ||
| 578 | + pageNum: pagination.pageNum, | ||
| 579 | + pageSize: pagination.pageSize | ||
| 580 | + } | ||
| 581 | + | ||
| 582 | + console.log('请求参数:', params) | ||
| 583 | + const response = await rebateApi.getRebatePage(params) | ||
| 584 | + console.log('完整API响应:', response) | ||
| 585 | + console.log('响应数据结构:', response.data) | ||
| 586 | + console.log('响应对象类型:', typeof response) | ||
| 587 | + console.log('响应对象键:', Object.keys(response || {})) | ||
| 588 | + | ||
| 589 | + // 检查不同层级的数据 | ||
| 590 | + if (response) { | ||
| 591 | + console.log('response存在') | ||
| 592 | + if (response.data !== undefined) { | ||
| 593 | + console.log('response.data存在:', response.data) | ||
| 594 | + } else { | ||
| 595 | + console.log('response.data不存在,检查其他字段') | ||
| 596 | + console.log('response直接内容:', response) | ||
| 597 | + } | ||
| 598 | + } | ||
| 599 | + | ||
| 600 | + // 根据后端实际返回的数据结构进行解析 | ||
| 601 | + let dataList = [] | ||
| 602 | + let total = 0 | ||
| 603 | + | ||
| 604 | + if (response) { | ||
| 605 | + let responseData = null | ||
| 606 | + | ||
| 607 | + // 首先尝试获取数据,response.data可能不存在 | ||
| 608 | + if (response.data !== undefined) { | ||
| 609 | + responseData = response.data | ||
| 610 | + console.log('使用response.data:', responseData) | ||
| 611 | + } else { | ||
| 612 | + // 如果response.data不存在,直接使用response | ||
| 613 | + responseData = response | ||
| 614 | + console.log('直接使用response:', responseData) | ||
| 615 | + } | ||
| 616 | + | ||
| 617 | + if (responseData) { | ||
| 618 | + // 情况1: 直接返回数组 | ||
| 619 | + if (Array.isArray(responseData)) { | ||
| 620 | + dataList = responseData | ||
| 621 | + total = responseData.length | ||
| 622 | + console.log('情况1: 直接数组,长度:', dataList.length) | ||
| 623 | + } | ||
| 624 | + // 情况2: 包含records字段(MyBatis Plus分页格式) | ||
| 625 | + else if (responseData.records && Array.isArray(responseData.records)) { | ||
| 626 | + dataList = responseData.records | ||
| 627 | + total = responseData.total || responseData.records.length | ||
| 628 | + console.log('情况2: records格式,长度:', dataList.length, '总数:', total) | ||
| 629 | + } | ||
| 630 | + // 情况3: 包含data字段的嵌套结构 | ||
| 631 | + else if (responseData.data) { | ||
| 632 | + if (Array.isArray(responseData.data)) { | ||
| 633 | + dataList = responseData.data | ||
| 634 | + total = responseData.total || responseData.data.length | ||
| 635 | + console.log('情况3a: data数组格式,长度:', dataList.length) | ||
| 636 | + } else if (responseData.data.records) { | ||
| 637 | + dataList = responseData.data.records | ||
| 638 | + total = responseData.data.total || 0 | ||
| 639 | + console.log('情况3b: data.records格式,长度:', dataList.length) | ||
| 640 | + } | ||
| 641 | + } | ||
| 642 | + // 情况4: 其他对象格式,寻找数组字段 | ||
| 643 | + else if (typeof responseData === 'object' && responseData !== null) { | ||
| 644 | + const keys = Object.keys(responseData) | ||
| 645 | + console.log('对象的所有键:', keys) | ||
| 646 | + | ||
| 647 | + // 寻找可能的数组字段 | ||
| 648 | + const arrayFields = keys.filter(key => Array.isArray(responseData[key])) | ||
| 649 | + console.log('数组字段:', arrayFields) | ||
| 650 | + | ||
| 651 | + if (arrayFields.length > 0) { | ||
| 652 | + const arrayField = arrayFields[0] | ||
| 653 | + dataList = responseData[arrayField] | ||
| 654 | + total = responseData.total || responseData.count || dataList.length | ||
| 655 | + console.log('情况4: 找到数组字段', arrayField, '长度:', dataList.length) | ||
| 656 | + } | ||
| 657 | + } | ||
| 658 | + } | ||
| 659 | + } | ||
| 660 | + | ||
| 661 | + rebateList.value = dataList | ||
| 662 | + pagination.total = total | ||
| 663 | + | ||
| 664 | + console.log('最终解析的列表数据:', rebateList.value) | ||
| 665 | + console.log('数据条数:', rebateList.value.length) | ||
| 666 | + console.log('分页总数:', pagination.total) | ||
| 667 | + | ||
| 668 | + if (rebateList.value.length === 0) { | ||
| 669 | + console.warn('解析后的数据为空,请检查数据结构') | ||
| 670 | + console.warn('如果需要测试,可以取消注释下面的模拟数据') | ||
| 671 | + | ||
| 672 | + // 临时模拟数据用于测试(可以取消注释来测试表格显示) | ||
| 673 | + /* | ||
| 674 | + rebateList.value = [ | ||
| 675 | + { | ||
| 676 | + rebateId: 1, | ||
| 677 | + rebateNo: 'RB2025-001', | ||
| 678 | + orderNo: 'ORD-2025-001', | ||
| 679 | + dealerCode: 'DEALER001', | ||
| 680 | + dealerName: '测试经销商1', | ||
| 681 | + productCode: 'PROD001', | ||
| 682 | + rebateAmount: 1000.00, | ||
| 683 | + rebateDate: '2025-01-15', | ||
| 684 | + operateType: 1, | ||
| 685 | + calcFlag: 0, | ||
| 686 | + createTime: '2025-01-15 10:00:00', | ||
| 687 | + updateTime: '2025-01-15 10:00:00' | ||
| 688 | + } | ||
| 689 | + ] | ||
| 690 | + pagination.total = 1 | ||
| 691 | + */ | ||
| 692 | + } | ||
| 693 | + | ||
| 694 | + } catch (error) { | ||
| 695 | + console.error('获取返利列表失败:', error) | ||
| 696 | + ElMessage.error('获取返利列表失败') | ||
| 697 | + rebateList.value = [] | ||
| 698 | + pagination.total = 0 | ||
| 699 | + } finally { | ||
| 700 | + loading.value = false | ||
| 701 | + } | ||
| 702 | +} | ||
| 703 | + | ||
| 704 | +// 处理日期范围变化 | ||
| 705 | +const handleDateRangeChange = (dates: [string, string] | null) => { | ||
| 706 | + if (dates) { | ||
| 707 | + searchForm.rebateStartDate = dates[0] | ||
| 708 | + searchForm.rebateEndDate = dates[1] | ||
| 709 | + } else { | ||
| 710 | + searchForm.rebateStartDate = '' | ||
| 711 | + searchForm.rebateEndDate = '' | ||
| 712 | + } | ||
| 713 | +} | ||
| 714 | + | ||
| 715 | +// 搜索 | ||
| 716 | +const handleSearch = () => { | ||
| 717 | + pagination.pageNum = 1 | ||
| 718 | + getRebateList() | ||
| 719 | +} | ||
| 720 | + | ||
| 721 | +// 重置 | ||
| 722 | +const handleReset = () => { | ||
| 723 | + Object.assign(searchForm, { | ||
| 724 | + pageNum: 1, | ||
| 725 | + pageSize: 10, | ||
| 726 | + rebateNo: undefined, | ||
| 727 | + dealerName: undefined, | ||
| 728 | + productName: undefined, | ||
| 729 | + rebateType: undefined, | ||
| 730 | + status: undefined, | ||
| 731 | + applyStartTime: undefined, | ||
| 732 | + applyEndTime: undefined | ||
| 733 | + }) | ||
| 734 | + applyDateRange.value = null | ||
| 735 | + pagination.pageNum = 1 | ||
| 736 | + getRebateList() | ||
| 737 | +} | ||
| 738 | + | ||
| 739 | +// 刷新 | ||
| 740 | +const handleRefresh = () => { | ||
| 741 | + getRebateList() | ||
| 742 | +} | ||
| 743 | + | ||
| 744 | +// 分页变化 | ||
| 745 | +const handleSizeChange = (size: number) => { | ||
| 746 | + pagination.pageSize = size | ||
| 747 | + pagination.pageNum = 1 | ||
| 748 | + getRebateList() | ||
| 749 | +} | ||
| 750 | + | ||
| 751 | +const handleCurrentChange = (page: number) => { | ||
| 752 | + pagination.pageNum = page | ||
| 753 | + getRebateList() | ||
| 754 | +} | ||
| 755 | + | ||
| 756 | +// 页面跳转 | ||
| 757 | +const handleJumpPage = () => { | ||
| 758 | + const page = Number(jumpPage.value) | ||
| 759 | + if (page && page > 0 && page <= Math.ceil(pagination.total / pagination.pageSize)) { | ||
| 760 | + pagination.pageNum = page | ||
| 761 | + getRebateList() | ||
| 762 | + jumpPage.value = '' | ||
| 763 | + } | ||
| 764 | +} | ||
| 765 | + | ||
| 766 | +// 选择变化 | ||
| 767 | +const handleSelectionChange = (selection: Rebate[]) => { | ||
| 768 | + selectedRebates.value = selection | ||
| 769 | +} | ||
| 770 | + | ||
| 771 | +// 查看详情 | ||
| 772 | +const handleViewDetail = async (row: Rebate) => { | ||
| 773 | + try { | ||
| 774 | + if (!row.rebateId) return | ||
| 775 | + console.log('查看详情,返利ID:', row.rebateId) | ||
| 776 | + | ||
| 777 | + const response = await rebateApi.getRebateById(row.rebateId) | ||
| 778 | + console.log('详情接口响应:', response) | ||
| 779 | + | ||
| 780 | + // 根据request.ts拦截器的处理,response.data已经是实际数据 | ||
| 781 | + if (response && response.data) { | ||
| 782 | + rebateDetail.value = response.data | ||
| 783 | + detailDialogVisible.value = true | ||
| 784 | + console.log('详情数据:', response.data) | ||
| 785 | + } else { | ||
| 786 | + ElMessage.error('获取返利详情失败') | ||
| 787 | + } | ||
| 788 | + } catch (error) { | ||
| 789 | + console.error('获取返利详情失败:', error) | ||
| 790 | + ElMessage.error('获取返利详情失败') | ||
| 791 | + } | ||
| 792 | +} | ||
| 793 | + | ||
| 794 | +// 新增 | ||
| 795 | +const handleAdd = () => { | ||
| 796 | + isEdit.value = false | ||
| 797 | + resetRebateForm() | ||
| 798 | + formDialogVisible.value = true | ||
| 799 | +} | ||
| 800 | + | ||
| 801 | +// 编辑 | ||
| 802 | +const handleEdit = (row: Rebate) => { | ||
| 803 | + ElMessage.info(`编辑返利记录:${row.rebateNo}`) | ||
| 804 | + // 这里可以打开编辑对话框或跳转到编辑页面 | ||
| 805 | +} | ||
| 806 | + | ||
| 807 | +// 标记已计算 | ||
| 808 | +const handleMarkCalculated = async (row: Rebate) => { | ||
| 809 | + try { | ||
| 810 | + await ElMessageBox.confirm('确认标记该返利为已计算吗?', '确认操作', { | ||
| 811 | + type: 'warning' | ||
| 812 | + }) | ||
| 813 | + | ||
| 814 | + await rebateApi.markRebateCalculated(row.rebateId!) | ||
| 815 | + ElMessage.success('标记成功') | ||
| 816 | + getRebateList() | ||
| 817 | + } catch (error) { | ||
| 818 | + if (error !== 'cancel') { | ||
| 819 | + console.error('标记失败:', error) | ||
| 820 | + ElMessage.error('标记失败') | ||
| 821 | + } | ||
| 822 | + } | ||
| 823 | +} | ||
| 824 | + | ||
| 825 | +// 重置表单 | ||
| 826 | +const resetRebateForm = () => { | ||
| 827 | + Object.assign(rebateForm, { | ||
| 828 | + rebateId: undefined, | ||
| 829 | + dealerId: 1, | ||
| 830 | + dealerName: '', | ||
| 831 | + productId: 1, | ||
| 832 | + productName: '', | ||
| 833 | + productModel: '', | ||
| 834 | + rebateType: undefined, | ||
| 835 | + policyName: '', | ||
| 836 | + calculationBase: undefined, | ||
| 837 | + rebateRate: undefined, | ||
| 838 | + rebateAmount: undefined, | ||
| 839 | + periodStart: '', | ||
| 840 | + periodEnd: '', | ||
| 841 | + applyRemark: '' | ||
| 842 | + }) | ||
| 843 | + periodRange.value = null | ||
| 844 | + rebateFormRef.value?.clearValidate() | ||
| 845 | +} | ||
| 846 | + | ||
| 847 | +// 提交表单 | ||
| 848 | +const handleSubmit = async () => { | ||
| 849 | + try { | ||
| 850 | + await rebateFormRef.value?.validate() | ||
| 851 | + | ||
| 852 | + if (periodRange.value) { | ||
| 853 | + rebateForm.periodStart = periodRange.value[0] | ||
| 854 | + rebateForm.periodEnd = periodRange.value[1] | ||
| 855 | + } | ||
| 856 | + | ||
| 857 | + submitLoading.value = true | ||
| 858 | + | ||
| 859 | + if (isEdit.value) { | ||
| 860 | + await rebateApi.updateRebate(rebateForm as any) | ||
| 861 | + ElMessage.success('更新返利成功') | ||
| 862 | + } else { | ||
| 863 | + await rebateApi.addRebate(rebateForm as any) | ||
| 864 | + ElMessage.success('新增返利成功') | ||
| 865 | + } | ||
| 866 | + | ||
| 867 | + formDialogVisible.value = false | ||
| 868 | + getRebateList() | ||
| 869 | + } catch (error) { | ||
| 870 | + console.error('提交表单失败:', error) | ||
| 871 | + ElMessage.error('操作失败') | ||
| 872 | + } finally { | ||
| 873 | + submitLoading.value = false | ||
| 874 | + } | ||
| 875 | +} | ||
| 876 | + | ||
| 877 | +// 删除 | ||
| 878 | +const handleDelete = async (row: Rebate) => { | ||
| 879 | + try { | ||
| 880 | + await ElMessageBox.confirm('确认删除该返利记录吗?', '确认删除', { | ||
| 881 | + type: 'warning' | ||
| 882 | + }) | ||
| 883 | + | ||
| 884 | + const { data } = await rebateApi.deleteRebate(row.rebateId!) | ||
| 885 | + if (data.code === 200) { | ||
| 886 | + ElMessage.success('删除成功') | ||
| 887 | + getRebateList() | ||
| 888 | + } else { | ||
| 889 | + ElMessage.error(data.message || '删除失败') | ||
| 890 | + } | ||
| 891 | + } catch (error) { | ||
| 892 | + if (error !== 'cancel') { | ||
| 893 | + console.error('删除返利失败:', error) | ||
| 894 | + ElMessage.error('删除返利失败') | ||
| 895 | + } | ||
| 896 | + } | ||
| 897 | +} | ||
| 898 | + | ||
| 899 | +// 批量标记已计算 | ||
| 900 | +const handleBatchMarkCalculated = async () => { | ||
| 901 | + try { | ||
| 902 | + if (selectedRebates.value.length === 0) { | ||
| 903 | + ElMessage.warning('请选择要标记的记录') | ||
| 904 | + return | ||
| 905 | + } | ||
| 906 | + | ||
| 907 | + await ElMessageBox.confirm(`确认标记选中的 ${selectedRebates.value.length} 条返利记录为已计算吗?`, '确认操作', { | ||
| 908 | + type: 'warning' | ||
| 909 | + }) | ||
| 910 | + | ||
| 911 | + const rebateIds = selectedRebates.value.map(item => item.rebateId!) | ||
| 912 | + const { data } = await rebateApi.batchMarkRebateCalculated(rebateIds) | ||
| 913 | + | ||
| 914 | + if (data.code === 200) { | ||
| 915 | + ElMessage.success('批量标记成功') | ||
| 916 | + getRebateList() | ||
| 917 | + } else { | ||
| 918 | + ElMessage.error(data.message || '批量标记失败') | ||
| 919 | + } | ||
| 920 | + } catch (error) { | ||
| 921 | + if (error !== 'cancel') { | ||
| 922 | + console.error('批量标记失败:', error) | ||
| 923 | + ElMessage.error('批量标记失败') | ||
| 924 | + } | ||
| 925 | + } | ||
| 926 | +} | ||
| 927 | + | ||
| 928 | +// 批量删除 | ||
| 929 | +const handleBatchDelete = async () => { | ||
| 930 | + try { | ||
| 931 | + if (selectedRebates.value.length === 0) { | ||
| 932 | + ElMessage.warning('请选择要删除的记录') | ||
| 933 | + return | ||
| 934 | + } | ||
| 935 | + | ||
| 936 | + await ElMessageBox.confirm(`确认删除选中的 ${selectedRebates.value.length} 条返利记录吗?`, '确认删除', { | ||
| 937 | + type: 'warning' | ||
| 938 | + }) | ||
| 939 | + | ||
| 940 | + const rebateIds = selectedRebates.value.map(item => item.rebateId!) | ||
| 941 | + const { data } = await rebateApi.batchDeleteRebate(rebateIds) | ||
| 942 | + | ||
| 943 | + if (data.code === 200) { | ||
| 944 | + ElMessage.success('批量删除成功') | ||
| 945 | + getRebateList() | ||
| 946 | + } else { | ||
| 947 | + ElMessage.error(data.message || '批量删除失败') | ||
| 948 | + } | ||
| 949 | + } catch (error) { | ||
| 950 | + if (error !== 'cancel') { | ||
| 951 | + console.error('批量删除返利失败:', error) | ||
| 952 | + ElMessage.error('批量删除返利失败') | ||
| 953 | + } | ||
| 954 | + } | ||
| 955 | +} | ||
| 956 | + | ||
| 957 | +// 审核 | ||
| 958 | +const handleAudit = (row: Rebate) => { | ||
| 959 | + auditForm.rebateId = row.rebateId | ||
| 960 | + auditForm.status = undefined | ||
| 961 | + auditForm.auditRemark = '' | ||
| 962 | + auditDialogVisible.value = true | ||
| 963 | +} | ||
| 964 | + | ||
| 965 | +// 提交审核 | ||
| 966 | +const handleSubmitAudit = async () => { | ||
| 967 | + try { | ||
| 968 | + await auditFormRef.value?.validate() | ||
| 969 | + | ||
| 970 | + auditLoading.value = true | ||
| 971 | + // 模拟审核操作 | ||
| 972 | + ElMessage.success('审核返利成功') | ||
| 973 | + | ||
| 974 | + auditDialogVisible.value = false | ||
| 975 | + getRebateList() | ||
| 976 | + } catch (error) { | ||
| 977 | + console.error('审核返利失败:', error) | ||
| 978 | + ElMessage.error('审核返利失败') | ||
| 979 | + } finally { | ||
| 980 | + auditLoading.value = false | ||
| 981 | + } | ||
| 982 | +} | ||
| 983 | + | ||
| 984 | +// 批量审核 | ||
| 985 | +const handleBatchAudit = async (status: number) => { | ||
| 986 | + try { | ||
| 987 | + const statusText = status === 1 ? '通过' : '拒绝' | ||
| 988 | + await ElMessageBox.confirm(`确认批量审核${statusText}选中的 ${selectedRebates.value.length} 条返利记录吗?`, '确认审核', { | ||
| 989 | + type: 'warning' | ||
| 990 | + }) | ||
| 991 | + | ||
| 992 | + // 模拟批量审核操作 | ||
| 993 | + ElMessage.success(`批量审核${statusText}成功`) | ||
| 994 | + getRebateList() | ||
| 995 | + } catch (error) { | ||
| 996 | + if (error !== 'cancel') { | ||
| 997 | + console.error('批量审核失败:', error) | ||
| 998 | + ElMessage.error('批量审核失败') | ||
| 999 | + } | ||
| 1000 | + } | ||
| 1001 | +} | ||
| 1002 | + | ||
| 1003 | +// 发放 | ||
| 1004 | +const handleRelease = async (row: any) => { | ||
| 1005 | + try { | ||
| 1006 | + await ElMessageBox.confirm('确认发放该返利吗?', '确认发放', { | ||
| 1007 | + type: 'warning' | ||
| 1008 | + }) | ||
| 1009 | + | ||
| 1010 | + // 模拟发放操作 | ||
| 1011 | + ElMessage.success('发放返利成功') | ||
| 1012 | + getRebateList() | ||
| 1013 | + } catch (error) { | ||
| 1014 | + if (error !== 'cancel') { | ||
| 1015 | + console.error('发放返利失败:', error) | ||
| 1016 | + ElMessage.error('发放返利失败') | ||
| 1017 | + } | ||
| 1018 | + } | ||
| 1019 | +} | ||
| 1020 | + | ||
| 1021 | +// 批量发放 | ||
| 1022 | +const handleBatchRelease = async () => { | ||
| 1023 | + try { | ||
| 1024 | + await ElMessageBox.confirm(`确认批量发放选中的 ${selectedRebates.value.length} 条返利记录吗?`, '确认发放', { | ||
| 1025 | + type: 'warning' | ||
| 1026 | + }) | ||
| 1027 | + | ||
| 1028 | + // 模拟批量发放操作 | ||
| 1029 | + ElMessage.success('批量发放返利成功') | ||
| 1030 | + getRebateList() | ||
| 1031 | + } catch (error) { | ||
| 1032 | + if (error !== 'cancel') { | ||
| 1033 | + console.error('批量发放返利失败:', error) | ||
| 1034 | + ElMessage.error('批量发放返利失败') | ||
| 1035 | + } | ||
| 1036 | + } | ||
| 1037 | +} | ||
| 1038 | + | ||
| 1039 | +// 导出 | ||
| 1040 | +const handleExport = async () => { | ||
| 1041 | + try { | ||
| 1042 | + loading.value = true | ||
| 1043 | + const params = { ...searchForm } | ||
| 1044 | + | ||
| 1045 | + const response = await rebateApi.exportRebate(params) | ||
| 1046 | + | ||
| 1047 | + // 创建下载链接 | ||
| 1048 | + const blob = new Blob([response.data], { | ||
| 1049 | + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' | ||
| 1050 | + }) | ||
| 1051 | + const url = window.URL.createObjectURL(blob) | ||
| 1052 | + const link = document.createElement('a') | ||
| 1053 | + link.href = url | ||
| 1054 | + link.download = `返利数据_${new Date().toISOString().split('T')[0]}.xlsx` | ||
| 1055 | + document.body.appendChild(link) | ||
| 1056 | + link.click() | ||
| 1057 | + document.body.removeChild(link) | ||
| 1058 | + window.URL.revokeObjectURL(url) | ||
| 1059 | + | ||
| 1060 | + ElMessage.success('导出成功') | ||
| 1061 | + } catch (error) { | ||
| 1062 | + console.error('导出失败:', error) | ||
| 1063 | + ElMessage.error('导出失败') | ||
| 1064 | + } finally { | ||
| 1065 | + loading.value = false | ||
| 1066 | + } | ||
| 1067 | +} | ||
| 1068 | + | ||
| 1069 | +// 初始化趋势图 | ||
| 1070 | +const initTrendChart = async () => { | ||
| 1071 | + if (!trendChart.value) return | ||
| 1072 | + | ||
| 1073 | + const chart = echarts.init(trendChart.value) | ||
| 1074 | + | ||
| 1075 | + try { | ||
| 1076 | + // 调用后端API获取月度统计数据 | ||
| 1077 | + const response = await rebateApi.getRebateMonthlyStats() | ||
| 1078 | + let monthlyData = [] | ||
| 1079 | + | ||
| 1080 | + if (response && response.data) { | ||
| 1081 | + monthlyData = response.data | ||
| 1082 | + } | ||
| 1083 | + | ||
| 1084 | + const months = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'] | ||
| 1085 | + | ||
| 1086 | + // 处理后端返回的月度数据 | ||
| 1087 | + const totalTrendData = months.map((month, index) => { | ||
| 1088 | + const monthIndex = index + 1 | ||
| 1089 | + const monthData = monthlyData.find((item: any) => item.month === monthIndex) | ||
| 1090 | + return monthData ? monthData.totalAmount : 0 | ||
| 1091 | + }) | ||
| 1092 | + | ||
| 1093 | + const calculatedTrendData = months.map((month, index) => { | ||
| 1094 | + const monthIndex = index + 1 | ||
| 1095 | + const monthData = monthlyData.find((item: any) => item.month === monthIndex) | ||
| 1096 | + return monthData ? monthData.calculatedAmount : 0 | ||
| 1097 | + }) | ||
| 1098 | + | ||
| 1099 | + const option = { | ||
| 1100 | + tooltip: { | ||
| 1101 | + trigger: 'axis', | ||
| 1102 | + formatter: (params: any) => { | ||
| 1103 | + let result = `${params[0].axisValue}<br/>` | ||
| 1104 | + params.forEach((param: any) => { | ||
| 1105 | + result += `${param.seriesName}: ¥${param.value.toLocaleString()}<br/>` | ||
| 1106 | + }) | ||
| 1107 | + return result | ||
| 1108 | + } | ||
| 1109 | + }, | ||
| 1110 | + grid: { | ||
| 1111 | + left: '8%', | ||
| 1112 | + right: '8%', | ||
| 1113 | + top: '15%', | ||
| 1114 | + bottom: '15%' | ||
| 1115 | + }, | ||
| 1116 | + xAxis: { | ||
| 1117 | + type: 'category', | ||
| 1118 | + boundaryGap: false, | ||
| 1119 | + data: months, | ||
| 1120 | + axisLine: { show: false }, | ||
| 1121 | + axisTick: { show: false }, | ||
| 1122 | + axisLabel: { | ||
| 1123 | + color: '#999', | ||
| 1124 | + fontSize: 12 | ||
| 1125 | + } | ||
| 1126 | + }, | ||
| 1127 | + yAxis: { | ||
| 1128 | + type: 'value', | ||
| 1129 | + axisLine: { show: false }, | ||
| 1130 | + axisTick: { show: false }, | ||
| 1131 | + splitLine: { | ||
| 1132 | + show: true, | ||
| 1133 | + lineStyle: { | ||
| 1134 | + color: '#f0f0f0', | ||
| 1135 | + type: 'dashed' | ||
| 1136 | + } | ||
| 1137 | + }, | ||
| 1138 | + axisLabel: { | ||
| 1139 | + color: '#999', | ||
| 1140 | + fontSize: 12, | ||
| 1141 | + formatter: (value: number) => `${(value / 1000).toFixed(0)}k` | ||
| 1142 | + } | ||
| 1143 | + }, | ||
| 1144 | + series: [ | ||
| 1145 | + { | ||
| 1146 | + name: '返利金额', | ||
| 1147 | + type: 'line', | ||
| 1148 | + smooth: true, | ||
| 1149 | + data: totalTrendData, | ||
| 1150 | + itemStyle: { color: '#4CAF50' }, | ||
| 1151 | + lineStyle: { | ||
| 1152 | + color: '#4CAF50', | ||
| 1153 | + width: 3 | ||
| 1154 | + }, | ||
| 1155 | + symbol: 'circle', | ||
| 1156 | + symbolSize: 6, | ||
| 1157 | + showSymbol: true | ||
| 1158 | + }, | ||
| 1159 | + { | ||
| 1160 | + name: '已审核返利', | ||
| 1161 | + type: 'line', | ||
| 1162 | + smooth: true, | ||
| 1163 | + data: calculatedTrendData, | ||
| 1164 | + itemStyle: { color: '#2196F3' }, | ||
| 1165 | + lineStyle: { | ||
| 1166 | + color: '#2196F3', | ||
| 1167 | + width: 3 | ||
| 1168 | + }, | ||
| 1169 | + symbol: 'circle', | ||
| 1170 | + symbolSize: 6, | ||
| 1171 | + showSymbol: true | ||
| 1172 | + } | ||
| 1173 | + ] | ||
| 1174 | + } | ||
| 1175 | + chart.setOption(option) | ||
| 1176 | + | ||
| 1177 | + } catch (error) { | ||
| 1178 | + console.error('获取趋势图数据失败:', error) | ||
| 1179 | + | ||
| 1180 | + // 如果API调用失败,使用当前列表数据作为备用方案 | ||
| 1181 | + const totalAmount = rebateList.value.reduce((sum, item) => sum + (item.rebateAmount || 0), 0) | ||
| 1182 | + const calculatedAmount = rebateList.value | ||
| 1183 | + .filter(item => item.calcFlag === 1) | ||
| 1184 | + .reduce((sum, item) => sum + (item.rebateAmount || 0), 0) | ||
| 1185 | + | ||
| 1186 | + const months = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'] | ||
| 1187 | + | ||
| 1188 | + const totalTrendData = months.map(() => Math.floor(totalAmount / 12)) | ||
| 1189 | + const calculatedTrendData = months.map(() => Math.floor(calculatedAmount / 12)) | ||
| 1190 | + | ||
| 1191 | + const fallbackOption = { | ||
| 1192 | + tooltip: { | ||
| 1193 | + trigger: 'axis', | ||
| 1194 | + formatter: (params: any) => { | ||
| 1195 | + let result = `${params[0].axisValue}<br/>` | ||
| 1196 | + params.forEach((param: any) => { | ||
| 1197 | + result += `${param.seriesName}: ¥${param.value.toLocaleString()}<br/>` | ||
| 1198 | + }) | ||
| 1199 | + return result | ||
| 1200 | + } | ||
| 1201 | + }, | ||
| 1202 | + grid: { | ||
| 1203 | + left: '8%', | ||
| 1204 | + right: '8%', | ||
| 1205 | + top: '15%', | ||
| 1206 | + bottom: '15%' | ||
| 1207 | + }, | ||
| 1208 | + xAxis: { | ||
| 1209 | + type: 'category', | ||
| 1210 | + boundaryGap: false, | ||
| 1211 | + data: months, | ||
| 1212 | + axisLine: { show: false }, | ||
| 1213 | + axisTick: { show: false }, | ||
| 1214 | + axisLabel: { | ||
| 1215 | + color: '#999', | ||
| 1216 | + fontSize: 12 | ||
| 1217 | + } | ||
| 1218 | + }, | ||
| 1219 | + yAxis: { | ||
| 1220 | + type: 'value', | ||
| 1221 | + axisLine: { show: false }, | ||
| 1222 | + axisTick: { show: false }, | ||
| 1223 | + splitLine: { | ||
| 1224 | + show: true, | ||
| 1225 | + lineStyle: { | ||
| 1226 | + color: '#f0f0f0', | ||
| 1227 | + type: 'dashed' | ||
| 1228 | + } | ||
| 1229 | + }, | ||
| 1230 | + axisLabel: { | ||
| 1231 | + color: '#999', | ||
| 1232 | + fontSize: 12, | ||
| 1233 | + formatter: (value: number) => `${(value / 1000).toFixed(0)}k` | ||
| 1234 | + } | ||
| 1235 | + }, | ||
| 1236 | + series: [ | ||
| 1237 | + { | ||
| 1238 | + name: '返利金额', | ||
| 1239 | + type: 'line', | ||
| 1240 | + smooth: true, | ||
| 1241 | + data: totalTrendData, | ||
| 1242 | + itemStyle: { color: '#4CAF50' }, | ||
| 1243 | + lineStyle: { | ||
| 1244 | + color: '#4CAF50', | ||
| 1245 | + width: 3 | ||
| 1246 | + }, | ||
| 1247 | + symbol: 'circle', | ||
| 1248 | + symbolSize: 6, | ||
| 1249 | + showSymbol: true | ||
| 1250 | + }, | ||
| 1251 | + { | ||
| 1252 | + name: '已审核返利', | ||
| 1253 | + type: 'line', | ||
| 1254 | + smooth: true, | ||
| 1255 | + data: calculatedTrendData, | ||
| 1256 | + itemStyle: { color: '#2196F3' }, | ||
| 1257 | + lineStyle: { | ||
| 1258 | + color: '#2196F3', | ||
| 1259 | + width: 3 | ||
| 1260 | + }, | ||
| 1261 | + symbol: 'circle', | ||
| 1262 | + symbolSize: 6, | ||
| 1263 | + showSymbol: true | ||
| 1264 | + } | ||
| 1265 | + ] | ||
| 1266 | + } | ||
| 1267 | + chart.setOption(fallbackOption) | ||
| 1268 | + } | ||
| 1269 | + | ||
| 1270 | + // 响应式调整 | ||
| 1271 | + window.addEventListener('resize', () => chart.resize()) | ||
| 1272 | +} | ||
| 1273 | + | ||
| 1274 | +// 初始化饼图 | ||
| 1275 | +const initPieChart = async () => { | ||
| 1276 | + if (!pieChart.value) return | ||
| 1277 | + | ||
| 1278 | + const chart = echarts.init(pieChart.value) | ||
| 1279 | + | ||
| 1280 | + try { | ||
| 1281 | + // 调用后端API获取状态统计数据 | ||
| 1282 | + const response = await rebateApi.getRebateStatusStats() | ||
| 1283 | + let statusData = { calculatedCount: 0, unCalculatedCount: 0 } | ||
| 1284 | + | ||
| 1285 | + if (response && response.data) { | ||
| 1286 | + statusData = response.data | ||
| 1287 | + } | ||
| 1288 | + | ||
| 1289 | + const option = { | ||
| 1290 | + tooltip: { | ||
| 1291 | + trigger: 'item', | ||
| 1292 | + formatter: '{b}: {c} ({d}%)' | ||
| 1293 | + }, | ||
| 1294 | + series: [ | ||
| 1295 | + { | ||
| 1296 | + name: '审核状态', | ||
| 1297 | + type: 'pie', | ||
| 1298 | + radius: ['35%', '65%'], | ||
| 1299 | + center: ['50%', '50%'], | ||
| 1300 | + data: [ | ||
| 1301 | + { | ||
| 1302 | + value: statusData.calculatedCount || 0, | ||
| 1303 | + name: '已计算', | ||
| 1304 | + itemStyle: { color: '#2196F3' } | ||
| 1305 | + }, | ||
| 1306 | + { | ||
| 1307 | + value: statusData.unCalculatedCount || 0, | ||
| 1308 | + name: '未计算', | ||
| 1309 | + itemStyle: { color: '#4CAF50' } | ||
| 1310 | + } | ||
| 1311 | + ], | ||
| 1312 | + label: { | ||
| 1313 | + show: true, | ||
| 1314 | + position: 'inside', | ||
| 1315 | + formatter: '{b}\n{d}%', | ||
| 1316 | + fontSize: 12, | ||
| 1317 | + color: '#fff', | ||
| 1318 | + fontWeight: 'bold' | ||
| 1319 | + }, | ||
| 1320 | + labelLine: { | ||
| 1321 | + show: false | ||
| 1322 | + }, | ||
| 1323 | + emphasis: { | ||
| 1324 | + itemStyle: { | ||
| 1325 | + shadowBlur: 10, | ||
| 1326 | + shadowOffsetX: 0, | ||
| 1327 | + shadowColor: 'rgba(0, 0, 0, 0.5)' | ||
| 1328 | + }, | ||
| 1329 | + label: { | ||
| 1330 | + fontSize: 14 | ||
| 1331 | + } | ||
| 1332 | + } | ||
| 1333 | + } | ||
| 1334 | + ] | ||
| 1335 | + } | ||
| 1336 | + chart.setOption(option) | ||
| 1337 | + | ||
| 1338 | + } catch (error) { | ||
| 1339 | + console.error('获取饼图数据失败:', error) | ||
| 1340 | + | ||
| 1341 | + // 如果API调用失败,使用当前列表数据作为备用方案 | ||
| 1342 | + const calculatedCount = rebateList.value.filter(item => item.calcFlag === 1).length | ||
| 1343 | + const unCalculatedCount = rebateList.value.filter(item => item.calcFlag === 0).length | ||
| 1344 | + | ||
| 1345 | + const fallbackOption = { | ||
| 1346 | + tooltip: { | ||
| 1347 | + trigger: 'item', | ||
| 1348 | + formatter: '{b}: {c} ({d}%)' | ||
| 1349 | + }, | ||
| 1350 | + series: [ | ||
| 1351 | + { | ||
| 1352 | + name: '审核状态', | ||
| 1353 | + type: 'pie', | ||
| 1354 | + radius: ['35%', '65%'], | ||
| 1355 | + center: ['50%', '50%'], | ||
| 1356 | + data: [ | ||
| 1357 | + { | ||
| 1358 | + value: calculatedCount, | ||
| 1359 | + name: '已审核', | ||
| 1360 | + itemStyle: { color: '#2196F3' } | ||
| 1361 | + }, | ||
| 1362 | + { | ||
| 1363 | + value: unCalculatedCount, | ||
| 1364 | + name: '未审核', | ||
| 1365 | + itemStyle: { color: '#4CAF50' } | ||
| 1366 | + } | ||
| 1367 | + ], | ||
| 1368 | + label: { | ||
| 1369 | + show: true, | ||
| 1370 | + position: 'inside', | ||
| 1371 | + formatter: '{b}\n{d}%', | ||
| 1372 | + fontSize: 12, | ||
| 1373 | + color: '#fff', | ||
| 1374 | + fontWeight: 'bold' | ||
| 1375 | + }, | ||
| 1376 | + labelLine: { | ||
| 1377 | + show: false | ||
| 1378 | + }, | ||
| 1379 | + emphasis: { | ||
| 1380 | + itemStyle: { | ||
| 1381 | + shadowBlur: 10, | ||
| 1382 | + shadowOffsetX: 0, | ||
| 1383 | + shadowColor: 'rgba(0, 0, 0, 0.5)' | ||
| 1384 | + }, | ||
| 1385 | + label: { | ||
| 1386 | + fontSize: 14 | ||
| 1387 | + } | ||
| 1388 | + } | ||
| 1389 | + } | ||
| 1390 | + ] | ||
| 1391 | + } | ||
| 1392 | + chart.setOption(fallbackOption) | ||
| 1393 | + } | ||
| 1394 | + | ||
| 1395 | + // 响应式调整 | ||
| 1396 | + window.addEventListener('resize', () => chart.resize()) | ||
| 1397 | +} | ||
| 1398 | + | ||
| 1399 | +// 页面加载时获取数据 | ||
| 1400 | +onMounted(async () => { | ||
| 1401 | + await getRebateList() | ||
| 1402 | + setTimeout(async () => { | ||
| 1403 | + await initTrendChart() | ||
| 1404 | + await initPieChart() | ||
| 1405 | + }, 100) | ||
| 1406 | +}) | ||
| 1407 | +</script> | ||
| 1408 | + | ||
| 1409 | +<style scoped lang="scss"> | ||
| 1410 | +.rebate-page { | ||
| 1411 | + padding: 20px; | ||
| 1412 | + background: #f5f7fa; | ||
| 1413 | + min-height: 100vh; | ||
| 1414 | + | ||
| 1415 | +} | ||
| 1416 | + | ||
| 1417 | +.search-section { | ||
| 1418 | + background: white; | ||
| 1419 | + padding: 16px 20px; | ||
| 1420 | + margin-bottom: 16px; | ||
| 1421 | + border-radius: 4px; | ||
| 1422 | + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); | ||
| 1423 | + | ||
| 1424 | + .search-form { | ||
| 1425 | + .search-row { | ||
| 1426 | + display: flex; | ||
| 1427 | + align-items: center; | ||
| 1428 | + gap: 24px; | ||
| 1429 | + flex-wrap: wrap; | ||
| 1430 | + | ||
| 1431 | + .search-item { | ||
| 1432 | + display: flex; | ||
| 1433 | + align-items: center; | ||
| 1434 | + gap: 8px; | ||
| 1435 | + | ||
| 1436 | + .search-label { | ||
| 1437 | + font-size: 14px; | ||
| 1438 | + color: #333; | ||
| 1439 | + white-space: nowrap; | ||
| 1440 | + min-width: 70px; | ||
| 1441 | + } | ||
| 1442 | + } | ||
| 1443 | + | ||
| 1444 | + .search-actions { | ||
| 1445 | + margin-left: auto; | ||
| 1446 | + display: flex; | ||
| 1447 | + gap: 8px; | ||
| 1448 | + } | ||
| 1449 | + } | ||
| 1450 | + } | ||
| 1451 | +} | ||
| 1452 | + | ||
| 1453 | +.charts-section { | ||
| 1454 | + margin-bottom: 16px; | ||
| 1455 | + | ||
| 1456 | + .charts-container { | ||
| 1457 | + display: grid; | ||
| 1458 | + grid-template-columns: 2fr 1fr; | ||
| 1459 | + gap: 16px; | ||
| 1460 | + | ||
| 1461 | + .chart-card { | ||
| 1462 | + background: white; | ||
| 1463 | + border-radius: 4px; | ||
| 1464 | + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); | ||
| 1465 | + overflow: hidden; | ||
| 1466 | + | ||
| 1467 | + .chart-header { | ||
| 1468 | + padding: 16px 20px 8px 20px; | ||
| 1469 | + border-bottom: 1px solid #f0f0f0; | ||
| 1470 | + display: flex; | ||
| 1471 | + justify-content: space-between; | ||
| 1472 | + align-items: center; | ||
| 1473 | + | ||
| 1474 | + .chart-title { | ||
| 1475 | + font-size: 16px; | ||
| 1476 | + font-weight: 500; | ||
| 1477 | + color: #333; | ||
| 1478 | + margin: 0; | ||
| 1479 | + } | ||
| 1480 | + | ||
| 1481 | + .chart-legend { | ||
| 1482 | + display: flex; | ||
| 1483 | + gap: 16px; | ||
| 1484 | + | ||
| 1485 | + .legend-item { | ||
| 1486 | + display: flex; | ||
| 1487 | + align-items: center; | ||
| 1488 | + gap: 6px; | ||
| 1489 | + font-size: 12px; | ||
| 1490 | + color: #666; | ||
| 1491 | + | ||
| 1492 | + .legend-dot { | ||
| 1493 | + width: 8px; | ||
| 1494 | + height: 8px; | ||
| 1495 | + border-radius: 50%; | ||
| 1496 | + } | ||
| 1497 | + } | ||
| 1498 | + } | ||
| 1499 | + } | ||
| 1500 | + | ||
| 1501 | + &.trend-chart { | ||
| 1502 | + .chart { | ||
| 1503 | + height: 260px; | ||
| 1504 | + } | ||
| 1505 | + } | ||
| 1506 | + | ||
| 1507 | + &.pie-chart { | ||
| 1508 | + .chart { | ||
| 1509 | + height: 240px; | ||
| 1510 | + } | ||
| 1511 | + } | ||
| 1512 | + | ||
| 1513 | + .chart-content { | ||
| 1514 | + padding: 12px 20px 20px 20px; | ||
| 1515 | + } | ||
| 1516 | + } | ||
| 1517 | + } | ||
| 1518 | +} | ||
| 1519 | + | ||
| 1520 | +.chart { | ||
| 1521 | + width: 100%; | ||
| 1522 | +} | ||
| 1523 | + | ||
| 1524 | +.table-section { | ||
| 1525 | + background: white; | ||
| 1526 | + border-radius: 8px; | ||
| 1527 | + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | ||
| 1528 | + overflow: hidden; | ||
| 1529 | + | ||
| 1530 | + .table-header { | ||
| 1531 | + display: flex; | ||
| 1532 | + justify-content: space-between; | ||
| 1533 | + align-items: center; | ||
| 1534 | + padding: 20px 20px 0 20px; | ||
| 1535 | + margin-bottom: 16px; | ||
| 1536 | + | ||
| 1537 | + .table-title { | ||
| 1538 | + font-size: 16px; | ||
| 1539 | + font-weight: 500; | ||
| 1540 | + color: #333; | ||
| 1541 | + } | ||
| 1542 | + | ||
| 1543 | + .table-info { | ||
| 1544 | + font-size: 12px; | ||
| 1545 | + color: #999; | ||
| 1546 | + } | ||
| 1547 | + } | ||
| 1548 | + | ||
| 1549 | + .el-table { | ||
| 1550 | + border: none; | ||
| 1551 | + | ||
| 1552 | + :deep(.el-table__header) { | ||
| 1553 | + th { | ||
| 1554 | + background-color: #fafafa; | ||
| 1555 | + border: none; | ||
| 1556 | + font-weight: 500; | ||
| 1557 | + color: #333; | ||
| 1558 | + } | ||
| 1559 | + } | ||
| 1560 | + | ||
| 1561 | + :deep(.el-table__body) { | ||
| 1562 | + tr:hover > td { | ||
| 1563 | + background-color: #f5f7fa; | ||
| 1564 | + } | ||
| 1565 | + | ||
| 1566 | + td { | ||
| 1567 | + border: none; | ||
| 1568 | + border-bottom: 1px solid #f0f0f0; | ||
| 1569 | + } | ||
| 1570 | + } | ||
| 1571 | + } | ||
| 1572 | + | ||
| 1573 | + .pagination-wrapper { | ||
| 1574 | + display: flex; | ||
| 1575 | + justify-content: space-between; | ||
| 1576 | + align-items: center; | ||
| 1577 | + padding: 12px 20px; | ||
| 1578 | + border-top: 1px solid #f0f0f0; | ||
| 1579 | + background: #fafafa; | ||
| 1580 | + | ||
| 1581 | + .pagination-info { | ||
| 1582 | + font-size: 14px; | ||
| 1583 | + color: #666; | ||
| 1584 | + } | ||
| 1585 | + | ||
| 1586 | + .pagination-jump { | ||
| 1587 | + display: flex; | ||
| 1588 | + align-items: center; | ||
| 1589 | + font-size: 14px; | ||
| 1590 | + color: #666; | ||
| 1591 | + } | ||
| 1592 | + } | ||
| 1593 | +} | ||
| 1594 | + | ||
| 1595 | +.amount-text { | ||
| 1596 | + color: #f56c6c; | ||
| 1597 | + font-weight: 500; | ||
| 1598 | +} | ||
| 1599 | + | ||
| 1600 | +.rebate-detail { | ||
| 1601 | + max-height: 600px; | ||
| 1602 | + overflow-y: auto; | ||
| 1603 | +} | ||
| 1604 | + | ||
| 1605 | +// 响应式设计 | ||
| 1606 | +@media (max-width: 1200px) { | ||
| 1607 | + .charts-container { | ||
| 1608 | + grid-template-columns: 1fr; | ||
| 1609 | + } | ||
| 1610 | +} | ||
| 1611 | + | ||
| 1612 | +@media (max-width: 768px) { | ||
| 1613 | + .rebate-page { | ||
| 1614 | + padding: 16px; | ||
| 1615 | + } | ||
| 1616 | + | ||
| 1617 | + .search-form .form-row { | ||
| 1618 | + flex-direction: column; | ||
| 1619 | + align-items: flex-start; | ||
| 1620 | + gap: 12px; | ||
| 1621 | + | ||
| 1622 | + .form-actions { | ||
| 1623 | + margin-left: 0; | ||
| 1624 | + margin-top: 12px; | ||
| 1625 | + } | ||
| 1626 | + } | ||
| 1627 | + | ||
| 1628 | +} | ||
| 1629 | +</style> |
-
Please register or login to post a comment