|
@@ -11,35 +11,35 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <!-- ① 运营统计带(注:聚合口径为当前页合计;家庭总收益无现成字段,标记为待后端接口) -->
|
|
|
|
|
|
|
+ <!-- ① 运营统计带:优先取 /api/admin/families/stats 全局聚合;接口不可用/失败时降级为当前页合计(stats) -->
|
|
|
<div class="stat-band">
|
|
<div class="stat-band">
|
|
|
<div class="stat-card stat-card--primary">
|
|
<div class="stat-card stat-card--primary">
|
|
|
<div class="stat-label">家庭总数</div>
|
|
<div class="stat-label">家庭总数</div>
|
|
|
- <div class="stat-value stat-value--primary adm-num">{{ stats.total }}</div>
|
|
|
|
|
|
|
+ <div class="stat-value stat-value--primary adm-num">{{ displayStats.total }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="stat-card">
|
|
<div class="stat-card">
|
|
|
- <div class="stat-label">成员总数(本页合计)</div>
|
|
|
|
|
- <div class="stat-value adm-num">{{ stats.members }}</div>
|
|
|
|
|
|
|
+ <div class="stat-label">成员总数<span v-if="statsDegraded">(本页合计)</span></div>
|
|
|
|
|
+ <div class="stat-value adm-num">{{ displayStats.members }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="stat-card">
|
|
<div class="stat-card">
|
|
|
- <div class="stat-label">报告数(本页合计)</div>
|
|
|
|
|
- <div class="stat-value adm-num">{{ stats.reports }}</div>
|
|
|
|
|
|
|
+ <div class="stat-label">报告数<span v-if="statsDegraded">(本页合计)</span></div>
|
|
|
|
|
+ <div class="stat-value adm-num">{{ displayStats.reports }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="stat-card">
|
|
<div class="stat-card">
|
|
|
- <div class="stat-label">方案数(本页合计)</div>
|
|
|
|
|
- <div class="stat-value adm-num">{{ stats.plans }}</div>
|
|
|
|
|
|
|
+ <div class="stat-label">方案数<span v-if="statsDegraded">(本页合计)</span></div>
|
|
|
|
|
+ <div class="stat-value adm-num">{{ displayStats.plans }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="stat-card">
|
|
<div class="stat-card">
|
|
|
- <div class="stat-label">任务数(本页合计)</div>
|
|
|
|
|
- <div class="stat-value adm-num">{{ stats.tasks }}</div>
|
|
|
|
|
|
|
+ <div class="stat-label">任务数<span v-if="statsDegraded">(本页合计)</span></div>
|
|
|
|
|
+ <div class="stat-value adm-num">{{ displayStats.tasks }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="stat-card">
|
|
<div class="stat-card">
|
|
|
- <div class="stat-label">优惠券数(本页合计)</div>
|
|
|
|
|
- <div class="stat-value adm-num">{{ stats.coupons }}</div>
|
|
|
|
|
|
|
+ <div class="stat-label">优惠券数<span v-if="statsDegraded">(本页合计)</span></div>
|
|
|
|
|
+ <div class="stat-value adm-num">{{ displayStats.coupons }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="stat-card">
|
|
<div class="stat-card">
|
|
|
<div class="stat-label">家庭总收益</div>
|
|
<div class="stat-label">家庭总收益</div>
|
|
|
- <div class="stat-value adm-num" title="需后端新增聚合接口">—</div>
|
|
|
|
|
|
|
+ <div class="stat-value adm-num" :title="displayStats.earningsAmount == null ? '待收益台账接入:家庭总收益口径未确认,暂不展示数值' : ''">{{ displayStats.earningsAmount == null ? '—' : displayStats.earningsAmount }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -392,7 +392,7 @@
|
|
|
<script>
|
|
<script>
|
|
|
import SortMixin from '@/mixins/SortMixin'
|
|
import SortMixin from '@/mixins/SortMixin'
|
|
|
import ListStateMixin from '@/mixins/ListStateMixin'
|
|
import ListStateMixin from '@/mixins/ListStateMixin'
|
|
|
-import { getFamilyList, getFamilyMembers, getFamilyOverview, getFamilyTasks, updateFamily, deleteFamily, setMemberAdminRole } from '@/api/admin'
|
|
|
|
|
|
|
+import { getFamilyList, getFamilyMembers, getFamilyOverview, getFamilyTasks, getFamilyStats, updateFamily, deleteFamily, setMemberAdminRole } from '@/api/admin'
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
name: 'Families',
|
|
name: 'Families',
|
|
@@ -408,7 +408,7 @@ export default {
|
|
|
currentPath() {
|
|
currentPath() {
|
|
|
return this.$route.path
|
|
return this.$route.path
|
|
|
},
|
|
},
|
|
|
- // 统计带:基于当前页 tableData 合计(真实可读,按"本页"口径标注)
|
|
|
|
|
|
|
+ // 统计带兜底:基于当前页 tableData 合计(全局聚合接口不可用时降级使用,勿删)
|
|
|
stats() {
|
|
stats() {
|
|
|
const sum = (key) => this.tableData.reduce((acc, r) => acc + (r[key] || 0), 0)
|
|
const sum = (key) => this.tableData.reduce((acc, r) => acc + (r[key] || 0), 0)
|
|
|
return {
|
|
return {
|
|
@@ -419,6 +419,26 @@ export default {
|
|
|
tasks: sum('taskCount'),
|
|
tasks: sum('taskCount'),
|
|
|
coupons: sum('couponCount')
|
|
coupons: sum('couponCount')
|
|
|
}
|
|
}
|
|
|
|
|
+ },
|
|
|
|
|
+ // 降级态:全局聚合接口尚未返回或失败时,统计带标注「(本页合计)」
|
|
|
|
|
+ statsDegraded() {
|
|
|
|
|
+ return this.globalStats == null
|
|
|
|
|
+ },
|
|
|
|
|
+ // 统计带展示值:优先取全局聚合接口(globalStats);字段缺失或降级时逐项回退到本页合计(stats)
|
|
|
|
|
+ displayStats() {
|
|
|
|
|
+ const g = this.globalStats
|
|
|
|
|
+ const fallback = this.stats
|
|
|
|
|
+ const pick = (key, fbKey) =>
|
|
|
|
|
+ (g && g[key] !== null && g[key] !== undefined) ? g[key] : fallback[fbKey]
|
|
|
|
|
+ return {
|
|
|
|
|
+ total: pick('familyCount', 'total'),
|
|
|
|
|
+ members: pick('memberCount', 'members'),
|
|
|
|
|
+ reports: pick('reportCount', 'reports'),
|
|
|
|
|
+ plans: pick('planCount', 'plans'),
|
|
|
|
|
+ tasks: pick('taskCount', 'tasks'),
|
|
|
|
|
+ coupons: pick('couponCount', 'coupons'),
|
|
|
|
|
+ earningsAmount: (g && g.earningsAmount !== null && g.earningsAmount !== undefined) ? g.earningsAmount : null
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
data() {
|
|
data() {
|
|
@@ -426,6 +446,8 @@ export default {
|
|
|
loading: false,
|
|
loading: false,
|
|
|
submitting: false,
|
|
submitting: false,
|
|
|
tableData: [],
|
|
tableData: [],
|
|
|
|
|
+ // 全局聚合统计(POST /api/admin/families/stats);null 表示未加载或失败,统计带降级为本页合计
|
|
|
|
|
+ globalStats: null,
|
|
|
sortableColumns: { id: 'ID', name: '家庭名称', inviteCode: '邀请码', createdAt: '创建时间' },
|
|
sortableColumns: { id: 'ID', name: '家庭名称', inviteCode: '邀请码', createdAt: '创建时间' },
|
|
|
searchForm: {
|
|
searchForm: {
|
|
|
name: '',
|
|
name: '',
|
|
@@ -525,11 +547,22 @@ export default {
|
|
|
this.handleViewMembersById(parseInt(familyId))
|
|
this.handleViewMembersById(parseInt(familyId))
|
|
|
}
|
|
}
|
|
|
this.loadFamilies()
|
|
this.loadFamilies()
|
|
|
|
|
+ this.loadGlobalStats() // 独立异步加载,不阻塞列表
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
loadList() {
|
|
loadList() {
|
|
|
this.loadFamilies()
|
|
this.loadFamilies()
|
|
|
},
|
|
},
|
|
|
|
|
+ // 全局聚合统计:独立异步加载;失败或返回空时保持 globalStats=null,统计带自动降级
|
|
|
|
|
+ async loadGlobalStats() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getFamilyStats()
|
|
|
|
|
+ const data = res && res.data
|
|
|
|
|
+ this.globalStats = (data && typeof data === 'object') ? data : null
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ this.globalStats = null
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
onSelectionChange(val) {
|
|
onSelectionChange(val) {
|
|
|
this.selectedRows = val
|
|
this.selectedRows = val
|
|
|
},
|
|
},
|
|
@@ -614,6 +647,7 @@ export default {
|
|
|
},
|
|
},
|
|
|
handleRefresh() {
|
|
handleRefresh() {
|
|
|
this.loadFamilies()
|
|
this.loadFamilies()
|
|
|
|
|
+ this.loadGlobalStats()
|
|
|
},
|
|
},
|
|
|
handleSizeChange(val) {
|
|
handleSizeChange(val) {
|
|
|
this.pagination.size = val
|
|
this.pagination.size = val
|