Commit 189ad8f683f9fe588b2430ac034600d8cba2fb3d
Merge branch 'master' of http://39.98.150.180/antissoft/lvqianmeiye_ERP
Showing
2 changed files
with
120 additions
and
20 deletions
antis-ncc-admin/src/views/extend/annualSummary/dashboard/index.vue
| @@ -119,7 +119,8 @@ | @@ -119,7 +119,8 @@ | ||
| 119 | </div> | 119 | </div> |
| 120 | <NCC-table v-loading="trendTableLoading" :data="trendTableData" border stripe style="width: 100%"> | 120 | <NCC-table v-loading="trendTableLoading" :data="trendTableData" border stripe style="width: 100%"> |
| 121 | <el-table-column v-for="col in trendTableColumns" :key="col.prop" :prop="col.prop" :label="col.label" | 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 | <template slot-scope="scope"> | 124 | <template slot-scope="scope"> |
| 124 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> | 125 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> |
| 125 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> | 126 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> |
| @@ -150,7 +151,8 @@ | @@ -150,7 +151,8 @@ | ||
| 150 | <NCC-table v-loading="performanceTableLoading" :data="performanceTableData" border stripe | 151 | <NCC-table v-loading="performanceTableLoading" :data="performanceTableData" border stripe |
| 151 | style="width: 100%"> | 152 | style="width: 100%"> |
| 152 | <el-table-column v-for="col in performanceTableColumns" :key="col.prop" :prop="col.prop" | 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 | <template slot-scope="scope"> | 156 | <template slot-scope="scope"> |
| 155 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> | 157 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> |
| 156 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> | 158 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> |
| @@ -180,7 +182,8 @@ | @@ -180,7 +182,8 @@ | ||
| 180 | </div> | 182 | </div> |
| 181 | <NCC-table v-loading="consumeTableLoading" :data="consumeTableData" border stripe style="width: 100%"> | 183 | <NCC-table v-loading="consumeTableLoading" :data="consumeTableData" border stripe style="width: 100%"> |
| 182 | <el-table-column v-for="col in consumeTableColumns" :key="col.prop" :prop="col.prop" :label="col.label" | 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 | <template slot-scope="scope"> | 187 | <template slot-scope="scope"> |
| 185 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> | 188 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> |
| 186 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> | 189 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> |
| @@ -210,7 +213,8 @@ | @@ -210,7 +213,8 @@ | ||
| 210 | </div> | 213 | </div> |
| 211 | <NCC-table v-loading="headCountTableLoading" :data="headCountTableData" border stripe style="width: 100%"> | 214 | <NCC-table v-loading="headCountTableLoading" :data="headCountTableData" border stripe style="width: 100%"> |
| 212 | <el-table-column v-for="col in headCountTableColumns" :key="col.prop" :prop="col.prop" | 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 | <template slot-scope="scope"> | 218 | <template slot-scope="scope"> |
| 215 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> | 219 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> |
| 216 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> | 220 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> |
| @@ -241,7 +245,8 @@ | @@ -241,7 +245,8 @@ | ||
| 241 | <NCC-table v-loading="personTimeTableLoading" :data="personTimeTableData" border stripe | 245 | <NCC-table v-loading="personTimeTableLoading" :data="personTimeTableData" border stripe |
| 242 | style="width: 100%"> | 246 | style="width: 100%"> |
| 243 | <el-table-column v-for="col in personTimeTableColumns" :key="col.prop" :prop="col.prop" | 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 | <template slot-scope="scope"> | 250 | <template slot-scope="scope"> |
| 246 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> | 251 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> |
| 247 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> | 252 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> |
| @@ -272,7 +277,8 @@ | @@ -272,7 +277,8 @@ | ||
| 272 | <NCC-table v-loading="projectCountTableLoading" :data="projectCountTableData" border stripe | 277 | <NCC-table v-loading="projectCountTableLoading" :data="projectCountTableData" border stripe |
| 273 | style="width: 100%"> | 278 | style="width: 100%"> |
| 274 | <el-table-column v-for="col in projectCountTableColumns" :key="col.prop" :prop="col.prop" | 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 | <template slot-scope="scope"> | 282 | <template slot-scope="scope"> |
| 277 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> | 283 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> |
| 278 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> | 284 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> |
| @@ -340,7 +346,7 @@ | @@ -340,7 +346,7 @@ | ||
| 340 | <NCC-table v-loading="storeIndicatorTableLoading" :data="storeIndicatorTableData" border stripe | 346 | <NCC-table v-loading="storeIndicatorTableLoading" :data="storeIndicatorTableData" border stripe |
| 341 | style="width: 100%"> | 347 | style="width: 100%"> |
| 342 | <el-table-column v-for="col in storeIndicatorTableColumns" :key="col.prop" :prop="col.prop" | 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 | <template slot-scope="scope"> | 350 | <template slot-scope="scope"> |
| 345 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> | 351 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> |
| 346 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> | 352 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> |
| @@ -408,7 +414,7 @@ | @@ -408,7 +414,7 @@ | ||
| 408 | <NCC-table v-loading="buIndicatorTableLoading" :data="buIndicatorTableData" border stripe | 414 | <NCC-table v-loading="buIndicatorTableLoading" :data="buIndicatorTableData" border stripe |
| 409 | style="width: 100%"> | 415 | style="width: 100%"> |
| 410 | <el-table-column v-for="col in buIndicatorTableColumns" :key="col.prop" :prop="col.prop" | 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 | <template slot-scope="scope"> | 418 | <template slot-scope="scope"> |
| 413 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> | 419 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> |
| 414 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> | 420 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> |
| @@ -431,7 +437,8 @@ | @@ -431,7 +437,8 @@ | ||
| 431 | <NCC-table v-loading="buSummaryTableLoading" :data="buSummaryTableData" border stripe style="width: 100%" | 437 | <NCC-table v-loading="buSummaryTableLoading" :data="buSummaryTableData" border stripe style="width: 100%" |
| 432 | :height="null" class="bu-summary-table"> | 438 | :height="null" class="bu-summary-table"> |
| 433 | <el-table-column v-for="col in buSummaryTableColumns" :key="col.prop" :prop="col.prop" :label="col.label" | 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 | <template slot-scope="scope"> | 442 | <template slot-scope="scope"> |
| 436 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> | 443 | <span v-if="col.formatter" v-html="col.formatter(scope.row)"></span> |
| 437 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> | 444 | <span v-else>{{ scope.row[col.prop] || '0' }}</span> |
| @@ -1203,11 +1210,37 @@ export default { | @@ -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 | buildTrendTableColumns(data) { | 1240 | buildTrendTableColumns(data) { |
| 1208 | const columns = [ | 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 | for (let i = 1; i <= 12; i++) { | 1246 | for (let i = 1; i <= 12; i++) { |
| @@ -1216,6 +1249,8 @@ export default { | @@ -1216,6 +1249,8 @@ export default { | ||
| 1216 | prop: `month${i}`, | 1249 | prop: `month${i}`, |
| 1217 | width: 100, | 1250 | width: 100, |
| 1218 | align: 'right', | 1251 | align: 'right', |
| 1252 | + sortable: 'custom', | ||
| 1253 | + sortMethod: this.getNumberSortMethod(`month${i}`), | ||
| 1219 | formatter: (row) => { | 1254 | formatter: (row) => { |
| 1220 | return row[`month${i}`] ? Number(row[`month${i}`]).toLocaleString() : '0' | 1255 | return row[`month${i}`] ? Number(row[`month${i}`]).toLocaleString() : '0' |
| 1221 | } | 1256 | } |
| @@ -1228,6 +1263,8 @@ export default { | @@ -1228,6 +1263,8 @@ export default { | ||
| 1228 | prop: 'totalCurrentYear', | 1263 | prop: 'totalCurrentYear', |
| 1229 | width: 120, | 1264 | width: 120, |
| 1230 | align: 'right', | 1265 | align: 'right', |
| 1266 | + sortable: 'custom', | ||
| 1267 | + sortMethod: this.getNumberSortMethod('totalCurrentYear'), | ||
| 1231 | formatter: (row) => { | 1268 | formatter: (row) => { |
| 1232 | return row.totalCurrentYear ? Number(row.totalCurrentYear).toLocaleString() : '0' | 1269 | return row.totalCurrentYear ? Number(row.totalCurrentYear).toLocaleString() : '0' |
| 1233 | } | 1270 | } |
| @@ -1237,6 +1274,8 @@ export default { | @@ -1237,6 +1274,8 @@ export default { | ||
| 1237 | prop: 'totalLastYear', | 1274 | prop: 'totalLastYear', |
| 1238 | width: 120, | 1275 | width: 120, |
| 1239 | align: 'right', | 1276 | align: 'right', |
| 1277 | + sortable: 'custom', | ||
| 1278 | + sortMethod: this.getNumberSortMethod('totalLastYear'), | ||
| 1240 | formatter: (row) => { | 1279 | formatter: (row) => { |
| 1241 | return row.totalLastYear ? Number(row.totalLastYear).toLocaleString() : '0' | 1280 | return row.totalLastYear ? Number(row.totalLastYear).toLocaleString() : '0' |
| 1242 | } | 1281 | } |
| @@ -1246,6 +1285,8 @@ export default { | @@ -1246,6 +1285,8 @@ export default { | ||
| 1246 | prop: 'growthRate', | 1285 | prop: 'growthRate', |
| 1247 | width: 100, | 1286 | width: 100, |
| 1248 | align: 'right', | 1287 | align: 'right', |
| 1288 | + sortable: 'custom', | ||
| 1289 | + sortMethod: this.getGrowthRateSortMethod('growthRate'), | ||
| 1249 | formatter: (row) => { | 1290 | formatter: (row) => { |
| 1250 | const rate = parseFloat(row.growthRate) || 0 | 1291 | const rate = parseFloat(row.growthRate) || 0 |
| 1251 | const color = rate >= 0 ? '#67C23A' : '#F56C6C' | 1292 | const color = rate >= 0 ? '#67C23A' : '#F56C6C' |
| @@ -1259,8 +1300,8 @@ export default { | @@ -1259,8 +1300,8 @@ export default { | ||
| 1259 | // 构建月度统计表格列 | 1300 | // 构建月度统计表格列 |
| 1260 | buildMonthlyStatTableColumns(data, name) { | 1301 | buildMonthlyStatTableColumns(data, name) { |
| 1261 | const columns = [ | 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 | for (let i = 1; i <= 12; i++) { | 1307 | for (let i = 1; i <= 12; i++) { |
| @@ -1269,6 +1310,8 @@ export default { | @@ -1269,6 +1310,8 @@ export default { | ||
| 1269 | prop: `month${i}`, | 1310 | prop: `month${i}`, |
| 1270 | width: 100, | 1311 | width: 100, |
| 1271 | align: 'right', | 1312 | align: 'right', |
| 1313 | + sortable: 'custom', | ||
| 1314 | + sortMethod: this.getNumberSortMethod(`month${i}`), | ||
| 1272 | formatter: (row) => { | 1315 | formatter: (row) => { |
| 1273 | const value = row[`month${i}`] || 0 | 1316 | const value = row[`month${i}`] || 0 |
| 1274 | if (name === '业绩' || name === '消耗') { | 1317 | if (name === '业绩' || name === '消耗') { |
| @@ -1285,6 +1328,8 @@ export default { | @@ -1285,6 +1328,8 @@ export default { | ||
| 1285 | prop: 'totalCurrentYear', | 1328 | prop: 'totalCurrentYear', |
| 1286 | width: 120, | 1329 | width: 120, |
| 1287 | align: 'right', | 1330 | align: 'right', |
| 1331 | + sortable: 'custom', | ||
| 1332 | + sortMethod: this.getNumberSortMethod('totalCurrentYear'), | ||
| 1288 | formatter: (row) => { | 1333 | formatter: (row) => { |
| 1289 | const value = row.totalCurrentYear || 0 | 1334 | const value = row.totalCurrentYear || 0 |
| 1290 | if (name === '业绩' || name === '消耗') { | 1335 | if (name === '业绩' || name === '消耗') { |
| @@ -1298,6 +1343,8 @@ export default { | @@ -1298,6 +1343,8 @@ export default { | ||
| 1298 | prop: 'totalLastYear', | 1343 | prop: 'totalLastYear', |
| 1299 | width: 120, | 1344 | width: 120, |
| 1300 | align: 'right', | 1345 | align: 'right', |
| 1346 | + sortable: 'custom', | ||
| 1347 | + sortMethod: this.getNumberSortMethod('totalLastYear'), | ||
| 1301 | formatter: (row) => { | 1348 | formatter: (row) => { |
| 1302 | const value = row.totalLastYear || 0 | 1349 | const value = row.totalLastYear || 0 |
| 1303 | if (name === '业绩' || name === '消耗') { | 1350 | if (name === '业绩' || name === '消耗') { |
| @@ -1311,6 +1358,8 @@ export default { | @@ -1311,6 +1358,8 @@ export default { | ||
| 1311 | prop: 'growthRate', | 1358 | prop: 'growthRate', |
| 1312 | width: 100, | 1359 | width: 100, |
| 1313 | align: 'right', | 1360 | align: 'right', |
| 1361 | + sortable: 'custom', | ||
| 1362 | + sortMethod: this.getGrowthRateSortMethod('growthRate'), | ||
| 1314 | formatter: (row) => { | 1363 | formatter: (row) => { |
| 1315 | const rate = parseFloat(row.growthRate) || 0 | 1364 | const rate = parseFloat(row.growthRate) || 0 |
| 1316 | const color = rate >= 0 ? '#67C23A' : '#F56C6C' | 1365 | const color = rate >= 0 ? '#67C23A' : '#F56C6C' |
| @@ -1340,13 +1389,15 @@ export default { | @@ -1340,13 +1389,15 @@ export default { | ||
| 1340 | // 构建门店指标表格列 | 1389 | // 构建门店指标表格列 |
| 1341 | buildStoreIndicatorTableColumns() { | 1390 | buildStoreIndicatorTableColumns() { |
| 1342 | this.storeIndicatorTableColumns = [ | 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 | label: '本年数值', | 1395 | label: '本年数值', |
| 1347 | prop: 'currentYearValue', | 1396 | prop: 'currentYearValue', |
| 1348 | width: 120, | 1397 | width: 120, |
| 1349 | align: 'right', | 1398 | align: 'right', |
| 1399 | + sortable: 'custom', | ||
| 1400 | + sortMethod: this.getNumberSortMethod('currentYearValue'), | ||
| 1350 | formatter: (row) => { | 1401 | formatter: (row) => { |
| 1351 | return Number(row.currentYearValue || 0).toLocaleString() | 1402 | return Number(row.currentYearValue || 0).toLocaleString() |
| 1352 | } | 1403 | } |
| @@ -1356,6 +1407,8 @@ export default { | @@ -1356,6 +1407,8 @@ export default { | ||
| 1356 | prop: 'lastYearValue', | 1407 | prop: 'lastYearValue', |
| 1357 | width: 120, | 1408 | width: 120, |
| 1358 | align: 'right', | 1409 | align: 'right', |
| 1410 | + sortable: 'custom', | ||
| 1411 | + sortMethod: this.getNumberSortMethod('lastYearValue'), | ||
| 1359 | formatter: (row) => { | 1412 | formatter: (row) => { |
| 1360 | return Number(row.lastYearValue || 0).toLocaleString() | 1413 | return Number(row.lastYearValue || 0).toLocaleString() |
| 1361 | } | 1414 | } |
| @@ -1365,6 +1418,8 @@ export default { | @@ -1365,6 +1418,8 @@ export default { | ||
| 1365 | prop: 'growthRate', | 1418 | prop: 'growthRate', |
| 1366 | width: 100, | 1419 | width: 100, |
| 1367 | align: 'right', | 1420 | align: 'right', |
| 1421 | + sortable: 'custom', | ||
| 1422 | + sortMethod: this.getGrowthRateSortMethod('growthRate'), | ||
| 1368 | formatter: (row) => { | 1423 | formatter: (row) => { |
| 1369 | const rate = parseFloat(row.growthRate) || 0 | 1424 | const rate = parseFloat(row.growthRate) || 0 |
| 1370 | const color = rate >= 0 ? '#67C23A' : '#F56C6C' | 1425 | const color = rate >= 0 ? '#67C23A' : '#F56C6C' |
| @@ -1376,12 +1431,14 @@ export default { | @@ -1376,12 +1431,14 @@ export default { | ||
| 1376 | // 构建事业部指标表格列 | 1431 | // 构建事业部指标表格列 |
| 1377 | buildBuIndicatorTableColumns() { | 1432 | buildBuIndicatorTableColumns() { |
| 1378 | this.buIndicatorTableColumns = [ | 1433 | this.buIndicatorTableColumns = [ |
| 1379 | - { label: '事业部', prop: 'businessUnitName', width: 200, align: 'left' }, | 1434 | + { label: '事业部', prop: 'businessUnitName', width: 200, align: 'left', sortable: true }, |
| 1380 | { | 1435 | { |
| 1381 | label: '本年数值', | 1436 | label: '本年数值', |
| 1382 | prop: 'currentYearValue', | 1437 | prop: 'currentYearValue', |
| 1383 | width: 150, | 1438 | width: 150, |
| 1384 | align: 'right', | 1439 | align: 'right', |
| 1440 | + sortable: 'custom', | ||
| 1441 | + sortMethod: this.getNumberSortMethod('currentYearValue'), | ||
| 1385 | formatter: (row) => { | 1442 | formatter: (row) => { |
| 1386 | return Number(row.currentYearValue || 0).toLocaleString() | 1443 | return Number(row.currentYearValue || 0).toLocaleString() |
| 1387 | } | 1444 | } |
| @@ -1391,6 +1448,8 @@ export default { | @@ -1391,6 +1448,8 @@ export default { | ||
| 1391 | prop: 'lastYearValue', | 1448 | prop: 'lastYearValue', |
| 1392 | width: 150, | 1449 | width: 150, |
| 1393 | align: 'right', | 1450 | align: 'right', |
| 1451 | + sortable: 'custom', | ||
| 1452 | + sortMethod: this.getNumberSortMethod('lastYearValue'), | ||
| 1394 | formatter: (row) => { | 1453 | formatter: (row) => { |
| 1395 | return Number(row.lastYearValue || 0).toLocaleString() | 1454 | return Number(row.lastYearValue || 0).toLocaleString() |
| 1396 | } | 1455 | } |
| @@ -1400,6 +1459,8 @@ export default { | @@ -1400,6 +1459,8 @@ export default { | ||
| 1400 | prop: 'growthRate', | 1459 | prop: 'growthRate', |
| 1401 | width: 120, | 1460 | width: 120, |
| 1402 | align: 'right', | 1461 | align: 'right', |
| 1462 | + sortable: 'custom', | ||
| 1463 | + sortMethod: this.getGrowthRateSortMethod('growthRate'), | ||
| 1403 | formatter: (row) => { | 1464 | formatter: (row) => { |
| 1404 | const rate = parseFloat(row.growthRate) || 0 | 1465 | const rate = parseFloat(row.growthRate) || 0 |
| 1405 | const color = rate >= 0 ? '#67C23A' : '#F56C6C' | 1466 | const color = rate >= 0 ? '#67C23A' : '#F56C6C' |
| @@ -1411,13 +1472,15 @@ export default { | @@ -1411,13 +1472,15 @@ export default { | ||
| 1411 | // 构建事业部汇总表格列 | 1472 | // 构建事业部汇总表格列 |
| 1412 | buildBuSummaryTableColumns() { | 1473 | buildBuSummaryTableColumns() { |
| 1413 | this.buSummaryTableColumns = [ | 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 | label: '本年业绩', | 1478 | label: '本年业绩', |
| 1418 | prop: 'currentPerformance', | 1479 | prop: 'currentPerformance', |
| 1419 | width: 120, | 1480 | width: 120, |
| 1420 | align: 'right', | 1481 | align: 'right', |
| 1482 | + sortable: 'custom', | ||
| 1483 | + sortMethod: this.getNumberSortMethod('currentPerformance'), | ||
| 1421 | formatter: (row) => { | 1484 | formatter: (row) => { |
| 1422 | return `¥${Number(row.currentPerformance || 0).toLocaleString()}` | 1485 | return `¥${Number(row.currentPerformance || 0).toLocaleString()}` |
| 1423 | } | 1486 | } |
| @@ -1427,6 +1490,8 @@ export default { | @@ -1427,6 +1490,8 @@ export default { | ||
| 1427 | prop: 'lastPerformance', | 1490 | prop: 'lastPerformance', |
| 1428 | width: 120, | 1491 | width: 120, |
| 1429 | align: 'right', | 1492 | align: 'right', |
| 1493 | + sortable: 'custom', | ||
| 1494 | + sortMethod: this.getNumberSortMethod('lastPerformance'), | ||
| 1430 | formatter: (row) => { | 1495 | formatter: (row) => { |
| 1431 | return `¥${Number(row.lastPerformance || 0).toLocaleString()}` | 1496 | return `¥${Number(row.lastPerformance || 0).toLocaleString()}` |
| 1432 | } | 1497 | } |
| @@ -1436,6 +1501,8 @@ export default { | @@ -1436,6 +1501,8 @@ export default { | ||
| 1436 | prop: 'performanceGrowthRate', | 1501 | prop: 'performanceGrowthRate', |
| 1437 | width: 110, | 1502 | width: 110, |
| 1438 | align: 'right', | 1503 | align: 'right', |
| 1504 | + sortable: 'custom', | ||
| 1505 | + sortMethod: this.getGrowthRateStringSortMethod('performanceGrowthRate'), | ||
| 1439 | formatter: (row) => { | 1506 | formatter: (row) => { |
| 1440 | const rate = row.performanceGrowthRate || '0%' | 1507 | const rate = row.performanceGrowthRate || '0%' |
| 1441 | const numRate = parseFloat(rate) || 0 | 1508 | const numRate = parseFloat(rate) || 0 |
| @@ -1448,6 +1515,8 @@ export default { | @@ -1448,6 +1515,8 @@ export default { | ||
| 1448 | prop: 'currentConsume', | 1515 | prop: 'currentConsume', |
| 1449 | width: 120, | 1516 | width: 120, |
| 1450 | align: 'right', | 1517 | align: 'right', |
| 1518 | + sortable: 'custom', | ||
| 1519 | + sortMethod: this.getNumberSortMethod('currentConsume'), | ||
| 1451 | formatter: (row) => { | 1520 | formatter: (row) => { |
| 1452 | return `¥${Number(row.currentConsume || 0).toLocaleString()}` | 1521 | return `¥${Number(row.currentConsume || 0).toLocaleString()}` |
| 1453 | } | 1522 | } |
| @@ -1457,6 +1526,8 @@ export default { | @@ -1457,6 +1526,8 @@ export default { | ||
| 1457 | prop: 'lastConsume', | 1526 | prop: 'lastConsume', |
| 1458 | width: 120, | 1527 | width: 120, |
| 1459 | align: 'right', | 1528 | align: 'right', |
| 1529 | + sortable: 'custom', | ||
| 1530 | + sortMethod: this.getNumberSortMethod('lastConsume'), | ||
| 1460 | formatter: (row) => { | 1531 | formatter: (row) => { |
| 1461 | return `¥${Number(row.lastConsume || 0).toLocaleString()}` | 1532 | return `¥${Number(row.lastConsume || 0).toLocaleString()}` |
| 1462 | } | 1533 | } |
| @@ -1466,6 +1537,8 @@ export default { | @@ -1466,6 +1537,8 @@ export default { | ||
| 1466 | prop: 'consumeGrowthRate', | 1537 | prop: 'consumeGrowthRate', |
| 1467 | width: 110, | 1538 | width: 110, |
| 1468 | align: 'right', | 1539 | align: 'right', |
| 1540 | + sortable: 'custom', | ||
| 1541 | + sortMethod: this.getGrowthRateStringSortMethod('consumeGrowthRate'), | ||
| 1469 | formatter: (row) => { | 1542 | formatter: (row) => { |
| 1470 | const rate = row.consumeGrowthRate || '0%' | 1543 | const rate = row.consumeGrowthRate || '0%' |
| 1471 | const numRate = parseFloat(rate) || 0 | 1544 | const numRate = parseFloat(rate) || 0 |
| @@ -1478,6 +1551,8 @@ export default { | @@ -1478,6 +1551,8 @@ export default { | ||
| 1478 | prop: 'currentHeadCount', | 1551 | prop: 'currentHeadCount', |
| 1479 | width: 100, | 1552 | width: 100, |
| 1480 | align: 'right', | 1553 | align: 'right', |
| 1554 | + sortable: 'custom', | ||
| 1555 | + sortMethod: this.getNumberSortMethod('currentHeadCount'), | ||
| 1481 | formatter: (row) => { | 1556 | formatter: (row) => { |
| 1482 | return Number(row.currentHeadCount || 0).toLocaleString() | 1557 | return Number(row.currentHeadCount || 0).toLocaleString() |
| 1483 | } | 1558 | } |
| @@ -1487,6 +1562,8 @@ export default { | @@ -1487,6 +1562,8 @@ export default { | ||
| 1487 | prop: 'lastHeadCount', | 1562 | prop: 'lastHeadCount', |
| 1488 | width: 100, | 1563 | width: 100, |
| 1489 | align: 'right', | 1564 | align: 'right', |
| 1565 | + sortable: 'custom', | ||
| 1566 | + sortMethod: this.getNumberSortMethod('lastHeadCount'), | ||
| 1490 | formatter: (row) => { | 1567 | formatter: (row) => { |
| 1491 | return Number(row.lastHeadCount || 0).toLocaleString() | 1568 | return Number(row.lastHeadCount || 0).toLocaleString() |
| 1492 | } | 1569 | } |
| @@ -1496,6 +1573,8 @@ export default { | @@ -1496,6 +1573,8 @@ export default { | ||
| 1496 | prop: 'headCountGrowthRate', | 1573 | prop: 'headCountGrowthRate', |
| 1497 | width: 110, | 1574 | width: 110, |
| 1498 | align: 'right', | 1575 | align: 'right', |
| 1576 | + sortable: 'custom', | ||
| 1577 | + sortMethod: this.getGrowthRateStringSortMethod('headCountGrowthRate'), | ||
| 1499 | formatter: (row) => { | 1578 | formatter: (row) => { |
| 1500 | const rate = row.headCountGrowthRate || '0%' | 1579 | const rate = row.headCountGrowthRate || '0%' |
| 1501 | const numRate = parseFloat(rate) || 0 | 1580 | const numRate = parseFloat(rate) || 0 |
| @@ -1508,6 +1587,8 @@ export default { | @@ -1508,6 +1587,8 @@ export default { | ||
| 1508 | prop: 'currentPersonTime', | 1587 | prop: 'currentPersonTime', |
| 1509 | width: 100, | 1588 | width: 100, |
| 1510 | align: 'right', | 1589 | align: 'right', |
| 1590 | + sortable: 'custom', | ||
| 1591 | + sortMethod: this.getNumberSortMethod('currentPersonTime'), | ||
| 1511 | formatter: (row) => { | 1592 | formatter: (row) => { |
| 1512 | return Number(row.currentPersonTime || 0).toLocaleString() | 1593 | return Number(row.currentPersonTime || 0).toLocaleString() |
| 1513 | } | 1594 | } |
| @@ -1517,6 +1598,8 @@ export default { | @@ -1517,6 +1598,8 @@ export default { | ||
| 1517 | prop: 'lastPersonTime', | 1598 | prop: 'lastPersonTime', |
| 1518 | width: 100, | 1599 | width: 100, |
| 1519 | align: 'right', | 1600 | align: 'right', |
| 1601 | + sortable: 'custom', | ||
| 1602 | + sortMethod: this.getNumberSortMethod('lastPersonTime'), | ||
| 1520 | formatter: (row) => { | 1603 | formatter: (row) => { |
| 1521 | return Number(row.lastPersonTime || 0).toLocaleString() | 1604 | return Number(row.lastPersonTime || 0).toLocaleString() |
| 1522 | } | 1605 | } |
| @@ -1526,6 +1609,8 @@ export default { | @@ -1526,6 +1609,8 @@ export default { | ||
| 1526 | prop: 'personTimeGrowthRate', | 1609 | prop: 'personTimeGrowthRate', |
| 1527 | width: 110, | 1610 | width: 110, |
| 1528 | align: 'right', | 1611 | align: 'right', |
| 1612 | + sortable: 'custom', | ||
| 1613 | + sortMethod: this.getGrowthRateStringSortMethod('personTimeGrowthRate'), | ||
| 1529 | formatter: (row) => { | 1614 | formatter: (row) => { |
| 1530 | const rate = row.personTimeGrowthRate || '0%' | 1615 | const rate = row.personTimeGrowthRate || '0%' |
| 1531 | const numRate = parseFloat(rate) || 0 | 1616 | const numRate = parseFloat(rate) || 0 |
| @@ -1538,6 +1623,8 @@ export default { | @@ -1538,6 +1623,8 @@ export default { | ||
| 1538 | prop: 'currentProjectCount', | 1623 | prop: 'currentProjectCount', |
| 1539 | width: 100, | 1624 | width: 100, |
| 1540 | align: 'right', | 1625 | align: 'right', |
| 1626 | + sortable: 'custom', | ||
| 1627 | + sortMethod: this.getNumberSortMethod('currentProjectCount'), | ||
| 1541 | formatter: (row) => { | 1628 | formatter: (row) => { |
| 1542 | return Number(row.currentProjectCount || 0).toLocaleString() | 1629 | return Number(row.currentProjectCount || 0).toLocaleString() |
| 1543 | } | 1630 | } |
| @@ -1547,6 +1634,8 @@ export default { | @@ -1547,6 +1634,8 @@ export default { | ||
| 1547 | prop: 'lastProjectCount', | 1634 | prop: 'lastProjectCount', |
| 1548 | width: 100, | 1635 | width: 100, |
| 1549 | align: 'right', | 1636 | align: 'right', |
| 1637 | + sortable: 'custom', | ||
| 1638 | + sortMethod: this.getNumberSortMethod('lastProjectCount'), | ||
| 1550 | formatter: (row) => { | 1639 | formatter: (row) => { |
| 1551 | return Number(row.lastProjectCount || 0).toLocaleString() | 1640 | return Number(row.lastProjectCount || 0).toLocaleString() |
| 1552 | } | 1641 | } |
| @@ -1556,6 +1645,8 @@ export default { | @@ -1556,6 +1645,8 @@ export default { | ||
| 1556 | prop: 'projectCountGrowthRate', | 1645 | prop: 'projectCountGrowthRate', |
| 1557 | width: 110, | 1646 | width: 110, |
| 1558 | align: 'right', | 1647 | align: 'right', |
| 1648 | + sortable: 'custom', | ||
| 1649 | + sortMethod: this.getGrowthRateStringSortMethod('projectCountGrowthRate'), | ||
| 1559 | formatter: (row) => { | 1650 | formatter: (row) => { |
| 1560 | const rate = row.projectCountGrowthRate || '0%' | 1651 | const rate = row.projectCountGrowthRate || '0%' |
| 1561 | const numRate = parseFloat(rate) || 0 | 1652 | const numRate = parseFloat(rate) || 0 |
netcore/src/Modularity/Extend/NCC.Extend/LqAnnualSummaryService.cs
| @@ -985,8 +985,17 @@ namespace NCC.Extend | @@ -985,8 +985,17 @@ namespace NCC.Extend | ||
| 985 | .ToListAsync(); | 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 | .OrderBy(x => x.BusinessUnitName) | 999 | .OrderBy(x => x.BusinessUnitName) |
| 991 | .ThenBy(x => x.StoreName) | 1000 | .ThenBy(x => x.StoreName) |
| 992 | .ToList(); | 1001 | .ToList(); |