Commit 7763154367905619586ee812fa8d6e2bb9ba6e78

Authored by “wangming”
1 parent 59e71c70

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

- 修复门店驾驶舱页面布局对齐问题
- 删除每日运营数据(当月)图表
- 退款明细列表接口添加健康师和业绩字段
- 退款明细列表页面显示健康师和业绩信息
antis-ncc-admin/src/views/extend/storeDashboard/index.vue
@@ -92,18 +92,9 @@ @@ -92,18 +92,9 @@
92 </el-col> 92 </el-col>
93 </el-row> 93 </el-row>
94 94
95 - <!-- 第二行:每日运营数据趋势 + 门店综合能力雷达图 --> 95 + <!-- 第二行:门店综合能力雷达图 -->
96 <el-row :gutter="16" class="chart-row"> 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 <el-card class="chart-card" shadow="hover"> 98 <el-card class="chart-card" shadow="hover">
108 <div slot="header" class="card-header"> 99 <div slot="header" class="card-header">
109 <i class="el-icon-aim"></i> 100 <i class="el-icon-aim"></i>
@@ -221,15 +212,15 @@ @@ -221,15 +212,15 @@
221 <el-table-column prop="name" label="健康师姓名" min-width="120" /> 212 <el-table-column prop="name" label="健康师姓名" min-width="120" />
222 <el-table-column prop="billingPerformance" label="开单业绩" width="120" align="right"> 213 <el-table-column prop="billingPerformance" label="开单业绩" width="120" align="right">
223 <template slot-scope="scope">¥{{ formatMoney(scope.row.billingPerformance) 214 <template slot-scope="scope">¥{{ formatMoney(scope.row.billingPerformance)
224 - }}</template> 215 + }}</template>
225 </el-table-column> 216 </el-table-column>
226 <el-table-column prop="consumePerformance" label="消耗业绩" width="120" align="right"> 217 <el-table-column prop="consumePerformance" label="消耗业绩" width="120" align="right">
227 <template slot-scope="scope">¥{{ formatMoney(scope.row.consumePerformance) 218 <template slot-scope="scope">¥{{ formatMoney(scope.row.consumePerformance)
228 - }}</template> 219 + }}</template>
229 </el-table-column> 220 </el-table-column>
230 <el-table-column prop="totalPerformance" label="总业绩" width="120" align="right"> 221 <el-table-column prop="totalPerformance" label="总业绩" width="120" align="right">
231 <template slot-scope="scope">¥{{ formatMoney(scope.row.totalPerformance) 222 <template slot-scope="scope">¥{{ formatMoney(scope.row.totalPerformance)
232 - }}</template> 223 + }}</template>
233 </el-table-column> 224 </el-table-column>
234 </el-table> 225 </el-table>
235 </el-card> 226 </el-card>
@@ -526,7 +517,6 @@ export default { @@ -526,7 +517,6 @@ export default {
526 ], 517 ],
527 trendChart: null, 518 trendChart: null,
528 categoryChart: null, 519 categoryChart: null,
529 - dailyChart: null,  
530 compareChart: null, 520 compareChart: null,
531 stackedChart: null, 521 stackedChart: null,
532 funnelChart: null, 522 funnelChart: null,
@@ -570,7 +560,6 @@ export default { @@ -570,7 +560,6 @@ export default {
570 beforeDestroy() { 560 beforeDestroy() {
571 if (this.trendChart) this.trendChart.dispose() 561 if (this.trendChart) this.trendChart.dispose()
572 if (this.categoryChart) this.categoryChart.dispose() 562 if (this.categoryChart) this.categoryChart.dispose()
573 - if (this.dailyChart) this.dailyChart.dispose()  
574 if (this.compareChart) this.compareChart.dispose() 563 if (this.compareChart) this.compareChart.dispose()
575 if (this.stackedChart) this.stackedChart.dispose() 564 if (this.stackedChart) this.stackedChart.dispose()
576 if (this.funnelChart) this.funnelChart.dispose() 565 if (this.funnelChart) this.funnelChart.dispose()
@@ -586,7 +575,6 @@ export default { @@ -586,7 +575,6 @@ export default {
586 this.$nextTick(() => { 575 this.$nextTick(() => {
587 this.renderTrendChart() 576 this.renderTrendChart()
588 this.renderCategoryChart() 577 this.renderCategoryChart()
589 - this.renderDailyChart()  
590 this.renderCompareChart() 578 this.renderCompareChart()
591 this.renderStackedChart() 579 this.renderStackedChart()
592 this.renderFunnelChart() 580 this.renderFunnelChart()
@@ -639,28 +627,6 @@ export default { @@ -639,28 +627,6 @@ export default {
639 } 627 }
640 this.categoryChart.setOption(option) 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 renderCompareChart() { 630 renderCompareChart() {
665 if (!this.$refs.compareChart) return 631 if (!this.$refs.compareChart) return
666 this.compareChart = echarts.init(this.$refs.compareChart) 632 this.compareChart = echarts.init(this.$refs.compareChart)
@@ -873,7 +839,6 @@ export default { @@ -873,7 +839,6 @@ export default {
873 handleResize() { 839 handleResize() {
874 if (this.trendChart) this.trendChart.resize() 840 if (this.trendChart) this.trendChart.resize()
875 if (this.categoryChart) this.categoryChart.resize() 841 if (this.categoryChart) this.categoryChart.resize()
876 - if (this.dailyChart) this.dailyChart.resize()  
877 if (this.compareChart) this.compareChart.resize() 842 if (this.compareChart) this.compareChart.resize()
878 if (this.stackedChart) this.stackedChart.resize() 843 if (this.stackedChart) this.stackedChart.resize()
879 if (this.funnelChart) this.funnelChart.resize() 844 if (this.funnelChart) this.funnelChart.resize()
@@ -1159,6 +1124,7 @@ export default { @@ -1159,6 +1124,7 @@ export default {
1159 grid-template-columns: 1fr 400px; 1124 grid-template-columns: 1fr 400px;
1160 gap: 20px; 1125 gap: 20px;
1161 margin-bottom: 20px; 1126 margin-bottom: 20px;
  1127 + align-items: start;
1162 1128
1163 .content-left { 1129 .content-left {
1164 display: flex; 1130 display: flex;
antis-ncc-admin/src/views/statisticsList/form21.vue
@@ -270,6 +270,17 @@ @@ -270,6 +270,17 @@
270 </el-tag> 270 </el-tag>
271 </template> 271 </template>
272 </el-table-column> 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 </NCC-table> 284 </NCC-table>
274 <pagination 285 <pagination
275 v-show="total > 0" 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,6 +81,21 @@ namespace NCC.Extend.Entitys.Dto.LqHytkHytk
81 /// 品项分类 81 /// 品项分类
82 /// </summary> 82 /// </summary>
83 public string itemCategory { get; set; } 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,6 +27,7 @@ using NCC.Extend.Entitys.lq_hytk_jksyj;
27 using NCC.Extend.Entitys.lq_hytk_kjbsyj; 27 using NCC.Extend.Entitys.lq_hytk_kjbsyj;
28 using NCC.Extend.Entitys.lq_hytk_mx; 28 using NCC.Extend.Entitys.lq_hytk_mx;
29 using NCC.Extend.Entitys.lq_kd_pxmx; 29 using NCC.Extend.Entitys.lq_kd_pxmx;
  30 +using NCC.Extend.Entitys.lq_kd_jksyj;
30 using NCC.Extend.Entitys.lq_xmzl; 31 using NCC.Extend.Entitys.lq_xmzl;
31 using NCC.Extend.Entitys.lq_khxx; 32 using NCC.Extend.Entitys.lq_khxx;
32 using NCC.Extend.Entitys.lq_mdxx; 33 using NCC.Extend.Entitys.lq_mdxx;
@@ -915,7 +916,43 @@ namespace NCC.Extend.LqHytkHytk @@ -915,7 +916,43 @@ namespace NCC.Extend.LqHytkHytk
915 memberDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? ""); 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 var resultList = new List<RefundDetailListOutput>(); 956 var resultList = new List<RefundDetailListOutput>();
920 foreach (var mx in mxList) 957 foreach (var mx in mxList)
921 { 958 {
@@ -936,6 +973,11 @@ namespace NCC.Extend.LqHytkHytk @@ -936,6 +973,11 @@ namespace NCC.Extend.LqHytkHytk
936 var memberId = mx.MemberId ?? (refundInfo?.Hy); 973 var memberId = mx.MemberId ?? (refundInfo?.Hy);
937 var refundTime = mx.Tksj ?? refundInfo?.Tksj; 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 resultList.Add(new RefundDetailListOutput 981 resultList.Add(new RefundDetailListOutput
940 { 982 {
941 storeId = refundInfo?.Md, 983 storeId = refundInfo?.Md,
@@ -952,11 +994,14 @@ namespace NCC.Extend.LqHytkHytk @@ -952,11 +994,14 @@ namespace NCC.Extend.LqHytkHytk
952 performanceType = mx.PerformanceType, 994 performanceType = mx.PerformanceType,
953 beautyType = mx.BeautyType, 995 beautyType = mx.BeautyType,
954 sourceType = mx.SourceType, 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 var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx; 1005 var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx;
961 var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort; 1006 var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort;
962 1007
@@ -969,7 +1014,7 @@ namespace NCC.Extend.LqHytkHytk @@ -969,7 +1014,7 @@ namespace NCC.Extend.LqHytkHytk
969 resultList = resultList.OrderBy(x => GetPropertyValue(x, sidx)).ToList(); 1014 resultList = resultList.OrderBy(x => GetPropertyValue(x, sidx)).ToList();
970 } 1015 }
971 1016
972 - // 7. 分页 1017 + // 8. 分页
973 var total = resultList.Count; 1018 var total = resultList.Count;
974 var skip = (input.currentPage - 1) * input.pageSize; 1019 var skip = (input.currentPage - 1) * input.pageSize;
975 var pagedList = resultList.Skip(skip).Take(input.pageSize).ToList(); 1020 var pagedList = resultList.Skip(skip).Take(input.pageSize).ToList();
@@ -1069,7 +1114,43 @@ namespace NCC.Extend.LqHytkHytk @@ -1069,7 +1114,43 @@ namespace NCC.Extend.LqHytkHytk
1069 memberDict = members.ToDictionary(x => x.Id, x => x.Sjh ?? ""); 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 var resultList = new List<RefundDetailListOutput>(); 1154 var resultList = new List<RefundDetailListOutput>();
1074 foreach (var mx in mxList) 1155 foreach (var mx in mxList)
1075 { 1156 {
@@ -1090,6 +1171,11 @@ namespace NCC.Extend.LqHytkHytk @@ -1090,6 +1171,11 @@ namespace NCC.Extend.LqHytkHytk
1090 var memberId = mx.MemberId ?? (refundInfo?.Hy); 1171 var memberId = mx.MemberId ?? (refundInfo?.Hy);
1091 var refundTime = mx.Tksj ?? refundInfo?.Tksj; 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 resultList.Add(new RefundDetailListOutput 1179 resultList.Add(new RefundDetailListOutput
1094 { 1180 {
1095 storeId = refundInfo?.Md, 1181 storeId = refundInfo?.Md,
@@ -1106,11 +1192,14 @@ namespace NCC.Extend.LqHytkHytk @@ -1106,11 +1192,14 @@ namespace NCC.Extend.LqHytkHytk
1106 performanceType = mx.PerformanceType, 1192 performanceType = mx.PerformanceType,
1107 beautyType = mx.BeautyType, 1193 beautyType = mx.BeautyType,
1108 sourceType = mx.SourceType, 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 var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx; 1203 var sidx = string.IsNullOrEmpty(input.sidx) ? "refundTime" : input.sidx;
1115 var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort; 1204 var sort = string.IsNullOrEmpty(input.sort) ? "desc" : input.sort;
1116 1205