Skip to content

Commit 3802fee

Browse files
committed
商城:增加商城首页
1 parent 532b237 commit 3802fee

File tree

15 files changed

+1038
-275
lines changed

15 files changed

+1038
-275
lines changed

src/api/mall/statistics/member.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ export interface MemberTerminalStatisticsRespVO {
5454
userCount: number
5555
}
5656

57+
/** 会员数量统计 Response VO */
58+
export interface MemberCountRespVO {
59+
/** 用户访问量 */
60+
visitUserCount: string
61+
/** 新增用户数量 */
62+
createUserCount: number
63+
}
64+
65+
/** 会员注册数量 Response VO */
66+
export interface MemberRegisterCountRespVO {
67+
date: string
68+
count: number
69+
}
70+
5771
// 查询会员统计
5872
export const getMemberSummary = () => {
5973
return request.get<MemberSummaryRespVO>({
@@ -89,3 +103,21 @@ export const getMemberTerminalStatisticsList = () => {
89103
url: '/statistics/member/get-terminal-statistics-list'
90104
})
91105
}
106+
107+
// 获得用户数量量对照
108+
export const getUserCountComparison = () => {
109+
return request.get<TradeStatisticsComparisonRespVO<MemberCountRespVO>>({
110+
url: '/statistics/member/user-count-comparison'
111+
})
112+
}
113+
114+
// 获得会员注册数量列表
115+
export const getMemberRegisterCountList = (
116+
beginTime: dayjs.ConfigType,
117+
endTime: dayjs.ConfigType
118+
) => {
119+
return request.get<MemberRegisterCountRespVO[]>({
120+
url: '/statistics/member/register-count-list',
121+
params: { times: [formatDate(beginTime), formatDate(endTime)] }
122+
})
123+
}

src/api/mall/statistics/pay.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import request from '@/config/axios'
2+
3+
/** 获取钱包充值金额 */
4+
export const getWalletRechargePrice = async () => {
5+
return await request.get<number>({ url: `/statistics/pay/wallet-recharge-price` })
6+
}

src/api/mall/statistics/trade.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,36 @@ export interface TradeTrendSummaryRespVO {
3333
orderRefundPrice: number
3434
}
3535

36+
/** 交易订单数量 Response VO */
37+
export interface TradeOrderCountRespVO {
38+
/** 待发货 */
39+
undelivered?: number
40+
/** 待核销 */
41+
pickUp?: number
42+
/** 退款中 */
43+
afterSaleApply?: number
44+
/** 提现待审核 */
45+
auditingWithdraw?: number
46+
}
47+
48+
/** 交易订单统计 Response VO */
49+
export interface TradeOrderSummaryRespVO {
50+
/** 支付订单商品数 */
51+
orderPayCount?: number
52+
/** 总支付金额,单位:分 */
53+
orderPayPrice?: number
54+
}
55+
56+
/** 订单量趋势统计 Response VO */
57+
export interface TradeOrderTrendRespVO {
58+
/** 日期 */
59+
date: string
60+
/** 订单数量 */
61+
orderPayCount: number
62+
/** 订单支付金额 */
63+
orderPayPrice: number
64+
}
65+
3666
// 查询交易统计
3767
export const getTradeStatisticsSummary = () => {
3868
return request.get<TradeStatisticsComparisonRespVO<TradeSummaryRespVO>>({
@@ -64,6 +94,30 @@ export const exportTradeTrend = (params: TradeTrendReqVO) => {
6494
})
6595
}
6696

97+
// 获得交易订单数量
98+
export const getOrderCount = async () => {
99+
return await request.get<TradeOrderCountRespVO>({ url: `/statistics/trade/order-count` })
100+
}
101+
102+
// 获得交易订单数量对照
103+
export const getOrderComparison = async () => {
104+
return await request.get<TradeStatisticsComparisonRespVO<TradeOrderSummaryRespVO>>({
105+
url: `/statistics/trade/order-comparison`
106+
})
107+
}
108+
109+
// 获得订单量趋势统计
110+
export const getOrderCountTrendComparison = (
111+
type: number,
112+
beginTime: dayjs.ConfigType,
113+
endTime: dayjs.ConfigType
114+
) => {
115+
return request.get<TradeStatisticsComparisonRespVO<TradeOrderTrendRespVO>[]>({
116+
url: '/statistics/trade/order-count-trend',
117+
params: { type, beginTime: formatDate(beginTime), endTime: formatDate(endTime) }
118+
})
119+
}
120+
67121
/** 时间参数需要格式化, 确保接口能识别 */
68122
const formatDateParam = (params: TradeTrendReqVO) => {
69123
return { times: [formatDate(params.times[0]), formatDate(params.times[1])] } as TradeTrendReqVO
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<template>
2+
<div class="flex flex-row items-center gap-2">
3+
<el-radio-group v-model="shortcutDays" @change="handleShortcutDaysChange">
4+
<el-radio-button :label="1">昨天</el-radio-button>
5+
<el-radio-button :label="7">最近7天</el-radio-button>
6+
<el-radio-button :label="30">最近30天</el-radio-button>
7+
</el-radio-group>
8+
<el-date-picker
9+
v-model="times"
10+
value-format="YYYY-MM-DD HH:mm:ss"
11+
type="daterange"
12+
start-placeholder="开始日期"
13+
end-placeholder="结束日期"
14+
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
15+
:shortcuts="shortcuts"
16+
class="!w-240px"
17+
@change="emitDateRangePicker"
18+
/>
19+
<slot></slot>
20+
</div>
21+
</template>
22+
<script lang="ts" setup>
23+
import dayjs from 'dayjs'
24+
import * as DateUtil from '@/utils/formatTime'
25+
26+
/** 快捷日期范围选择组件 */
27+
defineOptions({ name: 'ShortcutDateRangePicker' })
28+
29+
const shortcutDays = ref(7) // 日期快捷天数(单选按钮组), 默认7天
30+
const times = ref<[dayjs.ConfigType, dayjs.ConfigType]>(['', '']) // 时间范围参数
31+
defineExpose({ times }) // 暴露时间范围参数
32+
/** 日期快捷选择 */
33+
const shortcuts = [
34+
{
35+
text: '昨天',
36+
value: () => DateUtil.getDayRange(new Date(), -1)
37+
},
38+
{
39+
text: '最近7天',
40+
value: () => DateUtil.getLast7Days()
41+
},
42+
{
43+
text: '本月',
44+
value: () => [dayjs().startOf('M'), dayjs().subtract(1, 'd')]
45+
},
46+
{
47+
text: '最近30天',
48+
value: () => DateUtil.getLast30Days()
49+
},
50+
{
51+
text: '最近1年',
52+
value: () => DateUtil.getLast1Year()
53+
}
54+
]
55+
56+
/** 设置时间范围 */
57+
function setTimes() {
58+
const beginDate = dayjs().subtract(shortcutDays.value, 'd')
59+
const yesterday = dayjs().subtract(1, 'd')
60+
times.value = DateUtil.getDateRange(beginDate, yesterday)
61+
}
62+
63+
/** 快捷日期单选按钮选中 */
64+
const handleShortcutDaysChange = async () => {
65+
// 设置时间范围
66+
setTimes()
67+
// 发送时间范围选中事件
68+
await emitDateRangePicker()
69+
}
70+
71+
/** 触发事件:时间范围选中 */
72+
const emits = defineEmits<{
73+
(e: 'change', times: [dayjs.ConfigType, dayjs.ConfigType]): void
74+
}>()
75+
/** 触发时间范围选中事件 */
76+
const emitDateRangePicker = async () => {
77+
// 开始与截止在同一天的, 折线图出不来, 需要延长一天
78+
if (DateUtil.isSameDay(times.value[0], times.value[1])) {
79+
// 前天
80+
times.value[0] = DateUtil.formatDate(dayjs(times.value[0]).subtract(1, 'd'))
81+
}
82+
emits('change', times.value)
83+
}
84+
85+
/** 初始化 **/
86+
onMounted(() => {
87+
handleShortcutDaysChange()
88+
})
89+
</script>

src/utils/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,16 @@ export const yuanToFen = (amount: string | number): number => {
233233
export const fenToYuan = (price: string | number): number => {
234234
return formatToFraction(price)
235235
}
236+
237+
/**
238+
* 计算环比
239+
*
240+
* @param value 当前数值
241+
* @param reference 对比数值
242+
*/
243+
export const calculateRelativeRate = (value?: number, reference?: number) => {
244+
// 防止除0
245+
if (!reference) return 0
246+
247+
return ((100 * ((value || 0) - reference)) / reference).toFixed(0)
248+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<template>
2+
<div class="flex flex-col gap-2 bg-[var(--el-bg-color-overlay)] p-6">
3+
<div class="flex items-center justify-between text-gray-500">
4+
<span>{{ title }}</span>
5+
<el-tag>{{ tag }}</el-tag>
6+
</div>
7+
<div class="flex flex-row items-baseline justify-between">
8+
<CountTo :prefix="prefix" :end-val="value" :decimals="decimals" class="text-3xl" />
9+
<span :class="toNumber(percent) > 0 ? 'text-red-500' : 'text-green-500'">
10+
{{ Math.abs(toNumber(percent)) }}%
11+
<Icon :icon="toNumber(percent) > 0 ? 'ep:caret-top' : 'ep:caret-bottom'" class="!text-sm" />
12+
</span>
13+
</div>
14+
<el-divider class="mb-1! mt-2!" />
15+
<div class="flex flex-row items-center justify-between text-sm">
16+
<span class="text-gray-500">昨日数据</span>
17+
<span>{{ prefix || '' }}{{ reference }}</span>
18+
</div>
19+
</div>
20+
</template>
21+
<script lang="ts" setup>
22+
import { propTypes } from '@/utils/propTypes'
23+
import { toNumber } from 'lodash-es'
24+
import { calculateRelativeRate } from '@/utils'
25+
26+
/** 交易对照卡片 */
27+
defineOptions({ name: 'ComparisonCard' })
28+
29+
const props = defineProps({
30+
title: propTypes.string.def('').isRequired,
31+
tag: propTypes.string.def(''),
32+
prefix: propTypes.string.def(''),
33+
value: propTypes.number.def(0).isRequired,
34+
reference: propTypes.number.def(0).isRequired,
35+
decimals: propTypes.number.def(0)
36+
})
37+
38+
// 计算环比
39+
const percent = computed(() =>
40+
calculateRelativeRate(props.value as number, props.reference as number)
41+
)
42+
</script>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<template>
2+
<el-card shadow="never">
3+
<template #header>
4+
<CardTitle title="用户统计" />
5+
</template>
6+
<!-- 折线图 -->
7+
<Echart :height="300" :options="lineChartOptions" />
8+
</el-card>
9+
</template>
10+
<script lang="ts" setup>
11+
import dayjs from 'dayjs'
12+
import { EChartsOption } from 'echarts'
13+
import * as MemberStatisticsApi from '@/api/mall/statistics/member'
14+
import { formatDate } from '@/utils/formatTime'
15+
import { CardTitle } from '@/components/Card'
16+
17+
/** 会员用户统计卡片 */
18+
defineOptions({ name: 'MemberStatisticsCard' })
19+
20+
const loading = ref(true) // 加载中
21+
/** 折线图配置 */
22+
const lineChartOptions = reactive<EChartsOption>({
23+
dataset: {
24+
dimensions: ['date', 'count'],
25+
source: []
26+
},
27+
grid: {
28+
left: 20,
29+
right: 20,
30+
bottom: 20,
31+
top: 80,
32+
containLabel: true
33+
},
34+
legend: {
35+
top: 50
36+
},
37+
series: [{ name: '注册量', type: 'line', smooth: true, areaStyle: {} }],
38+
toolbox: {
39+
feature: {
40+
// 数据区域缩放
41+
dataZoom: {
42+
yAxisIndex: false // Y轴不缩放
43+
},
44+
brush: {
45+
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
46+
},
47+
saveAsImage: { show: true, name: '会员统计' } // 保存为图片
48+
}
49+
},
50+
tooltip: {
51+
trigger: 'axis',
52+
axisPointer: {
53+
type: 'cross'
54+
},
55+
padding: [5, 10]
56+
},
57+
xAxis: {
58+
type: 'category',
59+
boundaryGap: false,
60+
axisTick: {
61+
show: false
62+
},
63+
axisLabel: {
64+
formatter: (date: string) => formatDate(date, 'MM-DD')
65+
}
66+
},
67+
yAxis: {
68+
axisTick: {
69+
show: false
70+
}
71+
}
72+
}) as EChartsOption
73+
74+
const getMemberRegisterCountList = async () => {
75+
loading.value = true
76+
// 查询最近一月数据
77+
const beginTime = dayjs().subtract(30, 'd').startOf('d')
78+
const endTime = dayjs().endOf('d')
79+
const list = await MemberStatisticsApi.getMemberRegisterCountList(beginTime, endTime)
80+
// 更新 Echarts 数据
81+
if (lineChartOptions.dataset && lineChartOptions.dataset['source']) {
82+
lineChartOptions.dataset['source'] = list
83+
}
84+
loading.value = false
85+
}
86+
87+
/** 初始化 **/
88+
onMounted(() => {
89+
getMemberRegisterCountList()
90+
})
91+
</script>

0 commit comments

Comments
 (0)