feat(statistics): 增强购物车统计功能,新增店铺与商品加购排行及弃购明细导出

- 更新加购排行接口,新增店铺与商品加购排行 API
- 添加当前弃购明细分页与导出功能
- 更新购物车统计页面,展示新增统计数据
This commit is contained in:
pikachu1995@126.com
2026-08-18 14:47:24 +08:00
parent 1c8fccae66
commit 457eda829d
2 changed files with 140 additions and 3 deletions

View File

@@ -161,7 +161,27 @@ export const getCartDailyReport = (params) => {
return getRequest("/statistics/cart/daily", params); return getRequest("/statistics/cart/daily", params);
}; };
/** 加购排行 TOP10 */ /** SKU 加购排行 TOP10 */
export const getCartAddRank = (params) => { export const getCartAddRank = (params) => {
return getRequest("/statistics/cart/rank/add", params); return getRequest("/statistics/cart/rank/add", params);
}; };
/** 店铺加购排行 TOP10 */
export const getCartStoreRank = (params) => {
return getRequest("/statistics/cart/rank/store", params);
};
/** 商品加购排行 TOP10 */
export const getCartGoodsRank = (params) => {
return getRequest("/statistics/cart/rank/goods", params);
};
/** 当前弃购明细分页 */
export const getCartAbandonPage = (params) => {
return getRequest("/statistics/cart/abandon", params);
};
/** 导出当前弃购明细 */
export const exportCartAbandon = (params) => {
return getRequest("/statistics/cart/abandon/export", params, "blob");
};

View File

