Commit 189ad8f683f9fe588b2430ac034600d8cba2fb3d

Authored by 李宇
2 parents 0f14a621 28ad29c0

Merge branch 'master' of http://39.98.150.180/antissoft/lvqianmeiye_ERP

antis-ncc-admin/src/views/extend/annualSummary/dashboard/index.vue
... ... @@ -119,7 +119,8 @@
119 119 </div>
120 120 <NCC-table v-loading="trendTableLoading" :data="trendTableData" border stripe style="width: 100%">
121 121 <el-table-column v-for="col in trendTableColumns" :key="col.prop" :prop="col.prop" :label="col.label"
122   - :width="col.width" :align="col.align || 'center'">
  122 + :width="col.width" :align="col.align || 'center'" :sortable="col.sortable || false"
  123 + :sort-method="col.sortMethod || undefined">
123 124 <template slot-scope="scope">
124 125 <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span>
125 126 <span v-else>{{ scope.row[col.prop] || '0' }}</span>
... ... @@ -150,7 +151,8 @@
150 151 <NCC-table v-loading="performanceTableLoading" :data="performanceTableData" border stripe
151 152 style="width: 100%">
152 153 <el-table-column v-for="col in performanceTableColumns" :key="col.prop" :prop="col.prop"
153   - :label="col.label" :width="col.width" :align="col.align || 'center'">
  154 + :label="col.label" :width="col.width" :align="col.align || 'center'" :sortable="col.sortable || false"
  155 + :sort-method="col.sortMethod || undefined">
154 156 <template slot-scope="scope">
155 157 <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span>
156 158 <span v-else>{{ scope.row[col.prop] || '0' }}</span>
... ... @@ -180,7 +182,8 @@
180 182 </div>
181 183 <NCC-table v-loading="consumeTableLoading" :data="consumeTableData" border stripe style="width: 100%">
182 184 <el-table-column v-for="col in consumeTableColumns" :key="col.prop" :prop="col.prop" :label="col.label"
183   - :width="col.width" :align="col.align || 'center'">
  185 + :width="col.width" :align="col.align || 'center'" :sortable="col.sortable || false"
  186 + :sort-method="col.sortMethod || undefined">
184 187 <template slot-scope="scope">
185 188 <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span>
186 189 <span v-else>{{ scope.row[col.prop] || '0' }}</span>
... ... @@ -210,7 +213,8 @@
210 213 </div>
211 214 <NCC-table v-loading="headCountTableLoading" :data="headCountTableData" border stripe style="width: 100%">
212 215 <el-table-column v-for="col in headCountTableColumns" :key="col.prop" :prop="col.prop"
213   - :label="col.label" :width="col.width" :align="col.align || 'center'">
  216 + :label="col.label" :width="col.width" :align="col.align || 'center'" :sortable="col.sortable || false"
  217 + :sort-method="col.sortMethod || undefined">
214 218 <template slot-scope="scope">
215 219 <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span>
216 220 <span v-else>{{ scope.row[col.prop] || '0' }}</span>
... ... @@ -241,7 +245,8 @@
241 245 <NCC-table v-loading="personTimeTableLoading" :data="personTimeTableData" border stripe
242 246 style="width: 100%">
243 247 <el-table-column v-for="col in personTimeTableColumns" :key="col.prop" :prop="col.prop"
244   - :label="col.label" :width="col.width" :align="col.align || 'center'">
  248 + :label="col.label" :width="col.width" :align="col.align || 'center'" :sortable="col.sortable || false"
  249 + :sort-method="col.sortMethod || undefined">
245 250 <template slot-scope="scope">
246 251 <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span>
247 252 <span v-else>{{ scope.row[col.prop] || '0' }}</span>
... ... @@ -272,7 +277,8 @@
272 277 <NCC-table v-loading="projectCountTableLoading" :data="projectCountTableData" border stripe
273 278 style="width: 100%">
274 279 <el-table-column v-for="col in projectCountTableColumns" :key="col.prop" :prop="col.prop"
275   - :label="col.label" :width="col.width" :align="col.align || 'center'">
  280 + :label="col.label" :width="col.width" :align="col.align || 'center'" :sortable="col.sortable || false"
  281 + :sort-method="col.sortMethod || undefined">
