Commit 7763154367905619586ee812fa8d6e2bb9ba6e78

Authored by “wangming”
1 parent 59e71c70

优化门店驾驶舱页面布局并添加退款明细健康师信息

- 修复门店驾驶舱页面布局对齐问题
- 删除每日运营数据(当月)图表
- 退款明细列表接口添加健康师和业绩字段
- 退款明细列表页面显示健康师和业绩信息
antis-ncc-admin/src/views/extend/storeDashboard/index.vue
... ... @@ -92,18 +92,9 @@
92 92 </el-col>
93 93 </el-row>
94 94  
95   - <!-- 第二行:每日运营数据趋势 + 门店综合能力雷达图 -->
  95 + <!-- 第二行:门店综合能力雷达图 -->
96 96 <el-row :gutter="16" class="chart-row">
97   - <el-col :span="16">
98   - <el-card class="chart-card" shadow="hover">
99   - <div slot="header" class="card-header">
100   - <i class="el-icon-date"></i>
101   - <span>每日运营数据趋势</span>
102   - </div>
103   - <div ref="dailyChart" class="chart-container"></div>
104   - </el-card>
105   - </el-col>
106   - <el-col :span="8">
  97 + <el-col :span="24">
107 98 <el-card class="chart-card" shadow="hover">
108 99 <div slot="header" class="card-header">
109 100 <i class="el-icon-aim"></i>
... ... @@ -221,15 +212,15 @@
221 212 <el-table-column prop="name" label="健康师姓名" min-width="120" />
222 213 <el-table-column prop="billingPerformance" label="开单业绩" width="120" align="right">
223 214 <template slot-scope="scope">¥{{ formatMoney(scope.row.billingPerformance)
224   - }}</template>
  215 + }}</template>
225 216 </el-table-column>
226 217 <el-table-column prop="consumePerformance" label="消耗业绩" width="120" align="right">
227 218 <template slot-scope="scope">¥{{ formatMoney(scope.row.consumePerformance)
228   - }}</template>
  219 + }}</template>
229 220 </el-table-column>
230 221 <el-table-column prop="totalPerformance" label="总业绩" width="120" align="right">
231 222 <template slot-scope="scope">¥{{ formatMoney(scope.row.totalPerformance)
232   - }}</template>
  223 + }}</template>