@@ -52,20 +52,80 @@
</el-card> </el-card>
<el-card class="card"> <el-card class="card">
<h4>加购排行 TOP10</h4> <h4>SKU 加购排行 TOP10</h4>
<el-table :data="addRank" stripe style="width: 100%"> <el-table :data="addRank" stripe style="width: 100%">
<el-table-column prop="rank" label="排名" width="70" /> <el-table-column prop="rank" label="排名" width="70" />
<el-table-column prop="skuName" label="商品" min-width="220" show-overflow-tooltip /> <el-table-column prop="skuName" label="商品" min-width="220" show-overflow-tooltip />
<el-table-column prop="storeName" label="店铺" min-width="140" show-overflow-tooltip />
<el-table-column prop="addCount" label="加购次数" min-width="110" /> <el-table-column prop="addCount" label="加购次数" min-width="110" />
<el-table-column prop="addUserCount" label="加购人数" min-width="110" /> <el-table-column prop="addUserCount" label="加购人数" min-width="110" />
</el-table> </el-table>
</el-card> </el-card>
<div class="rank-grid">
<el-card class="card">
<h4>店铺加购排行 TOP10</h4>
<el-table :data="storeRank" stripe style="width: 100%">
<el-table-column prop="rank" label="排名" width="70" />
<el-table-column prop="storeName" label="店铺" min-width="160" show-overflow-tooltip />
<el-table-column prop="addCount" label="加购次数" min-width="110" />
<el-table-column prop="addUserCount" label="加购人数" min-width="110" />
</el-table>
</el-card>
<el-card class="card">
<h4>商品加购排行 TOP10</h4>
<el-table :data="goodsRank" stripe style="width: 100%">
<el-table-column prop="rank" label="排名" width="70" />
<el-table-column prop="goodsName" label="商品" min-width="180" show-overflow-tooltip />
<el-table-column prop="storeName" label="店铺" min-width="140" show-overflow-tooltip />
<el-table-column prop="addCount" label="加购次数" min-width="100" />
<el-table-column prop="addUserCount" label="加购人数" min-width="100" />
</el-table>
</el-card>
</div>
<el-card class="card">
<div class="card-header">
<div class="card-title">
<h4>当前弃购明细</h4>
<el-tooltip content="首次加入超过 24 小时仍在购物车的行;与上方日期无关,仅按店铺过滤" placement="top">
<el-icon class="metric-tip"><QuestionFilled /></el-icon>
</el-tooltip>
</div>
<el-button @click="handleExportAbandon">导出</el-button>
</div>
<el-table :data="abandonList" stripe style="width: 100%">
<el-table-column prop="memberName" label="用户名" min-width="120" show-overflow-tooltip />
<el-table-column prop="nickName" label="昵称" min-width="120" show-overflow-tooltip />
<el-table-column prop="storeName" label="店铺" min-width="140" show-overflow-tooltip />
<el-table-column prop="skuName" label="商品" min-width="180" show-overflow-tooltip />
<el-table-column prop="num" label="数量" width="80" />
<el-table-column label="是否勾选" width="90">
<template #default="{ row }">
{{ row.checked ? "是" : "否" }}
</template>
</el-table-column>
<el-table-column prop="firstAddTime" label="首次加入时间" min-width="170" />
<el-table-column prop="stayHours" label="停留小时" width="100" />
</el-table>
<el-pagination
v-model:current-page="abandonParams.pageNumber"
v-model:page-size="abandonParams.pageSize"
class="mt_10"
:total="abandonTotal"
:page-sizes="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadAbandon"
@size-change="loadAbandon"
/>
</el-card>
</div> </div>
</template> </template>
<script> <script>
import * as API_Statistics from "@/api/statistics"; import * as API_Statistics from "@/api/statistics";
import affixTime from "@/components/affix-time"; import affixTime from "@/components/affix-time";
import { downloadBlob } from "@/utils/downloadBlob";
import { QuestionFilled } from "@element-plus/icons-vue"; import { QuestionFilled } from "@element-plus/icons-vue";
export default { export default {
@@ -81,6 +141,14 @@ export default {
overview: {}, overview: {},
dailyList: [], dailyList: [],
addRank: [], addRank: [],
storeRank: [],
goodsRank: [],
abandonList: [],
abandonTotal: 0,
abandonParams: {
pageNumber: 1,
pageSize: 20,
},
metricDefs: [ metricDefs: [
{ {
key: "addCount", key: "addCount",
@@ -191,10 +259,12 @@ export default {
}, },
async loadData() { async loadData() {
const query = { ...this.params }; const query = { ...this.params };
const [overviewRes, dailyRes, rankRes] = await Promise.all([ const [overviewRes, dailyRes, rankRes, storeRes, goodsRes] = await Promise.all([
API_Statistics.getCartOverview(query), API_Statistics.getCartOverview(query),
API_Statistics.getCartDailyReport(query), API_Statistics.getCartDailyReport(query),
API_Statistics.getCartAddRank(query), API_Statistics.getCartAddRank(query),
API_Statistics.getCartStoreRank(query),
API_Statistics.getCartGoodsRank(query),
]); ]);
if (overviewRes && overviewRes.success) { if (overviewRes && overviewRes.success) {
this.overview = overviewRes.result || {}; this.overview = overviewRes.result || {};
@@ -205,6 +275,31 @@ export default {
if (rankRes && rankRes.success) { if (rankRes && rankRes.success) {
this.addRank = rankRes.result || []; this.addRank = rankRes.result || [];
} }
if (storeRes && storeRes.success) {
this.storeRank = storeRes.result || [];
}
if (goodsRes && goodsRes.success) {
this.goodsRank = goodsRes.result || [];
}
this.abandonParams.pageNumber = 1;
this.loadAbandon();
},
async loadAbandon() {
const query = {
...this.params,
pageNumber: this.abandonParams.pageNumber,
pageSize: this.abandonParams.pageSize,
};
const res = await API_Statistics.getCartAbandonPage(query);
if (res && res.success) {
this.abandonList = (res.result && res.result.records) || [];
this.abandonTotal = (res.result && res.result.total) || 0;
}
},
handleExportAbandon() {
API_Statistics.exportCartAbandon({ ...this.params }).then((blob) => {
downloadBlob(blob, "购物车弃购明细.xlsx");
});
}, },
}, },
}; };
@@ -220,6 +315,25 @@ export default {
h4 { h4 {
margin: 0 0 20px 0; margin: 0 0 20px 0;
} }
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.card-header h4 {
margin: 0;
}
.card-title {
display: flex;
align-items: center;
gap: 4px;
}
.rank-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.metric-grid { .metric-grid {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
@@ -267,5 +381,8 @@ h4 {
.metric-grid { .metric-grid {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
} }
.rank-grid {
grid-template-columns: 1fr;
}
} }
</style> </style>