增加会员统计界面

This commit is contained in:
lxd 2025-06-18 17:41:12 +08:00
parent 8057eda493
commit 7cd19fa4f2
3 changed files with 158 additions and 4 deletions

View File

@ -73,11 +73,17 @@ export const PersonApi = {
return await request.get({ url: `/system/person/page-no-familyid`, params })
},
// 更新用户家庭信息
updateFamilyInfo: async (id: number, familyrelation: number) => {
return await request.put({
url: `/system/person/update-family-info`,
data: { id, familyrelation }
})
},
// 查询会员开通数量
getMemberRegisterCount: async (startDate: string, endDate: string) => {
return await request.get({ url: `/system/person/get-member-register-count?startDate=` + startDate + `&endDate=` + endDate })
}
}

View File

@ -30,16 +30,16 @@
<!-- 下方地图+柱状图 -->
<el-row :gutter="16">
<el-col :span="16">
<el-card shadow="never" class="modern-card" style="height: 700px">
<el-col :span="15">
<el-card shadow="never" class="modern-card" style="height: 650px">
<template #header>
<span class="modern-card-title">设备分布地图</span>
</template>
<Echart :options="mapOptions" :height="580" />
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="never" class="modern-card" style="height: 700px;">
<el-col :span="9">
<el-card shadow="never" class="modern-card" style="height: 650px;">
<template #header>
<span class="modern-card-title">会员增长趋势</span>
</template>

View File

@ -0,0 +1,148 @@
<template>
<ContentWrap>
<!-- 搜索工具栏卡片 -->
<el-card class="member-stat-card search-card mb-4" shadow="hover">
<el-form :inline="true" @submit.prevent>
<el-form-item label="时间范围">
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD HH:mm:ss"
@change="onDateChange"
style="width: 300px;"
/>
</el-form-item>
<el-form-item label="图表类型">
<el-button :type="chartType === 'line' ? 'primary' : 'default'" @click="chartType = 'line'">
折线图
</el-button>
<el-button :type="chartType === 'bar' ? 'primary' : 'default'" @click="chartType = 'bar'">
柱状图
</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 统计图表卡片下方主内容区域 -->
<el-card class="member-stat-card stat-card" shadow="hover">
<div id="main" style="width:100%;height:580px;"></div>
</el-card>
</ContentWrap>
</template>
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import * as echarts from 'echarts'
import ShortcutDateRangePicker from '@/components/ShortcutDateRangePicker/index.vue'
import ContentWrap from '@/components/ContentWrap/src/ContentWrap.vue'
import { PersonApi } from '@/api/person'
import dayjs from 'dayjs'
// 'line' 线'bar'
const chartType = ref<'line' | 'bar'>('line')
//
const chartData = ref<{ date: string; count: number }[]>([])
//
const dateRange = ref<[string, string]>([
dayjs().subtract(6, 'day').startOf('day').format('YYYY-MM-DD 00:00:00'),
dayjs().endOf('day').format('YYYY-MM-DD 23:59:59')
])
// echarts
const renderChart = () => {
const dom = document.getElementById('main')
if (!dom) return
const myChart = echarts.init(dom)
myChart.setOption({
tooltip: { trigger: 'axis' },
grid: { left: 40, right: 20, top: 40, bottom: 40 },
xAxis: {
type: 'category',
data: chartData.value.map(item => item.date),
axisLine: { lineStyle: { color: '#dcdfe6' } },
axisLabel: { color: '#666', fontSize: 14 }
},
yAxis: {
type: 'value',
minInterval: 1,
axisLine: { lineStyle: { color: '#dcdfe6' } },
axisLabel: { color: '#666', fontSize: 14 }
},
series: [
{
name: '会员开通数量',
type: chartType.value,
data: chartData.value.map(item => item.count),
smooth: chartType.value === 'line',
barWidth: chartType.value === 'bar' ? '40%' : undefined,
itemStyle: {
color: chartType.value === 'line' ? '#409EFF' : '#67C23A',
borderRadius: chartType.value === 'bar' ? [6, 6, 0, 0] : 0
},
lineStyle: {
width: 3,
color: '#409EFF'
},
areaStyle: chartType.value === 'line' ? { color: 'rgba(64,158,255,0.15)' } : undefined
}
]
})
}
// PersonApi
const fetchData = async () => {
if (!dateRange.value || !dateRange.value[0] || !dateRange.value[1]) return
const res = await PersonApi.getMemberRegisterCount(dateRange.value[0], dateRange.value[1])
chartData.value = Array.isArray(res) ? res : []
}
//
const onDateChange = (range: [string, string]) => {
dateRange.value = range
fetchData()
}
//
watch([chartData, chartType], () => {
renderChart()
})
//
onMounted(() => {
fetchData()
setTimeout(() => {
renderChart()
}, 300)
})
</script>
<style scoped>
.member-stat-card {
border-radius: 16px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.04);
background: #fff;
padding: 24px 24px 16px 24px;
min-height: 80px; /* 卡片最小高度默认80px具体卡片可单独覆盖 */
}
.search-card {
height: 48px;
padding-top: 5px;
padding-bottom: 12px;
}
.mb-4 {
margin-bottom: 24px;
}
@media (max-width: 768px) {
.member-stat-card {
padding: 12px;
}
.mb-4 {
margin-bottom: 12px;
}
}
.stat-card {
min-height: 320px; /* 统计图卡片主内容区高度,可根据实际需求调整 */
}
</style>