233 224 </el-table-column>
234 225 </el-table>
235 226 </el-card>
... ... @@ -526,7 +517,6 @@ export default {
526 517 ],
527 518 trendChart: null,
528 519 categoryChart: null,
529   - dailyChart: null,
530 520 compareChart: null,
531 521 stackedChart: null,
532 522 funnelChart: null,
... ... @@ -570,7 +560,6 @@ export default {
570 560 beforeDestroy() {
571 561 if (this.trendChart) this.trendChart.dispose()
572 562 if (this.categoryChart) this.categoryChart.dispose()
573   - if (this.dailyChart) this.dailyChart.dispose()
574 563 if (this.compareChart) this.compareChart.dispose()
575 564 if (this.stackedChart) this.stackedChart.dispose()
576 565 if (this.funnelChart) this.funnelChart.dispose()
... ... @@ -586,7 +575,6 @@ export default {
586 575 this.$nextTick(() => {
587 576 this.renderTrendChart()
588 577 this.renderCategoryChart()
589   - this.renderDailyChart()
590 578 this.renderCompareChart()
591 579 this.renderStackedChart()
592 580 this.renderFunnelChart()
... ... @@ -639,28 +627,6 @@ export default {
639 627 }
640 628 this.categoryChart.setOption(option)
641 629 },
642   - renderDailyChart() {
643   - if (!this.$refs.dailyChart) return
644   - this.dailyChart = echarts.init(this.$refs.dailyChart)
645   - const option = {
646   - tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } },
647   - legend: { data: ['开单业绩', '消耗业绩', '人头数', '人次', '项目数'], top: 10 },
648   - grid: { left: '60px', right: '80px', top: '50px', bottom: '60px', containLabel: false },
649   - xAxis: { type: 'category', data: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30'], axisLabel: { rotate: 45, interval: 2, fontSize: 11 } },
650   - yAxis: [
651   - { type: 'value', name: '业绩', position: 'left', axisLabel: { formatter: value => value >= 10000 ? '¥' + (value / 10000).toFixed(1) + '万' : '¥' + value }, splitLine: { lineStyle: { type: 'dashed', color: '#E4E7ED' } } },
652   - { type: 'value', name: '数量', position: 'right', splitLine: { show: false } }
653   - ],
654   - series: [
655   - { name: '开单业绩', type: 'bar', yAxisIndex: 0, data: [45680, 52890, 48960, 56890, 49860, 59860, 62890, 55860, 51860, 59860, 52890, 48960, 56890, 49860, 59860, 62890, 55860, 51860, 59860, 52890, 48960, 56890, 49860, 59860, 62890, 55860, 51860, 59860, 52890, 48960], itemStyle: { color: '#409EFF' }, barWidth: '30%' },
656   - { name: '消耗业绩', type: 'bar', yAxisIndex: 0, data: [32890, 38960, 35280, 41250, 36580, 43280, 45680, 40250, 37580, 43280, 38960, 35280, 41250, 36580, 43280, 45680, 40250, 37580, 43280, 38960, 35280, 41250, 36580, 43280, 45680, 40250, 37580, 43280, 38960, 35280], itemStyle: { color: '#67C23A' }, barWidth: '30%' },
657   - { name: '人头数', type: 'line', yAxisIndex: 1, data: [45, 52, 48, 56, 49, 58, 62, 55, 51, 59, 52, 48, 56, 49, 58, 62, 55, 51, 59, 52, 48, 56, 49, 58, 62, 55, 51, 59, 52, 48], itemStyle: { color: '#F56C6C' }, lineStyle: { width: 2 }, symbol: 'circle', symbolSize: 6 },
658   - { name: '人次', type: 'line', yAxisIndex: 1, data: [128, 145, 132, 156, 138, 162, 178, 152, 142, 165, 145, 132, 156, 138, 162, 178, 152, 142, 165, 145, 132, 156, 138, 162, 178, 152, 142, 165, 145, 132], itemStyle: { color: '#E6A23C' }, lineStyle: { width: 2 }, symbol: 'circle', symbolSize: 6 },
659   - { name: '项目数', type: 'line', yAxisIndex: 1, data: [256, 289, 268, 312, 278, 325, 356, 304, 284, 330, 289, 268, 312, 278, 325, 356, 304, 284, 330, 289, 268, 312, 278, 325, 356, 304, 284, 330, 289, 268], itemStyle: { color: '#909399' }, lineStyle: { width: 2 }, symbol: 'circle', symbolSize: 6 }
660   - ]
661   - }
662   - this.dailyChart.setOption(option)
663   - },
664 630 renderCompareChart() {
665 631 if (!this.$refs.compareChart) return
666 632 this.compareChart = echarts.init(this.$refs.compareChart)
... ... @@ -873,7 +839,6 @@ export default {
873 839 handleResize() {
874 840 if (this.trendChart) this.trendChart.resize()
875 841 if (this.categoryChart) this.categoryChart.resize()
876   - if (this.dailyChart) this.dailyChart.resize()
877 842 if (this.compareChart) this.compareChart.resize()
878 843 if (this.stackedChart) this.stackedChart.resize()
879 844 if (this.funnelChart) this.funnelChart.resize()
... ... @@ -1159,6 +1124,7 @@ export default {
1159 1124 grid-template-columns: 1fr 400px;
1160 1125 gap: 20px;
1161 1126 margin-bottom: 20px;
  1127 + align-items: start;
1162 1128  
1163 1129 .content-left {
1164 1130 display: flex;
... ...
antis-ncc-admin/src/views/statisticsList/form21.vue
... ... @@ -270,6 +270,17 @@
270 270 </el-tag>
271 271 </template>
272 272 </el-table-column>
  273 + <el-table-column prop="healthCoachName" label="健康师" min-width="120" sortable="custom">
  274 + <template slot-scope="scope">
  275 + <i class="el-icon-user-solid" style="margin-right: 4px; color: #F56C6C;"></i>
  276 + <span>{{ scope.row.healthCoachName || '无' }}</span>
  277 + </template>
  278 + </el-table-column>
  279 + <el-table-column prop="healthCoachPerformance" label="健康师业绩" min-width="130" sortable="custom">
  280 + <template slot-scope="scope">
  281 + <span class="amount-value">¥{{ formatMoney(scope.row.healthCoachPerformance) }}</span>
  282 + </template>
  283 + </el-table-column>
273 284 </NCC-table>
274 285 <pagination
275 286 v-show="total > 0"
... ...
netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqHytkHytk/RefundDetailListOutput.cs
... ... @@ -81,6 +81,21 @@ namespace NCC.Extend.Entitys.Dto.LqHytkHytk
81 81 /// 品项分类
82 82 /// </summary>
83 83 public string itemCategory { get; set; }
  84 +
  85 + /// <summary>
  86 + /// 健康师账号
  87 + /// </summary>
  88 + public string healthCoachId { get; set; }
  89 +
  90 + /// <summary>
  91 + /// 健康师姓名
  92 + /// </summary>
  93 + public string healthCoachName { get; set; }
  94 +
  95 + /// <summary>
  96 + /// 健康师业绩
  97 + /// </summary>
  98 + public decimal healthCoachPerformance { get; set; }
84 99 }
85 100 }
86 101  
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqHytkHytkService.cs
... ... @@ -27,6 +27,7 @@ using NCC.Extend.Entitys.lq_hytk_jksyj;
27 27 using NCC.Extend.Entitys.lq_hytk_kjbsyj;
28 28 using NCC.Extend.Entitys.lq_hytk_mx;
29 29 using NCC.Extend.Entitys.lq_kd_pxmx;
  30 +using NCC.Extend.Entitys.lq_kd_jksyj;
30 31 using NCC.Extend.Entitys.lq_xmzl;
31 32 using NCC.Extend.Entitys.lq_khxx;
32 33 using NCC.Extend.Entitys.lq_mdxx;
... ... @@ -915,7 +916,43 @@ namespace NCC.Extend.LqHytkHytk
915 916 memberDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? "");
916 917 }
917 918  
918   - // 5. 组装数据
  919 + // 5. 批量查询健康师业绩信息
  920 + var billingItemIds = mxList.Select(x => x.BillingItemId).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
  921 + var healthCoachDict = new Dictionary<string, (string Id, string Name, decimal Performance)>();
  922 + if (billingItemIds.Any())
  923 + {
  924 + // 通过开单品项明细ID查询开单品项明细,获取开单编号和品项明细ID
  925 + var pxmxList = await _db.Queryable<LqKdPxmxEntity>()
  926 + .Where(x => billingItemIds.Contains(x.Id))
  927 + .Select(x => new { x.Id, x.Glkdbh })
  928 + .ToListAsync();
  929 +
  930 + if (pxmxList.Any())
  931 + {
  932 + var glkdbhList = pxmxList.Select(x => x.Glkdbh).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
  933 + var pxmxIdList = pxmxList.Select(x => x.Id).Distinct().ToList();
  934 +
  935 + // 通过开单编号和品项明细ID查询健康师业绩
  936 + var jksyjList = await _db.Queryable<LqKdJksyjEntity>()
  937 + .Where(x => glkdbhList.Contains(x.Glkdbh) && pxmxIdList.Contains(x.Kdpxid) && x.IsEffective == 1)
  938 + .Select(x => new { x.Glkdbh, x.Kdpxid, x.Jkszh, x.Jksxm, x.Jksyj })
  939 + .ToListAsync();
  940 +
  941 + // 建立开单品项明细ID到健康师信息的映射
  942 + foreach (var jksyj in jksyjList)
  943 + {
  944 + // 找到对应的开单品项明细ID
  945 + var pxmxId = pxmxList.FirstOrDefault(x => x.Glkdbh == jksyj.Glkdbh && x.Id == jksyj.Kdpxid)?.Id;
  946 + if (!string.IsNullOrEmpty(pxmxId))
  947 + {
  948 + var jksyjValue = decimal.TryParse(jksyj.Jksyj, out var perf) ? perf : 0;
  949 + healthCoachDict[pxmxId] = (jksyj.Jkszh ?? "", jksyj.Jksxm ?? "", jksyjValue);
  950 + }
  951 + }
  952 + }
  953 + }
  954 +
  955 + // 6. 组装数据
919 956 var resultList = new List<RefundDetailListOutput>();
920 957 foreach (var mx in mxList)
921 958 {
... ... @@ -936,6 +973,11 @@ namespace NCC.Extend.LqHytkHytk
936 973 var memberId = mx.MemberId ?? (refundInfo?.Hy);
937 974 var refundTime = mx.Tksj ?? refundInfo?.Tksj;
938 975  
  976 + // 获取健康师信息
  977 + var healthCoachInfo = !string.IsNullOrEmpty(mx.BillingItemId) && healthCoachDict.ContainsKey(mx.BillingItemId)
  978 + ? healthCoachDict[mx.BillingItemId]
  979 + : (Id: "", Name: "", Performance: 0m);
  980 +
939 981 resultList.Add(new RefundDetailListOutput
940 982 {
941 983 storeId = refundInfo?.Md,
... ... @@ -952,11 +994,14 @@ namespace NCC.Extend.LqHytkHytk
952 994 performanceType = mx.PerformanceType,
953 995 beautyType = mx.BeautyType,
954 996 sourceType = mx.SourceType,
955   - itemCategory = mx.ItemCategory
  997 + itemCategory = mx.ItemCategory,
  998 + healthCoachId = healthCoachInfo.Id,
  999 + healthCoachName = healthCoachInfo.Name,
  1000 + healthCoachPerformance = healthCoachInfo.Performance
956 1001 });
957 1002 }
958 1003  
959   - // 6. 排序
  1004 + // 7. 排序
960 1005 var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx;
961 1006 var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort;
962 1007  
... ... @@ -969,7 +1014,7 @@ namespace NCC.Extend.LqHytkHytk
969 1014 resultList = resultList.OrderBy(x => GetPropertyValue(x, sidx)).ToList();
970 1015 }
971 1016  
972   - // 7. 分页
  1017 + // 8. 分页
973 1018 var total = resultList.Count;
974 1019 var skip = (input.currentPage - 1) * input.pageSize;
975 1020 var pagedList = resultList.Skip(skip).Take(input.pageSize).ToList();
... ... @@ -1069,7 +1114,43 @@ namespace NCC.Extend.LqHytkHytk
1069 1114 memberDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? "");
1070 1115 }
1071 1116  
1072   - // 5. 组装数据
  1117 + // 5. 批量查询健康师业绩信息
  1118 + var billingItemIds = mxList.Select(x => x.BillingItemId).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
  1119 + var healthCoachDict = new Dictionary<string, (string Id, string Name, decimal Performance)>();
  1120 + if (billingItemIds.Any())
  1121 + {
  1122 + // 通过开单品项明细ID查询开单品项明细,获取开单编号和品项明细ID
  1123 + var pxmxList = await _db.Queryable<LqKdPxmxEntity>()
  1124 + .Where(x => billingItemIds.Contains(x.Id))
  1125 + .Select(x => new { x.Id, x.Glkdbh })
  1126 + .ToListAsync();
  1127 +
  1128 + if (pxmxList.Any())
  1129 + {
  1130 + var glkdbhList = pxmxList.Select(x => x.Glkdbh).Where(x => !string.IsNullOrEmpty(x)).Distinct().ToList();
  1131 + var pxmxIdList = pxmxList.Select(x => x.Id).Distinct().ToList();
  1132 +
  1133 + // 通过开单编号和品项明细ID查询健康师业绩
  1134 + var jksyjList = await _db.Queryable<LqKdJksyjEntity>()
  1135 + .Where(x => glkdbhList.Contains(x.Glkdbh) && pxmxIdList.Contains(x.Kdpxid) && x.IsEffective == 1)
  1136 + .Select(x => new { x.Glkdbh, x.Kdpxid, x.Jkszh, x.Jksxm, x.Jksyj })
  1137 + .ToListAsync();
  1138 +
  1139 + // 建立开单品项明细ID到健康师信息的映射
  1140 + foreach (var jksyj in jksyjList)
  1141 + {
  1142 + // 找到对应的开单品项明细ID
  1143 + var pxmxId = pxmxList.FirstOrDefault(x => x.Glkdbh == jksyj.Glkdbh && x.Id == jksyj.Kdpxid)?.Id;
  1144 + if (!string.IsNullOrEmpty(pxmxId))
  1145 + {
  1146 + var jksyjValue = decimal.TryParse(jksyj.Jksyj, out var perf) ? perf : 0;
  1147 + healthCoachDict[pxmxId] = (jksyj.Jkszh ?? "", jksyj.Jksxm ?? "", jksyjValue);
  1148 + }
  1149 + }
  1150 + }
  1151 + }
  1152 +
  1153 + // 6. 组装数据
1073 1154 var resultList = new List<RefundDetailListOutput>();
1074 1155 foreach (var mx in mxList)
1075 1156 {
... ... @@ -1090,6 +1171,11 @@ namespace NCC.Extend.LqHytkHytk
1090 1171 var memberId = mx.MemberId ?? (refundInfo?.Hy);
1091 1172 var refundTime = mx.Tksj ?? refundInfo?.Tksj;
1092 1173  
  1174 + // 获取健康师信息
  1175 + var healthCoachInfo = !string.IsNullOrEmpty(mx.BillingItemId) && healthCoachDict.ContainsKey(mx.BillingItemId)
  1176 + ? healthCoachDict[mx.BillingItemId]
  1177 + : (Id: "", Name: "", Performance: 0m);
  1178 +
1093 1179 resultList.Add(new RefundDetailListOutput
1094 1180 {
1095 1181 storeId = refundInfo?.Md,
... ... @@ -1106,11 +1192,14 @@ namespace NCC.Extend.LqHytkHytk
1106 1192 performanceType = mx.PerformanceType,
1107 1193 beautyType = mx.BeautyType,
1108 1194 sourceType = mx.SourceType,
1109   - itemCategory = mx.ItemCategory
  1195 + itemCategory = mx.ItemCategory,
  1196 + healthCoachId = healthCoachInfo.Id,
  1197 + healthCoachName = healthCoachInfo.Name,
  1198 + healthCoachPerformance = healthCoachInfo.Performance
1110 1199 });
1111 1200 }
1112 1201  
1113   - // 6. 排序
  1202 + // 7. 排序
1114 1203 var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx;
1115 1204 var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort;
1116 1205  
... ...