mirror of
https://gitee.com/hhyykk/ipms-sjy-ui.git
synced 2025-07-17 04:15:07 +08:00
统计:增加商品统计
This commit is contained in:
104
src/views/mall/statistics/product/components/ProductRank.vue
Normal file
104
src/views/mall/statistics/product/components/ProductRank.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<!-- 标题 -->
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<CardTitle title="商品排行" />
|
||||
<!-- 查询条件 -->
|
||||
<ShortcutDateRangePicker ref="shortcutDateRangePicker" @change="handleDateRangeChange" />
|
||||
</div>
|
||||
</template>
|
||||
<!-- 排行列表 -->
|
||||
<el-table v-loading="loading" :data="list" @sort-change="handleSortChange">
|
||||
<el-table-column label="商品ID" prop="spuId" min-width="70" />
|
||||
<el-table-column label="商品图片" align="center" prop="picUrl" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-image
|
||||
:src="row.picUrl"
|
||||
:preview-src-list="[row.picUrl]"
|
||||
class="h-30px w-30px"
|
||||
preview-teleported
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商品名称" prop="name" min-width="200" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="浏览量" prop="browseCount" min-width="90" sortable="custom" />
|
||||
<el-table-column label="访客数" prop="browseUserCount" min-width="90" sortable="custom" />
|
||||
<el-table-column label="加购件数" prop="cartCount" min-width="105" sortable="custom" />
|
||||
<el-table-column label="下单件数" prop="orderCount" min-width="105" sortable="custom" />
|
||||
<el-table-column label="支付件数" prop="orderPayCount" min-width="105" sortable="custom" />
|
||||
<el-table-column label="支付金额" prop="orderPayPrice" min-width="105" sortable="custom" />
|
||||
<el-table-column label="收藏数" prop="favoriteCount" min-width="90" sortable="custom" />
|
||||
<el-table-column
|
||||
label="访客-支付转化率(%)"
|
||||
prop="browseConvertPercent"
|
||||
min-width="180"
|
||||
sortable="custom"
|
||||
:formatter="formatConvertRate"
|
||||
/>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getSpuList"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ProductStatisticsApi, ProductStatisticsVO } from '@/api/mall/statistics/product'
|
||||
import { CardTitle } from '@/components/Card'
|
||||
import { buildSortingField } from '@/utils'
|
||||
|
||||
/** 商品排行 */
|
||||
defineOptions({ name: 'ProductRank' })
|
||||
|
||||
// 格式化:访客-支付转化率
|
||||
const formatConvertRate = (row: ProductStatisticsVO) => {
|
||||
return `${row.browseConvertPercent}%`
|
||||
}
|
||||
|
||||
const handleSortChange = (params: any) => {
|
||||
queryParams.sortingFields = [buildSortingField(params)]
|
||||
getSpuList()
|
||||
}
|
||||
|
||||
const handleDateRangeChange = (times: any[]) => {
|
||||
queryParams.times = times as []
|
||||
getSpuList()
|
||||
}
|
||||
|
||||
const shortcutDateRangePicker = ref()
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
times: [],
|
||||
sortingFields: {}
|
||||
})
|
||||
// 列表的加载中
|
||||
const loading = ref(false)
|
||||
// 列表的总页数
|
||||
const total = ref(0)
|
||||
// 列表的数据
|
||||
const list = ref<ProductStatisticsVO[]>([])
|
||||
|
||||
/** 查询商品列表 */
|
||||
const getSpuList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ProductStatisticsApi.getProductStatisticsRankPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
await getSpuList()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
304
src/views/mall/statistics/product/components/ProductSummary.vue
Normal file
304
src/views/mall/statistics/product/components/ProductSummary.vue
Normal file
@ -0,0 +1,304 @@
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<!-- 标题 -->
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<CardTitle title="商品概况" />
|
||||
<!-- 查询条件 -->
|
||||
<ShortcutDateRangePicker ref="shortcutDateRangePicker" @change="getProductTrendData">
|
||||
<el-button
|
||||
class="ml-4"
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
v-hasPermi="['statistics:product:export']"
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-1" />导出
|
||||
</el-button>
|
||||
</ShortcutDateRangePicker>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 统计值 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :xl="4" :md="8" :sm="24">
|
||||
<SummaryCard
|
||||
title="商品浏览量"
|
||||
tooltip="在选定条件下,所有商品详情页被访问的次数,一个人在统计时间内访问多次记为多次"
|
||||
icon="ep:view"
|
||||
icon-color="bg-blue-100"
|
||||
icon-bg-color="text-blue-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.browseCount || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.browseCount,
|
||||
trendSummary?.reference?.browseCount
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xl="4" :md="8" :sm="24">
|
||||
<SummaryCard
|
||||
title="商品访客数"
|
||||
tooltip="在选定条件下,访问任何商品详情页的人数,一个人在统计时间范围内访问多次只记为一个"
|
||||
icon="ep:user-filled"
|
||||
icon-color="bg-purple-100"
|
||||
icon-bg-color="text-purple-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.browseUserCount || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.browseUserCount,
|
||||
trendSummary?.reference?.browseUserCount
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xl="4" :md="8" :sm="24">
|
||||
<SummaryCard
|
||||
title="支付件数"
|
||||
tooltip="在选定条件下,成功付款订单的商品件数之和"
|
||||
icon="fa-solid:money-check-alt"
|
||||
icon-color="bg-yellow-100"
|
||||
icon-bg-color="text-yellow-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.orderPayCount || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.orderPayCount,
|
||||
trendSummary?.reference?.orderPayCount
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xl="4" :md="8" :sm="24">
|
||||
<SummaryCard
|
||||
title="支付金额"
|
||||
tooltip="在选定条件下,成功付款订单的商品金额之和"
|
||||
icon="ep:warning-filled"
|
||||
icon-color="bg-green-100"
|
||||
icon-bg-color="text-green-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.orderPayPrice || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.orderPayPrice,
|
||||
trendSummary?.reference?.orderPayPrice
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xl="4" :md="8" :sm="24">
|
||||
<SummaryCard
|
||||
title="退款件数"
|
||||
tooltip="在选定条件下,成功退款的商品件数之和"
|
||||
icon="fa-solid:wallet"
|
||||
icon-color="bg-cyan-100"
|
||||
icon-bg-color="text-cyan-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.afterSaleCount || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.afterSaleCount,
|
||||
trendSummary?.reference?.afterSaleCount
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :xl="4" :md="8" :sm="24">
|
||||
<SummaryCard
|
||||
title="退款金额"
|
||||
tooltip="在选定条件下,成功退款的商品金额之和"
|
||||
icon="fa-solid:award"
|
||||
icon-color="bg-yellow-100"
|
||||
icon-bg-color="text-yellow-500"
|
||||
prefix="¥"
|
||||
:decimals="2"
|
||||
:value="fenToYuan(trendSummary?.value?.afterSaleRefundPrice || 0)"
|
||||
:percent="
|
||||
calculateRelativeRate(
|
||||
trendSummary?.value?.afterSaleRefundPrice,
|
||||
trendSummary?.reference?.afterSaleRefundPrice
|
||||
)
|
||||
"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 折线图 -->
|
||||
<el-skeleton :loading="trendLoading" animated>
|
||||
<Echart :height="500" :options="lineChartOptions" />
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ProductStatisticsApi, ProductStatisticsVO } from '@/api/mall/statistics/product'
|
||||
import SummaryCard from '@/components/SummaryCard/index.vue'
|
||||
import { EChartsOption } from 'echarts'
|
||||
import { DataComparisonRespVO } from '@/api/mall/statistics/common'
|
||||
import { calculateRelativeRate, fenToYuan } from '@/utils'
|
||||
import download from '@/utils/download'
|
||||
import { CardTitle } from '@/components/Card'
|
||||
import * as DateUtil from '@/utils/formatTime'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
/** 商品概况 */
|
||||
defineOptions({ name: 'ProductSummary' })
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const trendLoading = ref(true) // 商品状态加载中
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const trendSummary = ref<DataComparisonRespVO<ProductStatisticsVO>>() // 商品状况统计数据
|
||||
const shortcutDateRangePicker = ref()
|
||||
|
||||
/** 折线图配置 */
|
||||
const lineChartOptions = reactive<EChartsOption>({
|
||||
dataset: {
|
||||
dimensions: ['time', 'browseCount', 'browseUserCount', 'orderPayPrice', 'afterSaleRefundPrice'],
|
||||
source: []
|
||||
},
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
top: 80,
|
||||
containLabel: true
|
||||
},
|
||||
legend: {
|
||||
top: 50
|
||||
},
|
||||
series: [
|
||||
{ name: '商品浏览量', type: 'line', smooth: true, itemStyle: { color: '#B37FEB' } },
|
||||
{ name: '商品访客数', type: 'line', smooth: true, itemStyle: { color: '#FFAB2B' } },
|
||||
{ name: '支付金额', type: 'bar', smooth: true, yAxisIndex: 1, itemStyle: { color: '#1890FF' } },
|
||||
{ name: '退款金额', type: 'bar', smooth: true, yAxisIndex: 1, itemStyle: { color: '#00C050' } }
|
||||
],
|
||||
toolbox: {
|
||||
feature: {
|
||||
// 数据区域缩放
|
||||
dataZoom: {
|
||||
yAxisIndex: false // Y轴不缩放
|
||||
},
|
||||
brush: {
|
||||
type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮
|
||||
},
|
||||
saveAsImage: { show: true, name: '商品状况' } // 保存为图片
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
},
|
||||
padding: [5, 10]
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '金额',
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#7F8B9C'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#F5F7F9'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '数量',
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#7F8B9C'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#F5F7F9'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}) as EChartsOption
|
||||
|
||||
/** 处理商品状况查询 */
|
||||
const getProductTrendData = async () => {
|
||||
trendLoading.value = true
|
||||
// 1. 处理时间: 开始与截止在同一天的, 折线图出不来, 需要延长一天
|
||||
const times = shortcutDateRangePicker.value.times
|
||||
if (DateUtil.isSameDay(times[0], times[1])) {
|
||||
// 前天
|
||||
times[0] = DateUtil.formatDate(dayjs(times[0]).subtract(1, 'd'))
|
||||
}
|
||||
// 查询数据
|
||||
await Promise.all([getProductTrendSummary(), getProductStatisticsList()])
|
||||
trendLoading.value = false
|
||||
}
|
||||
|
||||
/** 查询商品状况数据统计 */
|
||||
const getProductTrendSummary = async () => {
|
||||
const times = shortcutDateRangePicker.value.times
|
||||
trendSummary.value = await ProductStatisticsApi.getProductStatisticsAnalyse({ times })
|
||||
}
|
||||
|
||||
/** 查询商品状况数据列表 */
|
||||
const getProductStatisticsList = async () => {
|
||||
// 查询数据
|
||||
const times = shortcutDateRangePicker.value.times
|
||||
const list: ProductStatisticsVO[] = await ProductStatisticsApi.getProductStatisticsList({ times })
|
||||
// 处理数据
|
||||
for (let item of list) {
|
||||
item.orderPayPrice = fenToYuan(item.orderPayPrice)
|
||||
item.afterSaleRefundPrice = fenToYuan(item.afterSaleRefundPrice)
|
||||
}
|
||||
// 更新 Echarts 数据
|
||||
if (lineChartOptions.dataset && lineChartOptions.dataset['source']) {
|
||||
lineChartOptions.dataset['source'] = list
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const times = shortcutDateRangePicker.value.times
|
||||
const data = await ProductStatisticsApi.exportProductStatisticsExcel({ times })
|
||||
download.excel(data, '商品状况.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
14
src/views/mall/statistics/product/index.vue
Normal file
14
src/views/mall/statistics/product/index.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<!-- 商品概览 -->
|
||||
<ProductSummary />
|
||||
<!-- 商品排行 -->
|
||||
<ProductRank class="mt-16px" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import ProductSummary from './components/ProductSummary.vue'
|
||||
import ProductRank from './components/ProductRank.vue'
|
||||
|
||||
/** 商品统计 */
|
||||
defineOptions({ name: 'ProductStatistics' })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
@ -298,7 +298,7 @@ const getTradeTrendData = async () => {
|
||||
times[0] = DateUtil.formatDate(dayjs(times[0]).subtract(1, 'd'))
|
||||
}
|
||||
// 查询数据
|
||||
await Promise.all([getTradeTrendSummary(), getTradeStatisticsList()])
|
||||
await Promise.all([getTradeStatisticsAnalyse(), getTradeStatisticsList()])
|
||||
trendLoading.value = false
|
||||
}
|
||||
|
||||
@ -308,9 +308,9 @@ const getTradeStatisticsSummary = async () => {
|
||||
}
|
||||
|
||||
/** 查询交易状况数据统计 */
|
||||
const getTradeTrendSummary = async () => {
|
||||
const getTradeStatisticsAnalyse = async () => {
|
||||
const times = shortcutDateRangePicker.value.times
|
||||
trendSummary.value = await TradeStatisticsApi.getTradeTrendSummary({ times })
|
||||
trendSummary.value = await TradeStatisticsApi.getTradeStatisticsAnalyse({ times })
|
||||
}
|
||||
|
||||
/** 查询交易状况数据列表 */
|
||||
|
Reference in New Issue
Block a user