mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-09-20 20:02:05 +08:00
feat(statistics): 添加购物车统计功能
- 新增购物车概况、加购与弃购日报、加购排行 TOP10 的 API 接口 - 更新路由配置以支持购物车统计页面 - 创建购物车统计页面组件,展示相关统计数据
This commit is contained in:
@@ -150,3 +150,18 @@ export const getGoodsSalesSummaryReport = (params) => {
|
|||||||
export const exportGoodsSalesSummaryReport = (params) => {
|
export const exportGoodsSalesSummaryReport = (params) => {
|
||||||
return getRequest("/statistics/report/goods-sales-summary/export", params, "blob");
|
return getRequest("/statistics/report/goods-sales-summary/export", params, "blob");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 购物车概况 */
|
||||||
|
export const getCartOverview = (params) => {
|
||||||
|
return getRequest("/statistics/cart/overview", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 加购与弃购日报 */
|
||||||
|
export const getCartDailyReport = (params) => {
|
||||||
|
return getRequest("/statistics/cart/daily", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 加购排行 TOP10 */
|
||||||
|
export const getCartAddRank = (params) => {
|
||||||
|
return getRequest("/statistics/cart/rank/add", params);
|
||||||
|
};
|
||||||
|
|||||||
@@ -455,6 +455,13 @@ export const otherRouter = {
|
|||||||
meta: { title: "商品销售汇总报表" },
|
meta: { title: "商品销售汇总报表" },
|
||||||
component: () => import("@/views/statistics/goods-sales-summary.vue")
|
component: () => import("@/views/statistics/goods-sales-summary.vue")
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "statistics/cart",
|
||||||
|
title: "购物车统计",
|
||||||
|
name: "cartStatistics",
|
||||||
|
meta: { title: "购物车统计" },
|
||||||
|
component: () => import("@/views/statistics/cart.vue")
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "procurement/purchase-order/detail",
|
path: "procurement/purchase-order/detail",
|
||||||
title: "采购单详情",
|
title: "采购单详情",
|
||||||
|
|||||||
271
manager/src/views/statistics/cart.vue
Normal file
271
manager/src/views/statistics/cart.vue
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wrapper">
|
||||||
|
<el-affix :offset="100">
|
||||||
|
<el-card class="card fixed-bottom">
|
||||||
|
<affixTime @selected="clickBreadcrumb" />
|
||||||
|
</el-card>
|
||||||
|
</el-affix>
|
||||||
|
|
||||||
|
<el-card class="card">
|
||||||
|
<h4>购物车概况</h4>
|
||||||
|
<div class="metric-grid">
|
||||||
|
<div class="metric-item" v-for="item in metricList" :key="item.key">
|
||||||
|
<div class="metric-label">
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
<el-tooltip v-if="item.tip" :content="item.tip" placement="top">
|
||||||
|
<el-icon class="metric-tip"><QuestionFilled /></el-icon>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="metric-value">
|
||||||
|
<template v-if="item.isRate">{{ formatRate(item.metric) }}</template>
|
||||||
|
<template v-else>{{ formatCount(item.metric) }}</template>
|
||||||
|
</div>
|
||||||
|
<div v-if="item.showRate !== false" class="metric-rate" :class="rateClass(item.metric)">
|
||||||
|
环比 {{ rateText(item.metric) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="card">
|
||||||
|
<h4>加购与弃购日报</h4>
|
||||||
|
<el-table :data="dailyList" stripe style="width: 100%">
|
||||||
|
<el-table-column label="日期" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatDay(row.createDate) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="addCount" label="加购次数" min-width="100" />
|
||||||
|
<el-table-column prop="addUserCount" label="加购人数" min-width="100" />
|
||||||
|
<el-table-column prop="checkoutCount" label="结算行数" min-width="100" />
|
||||||
|
<el-table-column prop="checkoutUserCount" label="结算人数" min-width="100" />
|
||||||
|
<el-table-column label="人数转化率" min-width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ convertText(row) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="abandonUserCount" label="弃购人数" min-width="100" />
|
||||||
|
<el-table-column prop="abandonItemCount" label="弃购行数" min-width="100" />
|
||||||
|
<el-table-column prop="inCartUserCount" label="在购人数" min-width="100" />
|
||||||
|
<el-table-column prop="inCartItemCount" label="在购行数" min-width="100" />
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="card">
|
||||||
|
<h4>加购排行 TOP10</h4>
|
||||||
|
<el-table :data="addRank" stripe style="width: 100%">
|
||||||
|
<el-table-column prop="rank" label="排名" width="70" />
|
||||||
|
<el-table-column prop="skuName" label="商品" min-width="220" 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>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as API_Statistics from "@/api/statistics";
|
||||||
|
import affixTime from "@/components/affix-time";
|
||||||
|
import { QuestionFilled } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { affixTime, QuestionFilled },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
params: {
|
||||||
|
searchType: "LAST_SEVEN",
|
||||||
|
year: "",
|
||||||
|
month: "",
|
||||||
|
storeId: "",
|
||||||
|
},
|
||||||
|
overview: {},
|
||||||
|
dailyList: [],
|
||||||
|
addRank: [],
|
||||||
|
metricDefs: [
|
||||||
|
{
|
||||||
|
key: "addCount",
|
||||||
|
label: "加购次数",
|
||||||
|
tip: "周期内 CART_ADD 次数,不含立即购买",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "addUserCount",
|
||||||
|
label: "加购人数",
|
||||||
|
tip: "周期内至少加购一次的会员数",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "checkoutUserCount",
|
||||||
|
label: "结算人数",
|
||||||
|
tip: "周期内从购物车提交订单的会员数",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "convertRate",
|
||||||
|
label: "加购转结算率",
|
||||||
|
isRate: true,
|
||||||
|
tip: "结算人数 / 加购人数",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "inCartUserCount",
|
||||||
|
label: "当前在购人数",
|
||||||
|
showRate: false,
|
||||||
|
tip: "此刻购物车里仍有商品的会员数",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "inCartItemCount",
|
||||||
|
label: "当前在购行数",
|
||||||
|
showRate: false,
|
||||||
|
tip: "此刻购物车行项目数",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "abandonUserCount",
|
||||||
|
label: "当前弃购人数",
|
||||||
|
showRate: false,
|
||||||
|
tip: "首次加入超过 24 小时仍在车的会员数",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "abandonItemCount",
|
||||||
|
label: "当前弃购行数",
|
||||||
|
showRate: false,
|
||||||
|
tip: "首次加入超过 24 小时仍在车的行数",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
metricList() {
|
||||||
|
return this.metricDefs.map((def) => ({
|
||||||
|
...def,
|
||||||
|
metric: this.overview[def.key] || {},
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
params: {
|
||||||
|
handler() {
|
||||||
|
this.loadData();
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clickBreadcrumb(item) {
|
||||||
|
this.params = {
|
||||||
|
...this.params,
|
||||||
|
searchType: item.searchType || "",
|
||||||
|
year: item.year || "",
|
||||||
|
month: item.month || "",
|
||||||
|
storeId: item.storeId || "",
|
||||||
|
startTime: item.startTime || "",
|
||||||
|
endTime: item.endTime || "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
metricCurrent(metric) {
|
||||||
|
if (!metric || metric.current == null) return 0;
|
||||||
|
return Number(metric.current) || 0;
|
||||||
|
},
|
||||||
|
formatCount(metric) {
|
||||||
|
return Math.round(this.metricCurrent(metric));
|
||||||
|
},
|
||||||
|
formatRate(metric) {
|
||||||
|
return `${this.metricCurrent(metric).toFixed(2)}%`;
|
||||||
|
},
|
||||||
|
rateText(metric) {
|
||||||
|
if (!metric || !metric.rate) return "0%";
|
||||||
|
return metric.rate;
|
||||||
|
},
|
||||||
|
rateClass(metric) {
|
||||||
|
const trend = metric && metric.trend;
|
||||||
|
if (trend === "UP") return "up";
|
||||||
|
if (trend === "DOWN") return "down";
|
||||||
|
return "flat";
|
||||||
|
},
|
||||||
|
formatDay(value) {
|
||||||
|
if (!value) return "-";
|
||||||
|
return String(value).substring(0, 10);
|
||||||
|
},
|
||||||
|
convertText(row) {
|
||||||
|
const addUser = Number(row.addUserCount) || 0;
|
||||||
|
const checkoutUser = Number(row.checkoutUserCount) || 0;
|
||||||
|
if (addUser <= 0) return "0%";
|
||||||
|
return `${((checkoutUser * 100) / addUser).toFixed(2)}%`;
|
||||||
|
},
|
||||||
|
async loadData() {
|
||||||
|
const query = { ...this.params };
|
||||||
|
const [overviewRes, dailyRes, rankRes] = await Promise.all([
|
||||||
|
API_Statistics.getCartOverview(query),
|
||||||
|
API_Statistics.getCartDailyReport(query),
|
||||||
|
API_Statistics.getCartAddRank(query),
|
||||||
|
]);
|
||||||
|
if (overviewRes && overviewRes.success) {
|
||||||
|
this.overview = overviewRes.result || {};
|
||||||
|
}
|
||||||
|
if (dailyRes && dailyRes.success) {
|
||||||
|
this.dailyList = dailyRes.result || [];
|
||||||
|
}
|
||||||
|
if (rankRes && rankRes.success) {
|
||||||
|
this.addRank = rankRes.result || [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.wrapper {
|
||||||
|
padding-bottom: 200px;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
}
|
||||||
|
.metric-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.metric-item {
|
||||||
|
background: #f3f5f7;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px 16px;
|
||||||
|
min-height: 110px;
|
||||||
|
}
|
||||||
|
.metric-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.metric-tip {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.metric-value {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $theme_color;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.metric-rate {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.metric-rate.up {
|
||||||
|
color: #f5222d;
|
||||||
|
}
|
||||||
|
.metric-rate.down {
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
.metric-rate.flat {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.metric-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user