276 282 <template slot-scope="scope">
277 283 <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span>
278 284 <span v-else>{{ scope.row[col.prop] || '0' }}</span>
... ... @@ -340,7 +346,7 @@
340 346 <NCC-table v-loading="storeIndicatorTableLoading" :data="storeIndicatorTableData" border stripe
341 347 style="width: 100%">
342 348 <el-table-column v-for="col in storeIndicatorTableColumns" :key="col.prop" :prop="col.prop"
343   - :label="col.label">
  349 + :label="col.label" :sortable="col.sortable || false" :sort-method="col.sortMethod || undefined">
344 350 <template slot-scope="scope">
345 351 <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span>
346 352 <span v-else>{{ scope.row[col.prop] || '0' }}</span>
... ... @@ -408,7 +414,7 @@
408 414 <NCC-table v-loading="buIndicatorTableLoading" :data="buIndicatorTableData" border stripe
409 415 style="width: 100%">
410 416 <el-table-column v-for="col in buIndicatorTableColumns" :key="col.prop" :prop="col.prop"
411   - :label="col.label">
  417 + :label="col.label" :sortable="col.sortable || false" :sort-method="col.sortMethod || undefined">
412 418 <template slot-scope="scope">
413 419 <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span>
414 420 <span v-else>{{ scope.row[col.prop] || '0' }}</span>
... ... @@ -431,7 +437,8 @@
431 437 <NCC-table v-loading="buSummaryTableLoading" :data="buSummaryTableData" border stripe style="width: 100%"
432 438 :height="null" class="bu-summary-table">
433 439 <el-table-column v-for="col in buSummaryTableColumns" :key="col.prop" :prop="col.prop" :label="col.label"
434   - :width="col.width" :align="col.align || 'center'">
  440 + :width="col.width" :align="col.align || 'center'" :sortable="col.sortable || false"
  441 + :sort-method="col.sortMethod || undefined">
435 442 <template slot-scope="scope">
436 443 <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span>
437 444 <span v-else>{{ scope.row[col.prop] || '0' }}</span>
... ... @@ -1203,11 +1210,37 @@ export default {
1203 1210 }
1204 1211 })
1205 1212 },
  1213 + // 数字排序方法(返回一个排序函数)
  1214 + getNumberSortMethod(prop) {
  1215 + return (a, b) => {
  1216 + const valA = parseFloat(a[prop]) || 0
  1217 + const valB = parseFloat(b[prop]) || 0
  1218 + return valA - valB
  1219 + }
  1220 + },
  1221 + // 增长率排序方法(处理百分比,返回一个排序函数)
  1222 + getGrowthRateSortMethod(prop) {
  1223 + return (a, b) => {
  1224 + const valA = parseFloat(a[prop]) || 0
  1225 + const valB = parseFloat(b[prop]) || 0
  1226 + return valA - valB
  1227 + }
  1228 + },
  1229 + // 增长率排序方法(处理百分比字符串,返回一个排序函数)
  1230 + getGrowthRateStringSortMethod(prop) {
  1231 + return (a, b) => {
  1232 + const rateA = a[prop] || '0%'
  1233 + const rateB = b[prop] || '0%'
  1234 + const valA = parseFloat(rateA.toString().replace('%', '')) || 0
  1235 + const valB = parseFloat(rateB.toString().replace('%', '')) || 0
  1236 + return valA - valB
  1237 + }
  1238 + },
1206 1239 // 构建趋势表格列
1207 1240 buildTrendTableColumns(data) {
1208 1241 const columns = [
1209   - { label: '事业部', prop: 'businessUnitName', width: 120, align: 'left' },
1210   - { label: '门店', prop: 'storeName', align: 'left' }
  1242 + { label: '事业部', prop: 'businessUnitName', width: 120, align: 'left', sortable: true },
  1243 + { label: '门店', prop: 'storeName', align: 'left', sortable: true }
1211 1244 ]
1212 1245  
1213 1246 for (let i = 1; i <= 12; i++) {
... ... @@ -1216,6 +1249,8 @@ export default {
1216 1249 prop: `month${i}`,
1217 1250 width: 100,
1218 1251 align: 'right',
  1252 + sortable: 'custom',
  1253 + sortMethod: this.getNumberSortMethod(`month${i}`),
1219 1254 formatter: (row) => {
1220 1255 return row[`month${i}`] ? Number(row[`month${i}`]).toLocaleString() : '0'
1221 1256 }
... ... @@ -1228,6 +1263,8 @@ export default {
1228 1263 prop: 'totalCurrentYear',
1229 1264 width: 120,
1230 1265 align: 'right',
  1266 + sortable: 'custom',
  1267 + sortMethod: this.getNumberSortMethod('totalCurrentYear'),
1231 1268 formatter: (row) => {
1232 1269 return row.totalCurrentYear ? Number(row.totalCurrentYear).toLocaleString() : '0'
1233 1270 }
... ... @@ -1237,6 +1274,8 @@ export default {
1237 1274 prop: 'totalLastYear',
1238 1275 width: 120,
1239 1276 align: 'right',
  1277 + sortable: 'custom',
  1278 + sortMethod: this.getNumberSortMethod('totalLastYear'),
1240 1279 formatter: (row) => {
1241 1280 return row.totalLastYear ? Number(row.totalLastYear).toLocaleString() : '0'
1242 1281 }
... ... @@ -1246,6 +1285,8 @@ export default {
1246 1285 prop: 'growthRate',
1247 1286 width: 100,
1248 1287 align: 'right',
  1288 + sortable: 'custom',
  1289 + sortMethod: this.getGrowthRateSortMethod('growthRate'),
1249 1290 formatter: (row) => {
1250 1291 const rate = parseFloat(row.growthRate) || 0
1251 1292 const color = rate >= 0 ? '#67C23A' : '#F56C6C'
... ... @@ -1259,8 +1300,8 @@ export default {
1259 1300 // 构建月度统计表格列
1260 1301 buildMonthlyStatTableColumns(data, name) {
1261 1302 const columns = [
1262   - { label: '事业部', prop: 'businessUnitName', width: 120, align: 'left' },
1263   - { label: '门店', prop: 'storeName', align: 'left' }
  1303 + { label: '事业部', prop: 'businessUnitName', width: 120, align: 'left', sortable: true },
  1304 + { label: '门店', prop: 'storeName', align: 'left', sortable: true }
1264 1305 ]
1265 1306  
1266 1307 for (let i = 1; i <= 12; i++) {
... ... @@ -1269,6 +1310,8 @@ export default {
1269 1310 prop: `month${i}`,
1270 1311 width: 100,
1271 1312 align: 'right',
  1313 + sortable: 'custom',
  1314 + sortMethod: this.getNumberSortMethod(`month${i}`),
1272 1315 formatter: (row) => {
1273 1316 const value = row[`month${i}`] || 0
1274 1317 if (name === '业绩' || name === '消耗') {
... ... @@ -1285,6 +1328,8 @@ export default {
1285 1328 prop: 'totalCurrentYear',
1286 1329 width: 120,
1287 1330 align: 'right',
  1331 + sortable: 'custom',
  1332 + sortMethod: this.getNumberSortMethod('totalCurrentYear'),
1288 1333 formatter: (row) => {
1289 1334 const value = row.totalCurrentYear || 0
1290 1335 if (name === '业绩' || name === '消耗') {
... ... @@ -1298,6 +1343,8 @@ export default {
1298 1343 prop: 'totalLastYear',
1299 1344 width: 120,
1300 1345 align: 'right',
  1346 + sortable: 'custom',
  1347 + sortMethod: this.getNumberSortMethod('totalLastYear'),
1301 1348 formatter: (row) => {
1302 1349 const value = row.totalLastYear || 0
1303 1350 if (name === '业绩' || name === '消耗') {
... ... @@ -1311,6 +1358,8 @@ export default {
1311 1358 prop: 'growthRate',
1312 1359 width: 100,
1313 1360 align: 'right',
  1361 + sortable: 'custom',
  1362 + sortMethod: this.getGrowthRateSortMethod('growthRate'),
1314 1363 formatter: (row) => {
1315 1364 const rate = parseFloat(row.growthRate) || 0
1316 1365 const color = rate >= 0 ? '#67C23A' : '#F56C6C'
... ... @@ -1340,13 +1389,15 @@ export default {
1340 1389 // 构建门店指标表格列
1341 1390 buildStoreIndicatorTableColumns() {
1342 1391 this.storeIndicatorTableColumns = [
1343   - { label: '事业部', prop: 'businessUnitName', width: 120, align: 'left' },
1344   - { label: '门店', prop: 'storeName', align: 'left' },
  1392 + { label: '事业部', prop: 'businessUnitName', width: 120, align: 'left', sortable: true },
  1393 + { label: '门店', prop: 'storeName', align: 'left', sortable: true },
1345 1394 {
1346 1395 label: '本年数值',
1347 1396 prop: 'currentYearValue',
1348 1397 width: 120,
1349 1398 align: 'right',
  1399 + sortable: 'custom',
  1400 + sortMethod: this.getNumberSortMethod('currentYearValue'),
1350 1401 formatter: (row) => {
1351 1402 return Number(row.currentYearValue || 0).toLocaleString()
1352 1403 }
... ... @@ -1356,6 +1407,8 @@ export default {
1356 1407 prop: 'lastYearValue',
1357 1408 width: 120,
1358 1409 align: 'right',
  1410 + sortable: 'custom',
  1411 + sortMethod: this.getNumberSortMethod('lastYearValue'),
1359 1412 formatter: (row) => {
1360 1413 return Number(row.lastYearValue || 0).toLocaleString()
1361 1414 }
... ... @@ -1365,6 +1418,8 @@ export default {
1365 1418 prop: 'growthRate',
1366 1419 width: 100,
1367 1420 align: 'right',
  1421 + sortable: 'custom',
  1422 + sortMethod: this.getGrowthRateSortMethod('growthRate'),
1368 1423 formatter: (row) => {
1369 1424 const rate = parseFloat(row.growthRate) || 0
1370 1425 const color = rate >= 0 ? '#67C23A' : '#F56C6C'
... ... @@ -1376,12 +1431,14 @@ export default {
1376 1431 // 构建事业部指标表格列
1377 1432 buildBuIndicatorTableColumns() {
1378 1433 this.buIndicatorTableColumns = [
1379   - { label: '事业部', prop: 'businessUnitName', width: 200, align: 'left' },
  1434 + { label: '事业部', prop: 'businessUnitName', width: 200, align: 'left', sortable: true },
1380 1435 {
1381 1436 label: '本年数值',
1382 1437 prop: 'currentYearValue',
1383 1438 width: 150,
1384 1439 align: 'right',
  1440 + sortable: 'custom',
  1441 + sortMethod: this.getNumberSortMethod('currentYearValue'),
1385 1442 formatter: (row) => {
1386 1443 return Number(row.currentYearValue || 0).toLocaleString()
1387 1444 }
... ... @@ -1391,6 +1448,8 @@ export default {
1391 1448 prop: 'lastYearValue',
1392 1449 width: 150,
1393 1450 align: 'right',
  1451 + sortable: 'custom',
  1452 + sortMethod: this.getNumberSortMethod('lastYearValue'),
1394 1453 formatter: (row) => {
1395 1454 return Number(row.lastYearValue || 0).toLocaleString()
1396 1455 }
... ... @@ -1400,6 +1459,8 @@ export default {
1400 1459 prop: 'growthRate',
1401 1460 width: 120,
1402 1461 align: 'right',
  1462 + sortable: 'custom',
  1463 + sortMethod: this.getGrowthRateSortMethod('growthRate'),
1403 1464 formatter: (row) => {
1404 1465 const rate = parseFloat(row.growthRate) || 0
1405 1466 const color = rate >= 0 ? '#67C23A' : '#F56C6C'
... ... @@ -1411,13 +1472,15 @@ export default {
1411 1472 // 构建事业部汇总表格列
1412 1473 buildBuSummaryTableColumns() {
1413 1474 this.buSummaryTableColumns = [
1414   - { label: '事业部', prop: 'businessUnitName', width: 120, align: 'left' },
1415   - { label: '门店', prop: 'storeName', align: 'left' },
  1475 + { label: '事业部', prop: 'businessUnitName', width: 120, align: 'left', sortable: true },
  1476 + { label: '门店', prop: 'storeName', align: 'left', sortable: true },
1416 1477 {
1417 1478 label: '本年业绩',
1418 1479 prop: 'currentPerformance',
1419 1480 width: 120,
1420 1481 align: 'right',
  1482 + sortable: 'custom',
  1483 + sortMethod: this.getNumberSortMethod('currentPerformance'),
1421 1484 formatter: (row) => {
1422 1485 return `¥${Number(row.currentPerformance || 0).toLocaleString()}`
1423 1486 }
... ... @@ -1427,6 +1490,8 @@ export default {
1427 1490 prop: 'lastPerformance',
1428 1491 width: 120,
1429 1492 align: 'right',
  1493 + sortable: 'custom',
  1494 + sortMethod: this.getNumberSortMethod('lastPerformance'),
1430 1495 formatter: (row) => {
1431 1496 return `¥${Number(row.lastPerformance || 0).toLocaleString()}`
1432 1497 }
... ... @@ -1436,6 +1501,8 @@ export default {
1436 1501 prop: 'performanceGrowthRate',
1437 1502 width: 110,
1438 1503 align: 'right',
  1504 + sortable: 'custom',
  1505 + sortMethod: this.getGrowthRateStringSortMethod('performanceGrowthRate'),
1439 1506 formatter: (row) => {
1440 1507 const rate = row.performanceGrowthRate || '0%'
1441 1508 const numRate = parseFloat(rate) || 0
... ... @@ -1448,6 +1515,8 @@ export default {
1448 1515 prop: 'currentConsume',
1449 1516 width: 120,
1450 1517 align: 'right',
  1518 + sortable: 'custom',
  1519 + sortMethod: this.getNumberSortMethod('currentConsume'),
1451 1520 formatter: (row) => {
1452 1521 return `¥${Number(row.currentConsume || 0).toLocaleString()}`
1453 1522 }
... ... @@ -1457,6 +1526,8 @@ export default {
1457 1526 prop: 'lastConsume',
1458 1527 width: 120,
1459 1528 align: 'right',
  1529 + sortable: 'custom',
  1530 + sortMethod: this.getNumberSortMethod('lastConsume'),
1460 1531 formatter: (row) => {
1461 1532 return `¥${Number(row.lastConsume || 0).toLocaleString()}`
1462 1533 }
... ... @@ -1466,6 +1537,8 @@ export default {
1466 1537 prop: 'consumeGrowthRate',
1467 1538 width: 110,
1468 1539 align: 'right',
  1540 + sortable: 'custom',
  1541 + sortMethod: this.getGrowthRateStringSortMethod('consumeGrowthRate'),
1469 1542 formatter: (row) => {
1470 1543 const rate = row.consumeGrowthRate || '0%'
1471 1544 const numRate = parseFloat(rate) || 0
... ... @@ -1478,6 +1551,8 @@ export default {
1478 1551 prop: 'currentHeadCount',
1479 1552 width: 100,
1480 1553 align: 'right',
  1554 + sortable: 'custom',
  1555 + sortMethod: this.getNumberSortMethod('currentHeadCount'),
1481 1556 formatter: (row) => {
1482 1557 return Number(row.currentHeadCount || 0).toLocaleString()
1483 1558 }
... ... @@ -1487,6 +1562,8 @@ export default {
1487 1562 prop: 'lastHeadCount',
1488 1563 width: 100,
1489 1564 align: 'right',
  1565 + sortable: 'custom',
  1566 + sortMethod: this.getNumberSortMethod('lastHeadCount'),
1490 1567 formatter: (row) => {
1491 1568 return Number(row.lastHeadCount || 0).toLocaleString()
1492 1569 }
... ... @@ -1496,6 +1573,8 @@ export default {
1496 1573 prop: 'headCountGrowthRate',
1497 1574 width: 110,
1498 1575 align: 'right',
  1576 + sortable: 'custom',
  1577 + sortMethod: this.getGrowthRateStringSortMethod('headCountGrowthRate'),
1499 1578 formatter: (row) => {
1500 1579 const rate = row.headCountGrowthRate || '0%'
1501 1580 const numRate = parseFloat(rate) || 0
... ... @@ -1508,6 +1587,8 @@ export default {
1508 1587 prop: 'currentPersonTime',
1509 1588 width: 100,
1510 1589 align: 'right',
  1590 + sortable: 'custom',
  1591 + sortMethod: this.getNumberSortMethod('currentPersonTime'),
1511 1592 formatter: (row) => {
1512 1593 return Number(row.currentPersonTime || 0).toLocaleString()
1513 1594 }
... ... @@ -1517,6 +1598,8 @@ export default {
1517 1598 prop: 'lastPersonTime',
1518 1599 width: 100,
1519 1600 align: 'right',
  1601 + sortable: 'custom',
  1602 + sortMethod: this.getNumberSortMethod('lastPersonTime'),
1520 1603 formatter: (row) => {
1521 1604 return Number(row.lastPersonTime || 0).toLocaleString()
1522 1605 }
... ... @@ -1526,6 +1609,8 @@ export default {
1526 1609 prop: 'personTimeGrowthRate',
1527 1610 width: 110,
1528 1611 align: 'right',
  1612 + sortable: 'custom',
  1613 + sortMethod: this.getGrowthRateStringSortMethod('personTimeGrowthRate'),
1529 1614 formatter: (row) => {
1530 1615 const rate = row.personTimeGrowthRate || '0%'
1531 1616 const numRate = parseFloat(rate) || 0
... ... @@ -1538,6 +1623,8 @@ export default {
1538 1623 prop: 'currentProjectCount',
1539 1624 width: 100,
1540 1625 align: 'right',
  1626 + sortable: 'custom',
  1627 + sortMethod: this.getNumberSortMethod('currentProjectCount'),
1541 1628 formatter: (row) => {
1542 1629 return Number(row.currentProjectCount || 0).toLocaleString()
1543 1630 }
... ... @@ -1547,6 +1634,8 @@ export default {
1547 1634 prop: 'lastProjectCount',
1548 1635 width: 100,
1549 1636 align: 'right',
  1637 + sortable: 'custom',
  1638 + sortMethod: this.getNumberSortMethod('lastProjectCount'),
1550 1639 formatter: (row) => {
1551 1640 return Number(row.lastProjectCount || 0).toLocaleString()
1552 1641 }
... ... @@ -1556,6 +1645,8 @@ export default {
1556 1645 prop: 'projectCountGrowthRate',
1557 1646 width: 110,
1558 1647 align: 'right',
  1648 + sortable: 'custom',
  1649 + sortMethod: this.getGrowthRateStringSortMethod('projectCountGrowthRate'),
1559 1650 formatter: (row) => {
1560 1651 const rate = row.projectCountGrowthRate || '0%'
1561 1652 const numRate = parseFloat(rate) || 0
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqAnnualSummaryService.cs
... ... @@ -985,8 +985,17 @@ namespace NCC.Extend
985 985 .ToListAsync();
986 986  
987 987 // 整理所有门店 (包括今年有数据和去年有数据的)
988   - var allStores = currentYearData.Select(x => new { x.StoreId, x.StoreName, x.BusinessUnitName, x.BusinessUnitId })
989   - .Distinct()
  988 + // 按门店ID去重,确保每个门店只出现一次
  989 + // 如果同一门店有多个事业部,取第一个(通常应该只有一个)
  990 + var allStores = currentYearData
  991 + .GroupBy(x => x.StoreId)
  992 + .Select(g => new
  993 + {
  994 + StoreId = g.Key,
  995 + StoreName = g.First().StoreName,
  996 + BusinessUnitName = g.First().BusinessUnitName,
  997 + BusinessUnitId = g.First().BusinessUnitId
  998 + })
990 999 .OrderBy(x => x.BusinessUnitName)
991 1000 .ThenBy(x => x.StoreName)
992 1001 .ToList();
... ...