会员统计

This commit is contained in:
曹磊 2025-07-02 15:04:03 +08:00
parent 3b9c33d715
commit d4d211111c
9 changed files with 490 additions and 22 deletions

View File

@ -24,9 +24,9 @@
type: String as PropType<string>, type: String as PropType<string>,
default: 'calc(100vh - 78px)', default: 'calc(100vh - 78px)',
}, },
itemStyleLabel: { itemStyle: {
type: Boolean as PropType<boolean>, type: Object,
default: true, default: () => ({ normal: {label : {show: false, position: 'inside'}}}),
}, },
seriesName: { seriesName: {
type: String as PropType<string>, type: String as PropType<string>,
@ -49,7 +49,7 @@
type: 'shadow', type: 'shadow',
label: { label: {
show: true, show: true,
backgroundColor: '#333', backgroundColor: '#6a7985',
}, },
}, },
}, },
@ -64,9 +64,9 @@
{ {
name: props.seriesName, name: props.seriesName,
type: 'bar', type: 'bar',
data: [],
color: props.seriesColor, color: props.seriesColor,
itemStyle : { normal: {label : {show: props.itemStyleLabel, position: 'inside'}}}, itemStyle : props.itemStyle,
data: [],
}, },
], ],
}); });
@ -89,7 +89,7 @@
// update-begin--author:liaozhiyang---date:20240407---forQQYUN-8762echars // update-begin--author:liaozhiyang---date:20240407---forQQYUN-8762echars
option.series[0].name = props.seriesName; option.series[0].name = props.seriesName;
option.series[0].color = props.seriesColor; option.series[0].color = props.seriesColor;
option.series[0].itemStyle.normal.label.show = props.itemStyleLabel; option.series[0].itemStyle = props.itemStyle;
// update-end--author:liaozhiyang---date:20240407---forQQYUN-8762echars // update-end--author:liaozhiyang---date:20240407---forQQYUN-8762echars
option.xAxis.data = xAxisData; option.xAxis.data = xAxisData;
setOptions(option); setOptions(option);

View File

@ -0,0 +1,102 @@
<template>
<div ref="chartRef" :style="{ height, width }"></div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, Ref, reactive, watchEffect } from 'vue';
import { useECharts } from '/@/hooks/web/useECharts';
import { cloneDeep } from 'lodash-es';
export default defineComponent({
name: 'single-line',
props: {
chartData: {
type: Array,
default: () => [],
},
option: {
type: Object,
default: () => ({}),
},
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: 'calc(100vh - 78px)',
},
lineStyle: {
type: Object,
default: () => ({color: '#1890ff'}),
},
areaStyle: {
type: Object,
default: () => ({color: '#1890ff'}),
},
seriesLabel: {
type: Object,
default: () => ({show: false, position: 'top'}),
},
seriesName: {
type: String as PropType<string>,
default: 'bar',
},
},
setup(props) {
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
const option = reactive({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value',
},
series: [
{
name: props.seriesName,
type: 'line',
areaStyle: props.areaStyle,
lineStyle: props.lineStyle,
label: props.seriesLabel,
data: [],
}
]
});
watchEffect(() => {
props.chartData && initCharts();
});
function initCharts() {
if (props.option) {
Object.assign(option, cloneDeep(props.option));
}
let seriesData = props.chartData.map((item) => {
return item.value;
});
let xAxisData = props.chartData.map((item) => {
return item.name;
});
option.series[0].data = seriesData;
option.series[0].name = props.seriesName;
option.series[0].areaStyle = props.areaStyle;
option.series[0].lineStyle = props.lineStyle;
option.series[0].label = props.seriesLabel;
option.xAxis.data = xAxisData;
setOptions(option);
}
return { chartRef };
},
});
</script>

29
src/views/zh/hy/hy.api.ts Normal file
View File

@ -0,0 +1,29 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
housingestateList = '/zh/hy/queryHousingestateList',
registerList = '/zh/hy/queryHyRegisterList',
xzList = '/zh/hy/queryHyXzList',
tdcsList = '/zh/hy/queryHyTdcsList',
}
/**
*
* @param params
*/
export const housingestateList = (params) => defHttp.get({ url: Api.housingestateList, params });
/**
*
* @param params
*/
export const registerList = (params) => defHttp.get({ url: Api.registerList, params });
/**
*
* @param params
*/
export const xzList = (params) => defHttp.get({ url: Api.xzList, params });
/**
*
* @param params
*/
export const tdcsList = (params) => defHttp.get({ url: Api.tdcsList, params });

View File

@ -0,0 +1,106 @@
<template>
<div class="p-4">
<a-card :bordered="false" style="height: 100%">
<a-row>
<a-col :span="20">
<a-radio-group v-model:value="parmas.type" @change="changeType">
<a-radio-button value="1">近3天</a-radio-button>
<a-radio-button value="2">近7天</a-radio-button>
<a-radio-button value="3">近30天</a-radio-button>
<a-radio-button value="4">自定义</a-radio-button>
</a-radio-group>
<span v-show="parmas.type=='4'" style="margin-left: 5px;">
<a-range-picker :value-format="valueFormat" @change="changeDate" />
</span>
<a-select ref="select"
placeholder="请选区域"
v-model:value="parmas.housingestateId"
style="width: 200px;margin-left: 5px;"
@change="handleHousinges">
<a-select-option value="" >全部</a-select-option>
<a-select-option :value="item.housingestateId" v-for="item in housingestates" :key="item.housingestateId">{{item.housingestateName}}</a-select-option>
</a-select>
</a-col>
</a-row>
<Bar :chartData="dataSource" height="80vh" :option="{ title: { text: '会员投递次数排行榜', left: 'center' }}" :itemStyle="{ normal: {label : {show: true, position: 'top'}}}" :seriesName="'投递次数'"></Bar>
</a-card>
</div>
</template>
<script lang="ts" setup>
import {ref, unref, reactive, onMounted} from 'vue';
import Bar from '/@/components/chart/Bar.vue';
import { tdcsList,housingestateList } from '../hy.api';
import type { Dayjs } from 'dayjs';
const valueFormat = 'YYYY-MM-DD';
const dataSource = ref([]);
const dateValue = ref();
const housingestates = ref();
async function getHousingestates(){
housingestates.value = await housingestateList();
}
const parmas = reactive<any>({
beginTime: '',
endTime: '',
type: '1',
})
function changeType (){
parmas.endTime=getPreviousDate(0)+' 23:59:59';
if (parmas.type === '1') {
parmas.beginTime=getPreviousDate(2)+' 00:00:00';
loadDate();
}
if (parmas.type === '2') {
parmas.beginTime=getPreviousDate(6)+' 00:00:00';
loadDate();
}
if (parmas.type === '3') {
parmas.beginTime=getPreviousDate(29)+' 00:00:00';
loadDate();
}
}
const changeDate = (date, dateString) => {
parmas.beginTime=dateString[0]+' 00:00:00';
parmas.endTime=dateString[1]+' 23:59:59';
loadDate();
};
function handleHousinges(){
loadDate();
};
async function loadDate() {
const res = await tdcsList(parmas);
dataSource.value = [];
for (let i = 0; i < res.length; i++) {
dataSource.value.push({
name: `${res[i].phone}`,
value: res[i].cn,
});
}
}
function getPreviousDate(days){
const date = new Date();
date.setDate(date.getDate() - days);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // +10-indexed
const day = String(date.getDate()).padStart(2, '0'); //
const formattedDate = `${year}-${month}-${day}`;
return formattedDate;
}
onMounted(() => {
getHousingestates();
if(parmas.type=='1'){
parmas.beginTime=getPreviousDate(2)+' 00:00:00';
parmas.endTime=getPreviousDate(0)+' 23:59:59';
}
loadDate();
})
</script>

View File

@ -0,0 +1,106 @@
<template>
<div class="p-4">
<a-card :bordered="false" style="height: 100%">
<a-row>
<a-col :span="20">
<a-radio-group v-model:value="parmas.type" @change="changeType">
<a-radio-button value="1">近3天</a-radio-button>
<a-radio-button value="2">近7天</a-radio-button>
<a-radio-button value="3">近30天</a-radio-button>
<a-radio-button value="4">自定义</a-radio-button>
</a-radio-group>
<span v-show="parmas.type=='4'" style="margin-left: 5px;">
<a-range-picker :value-format="valueFormat" @change="changeDate" />
</span>
<a-select ref="select"
placeholder="请选区域"
v-model:value="parmas.housingestateId"
style="width: 200px;margin-left: 5px;"
@change="handleHousinges">
<a-select-option value="" >全部</a-select-option>
<a-select-option :value="item.housingestateId" v-for="item in housingestates" :key="item.housingestateId">{{item.housingestateName}}</a-select-option>
</a-select>
</a-col>
</a-row>
<Bar :chartData="dataSource" height="80vh" :option="{ title: { text: '会员投递次数排行榜', left: 'center' }}" :itemStyle="{ normal: {label : {show: true, position: 'top'}}}" :seriesName="'投递次数'"></Bar>
</a-card>
</div>
</template>
<script lang="ts" setup>
import {ref, unref, reactive, onMounted} from 'vue';
import Bar from '/@/components/chart/Bar.vue';
import { tdcsList,housingestateList } from '../hy.api';
import type { Dayjs } from 'dayjs';
const valueFormat = 'YYYY-MM-DD';
const dataSource = ref([]);
const dateValue = ref();
const housingestates = ref();
async function getHousingestates(){
housingestates.value = await housingestateList();
}
const parmas = reactive<any>({
beginTime: '',
endTime: '',
type: '1',
})
function changeType (){
parmas.endTime=getPreviousDate(0)+' 23:59:59';
if (parmas.type === '1') {
parmas.beginTime=getPreviousDate(2)+' 00:00:00';
loadDate();
}
if (parmas.type === '2') {
parmas.beginTime=getPreviousDate(6)+' 00:00:00';
loadDate();
}
if (parmas.type === '3') {
parmas.beginTime=getPreviousDate(29)+' 00:00:00';
loadDate();
}
}
const changeDate = (date, dateString) => {
parmas.beginTime=dateString[0]+' 00:00:00';
parmas.endTime=dateString[1]+' 23:59:59';
loadDate();
};
function handleHousinges(){
loadDate();
};
async function loadDate() {
const res = await tdcsList(parmas);
dataSource.value = [];
for (let i = 0; i < res.length; i++) {
dataSource.value.push({
name: `${res[i].phone}`,
value: res[i].cn,
});
}
}
function getPreviousDate(days){
const date = new Date();
date.setDate(date.getDate() - days);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // +10-indexed
const day = String(date.getDate()).padStart(2, '0'); //
const formattedDate = `${year}-${month}-${day}`;
return formattedDate;
}
onMounted(() => {
getHousingestates();
if(parmas.type=='1'){
parmas.beginTime=getPreviousDate(2)+' 00:00:00';
parmas.endTime=getPreviousDate(0)+' 23:59:59';
}
loadDate();
})
</script>

View File

@ -0,0 +1,92 @@
<template>
<div class="p-4">
<a-card :bordered="false" style="height: 100%">
<a-row>
<a-col :span="20">
<a-radio-group v-model:value="parmas.type" @change="changeType">
<a-radio-button value="1">近3天</a-radio-button>
<a-radio-button value="2">近7天</a-radio-button>
<a-radio-button value="3">近30天</a-radio-button>
<a-radio-button value="4">自定义</a-radio-button>
</a-radio-group>
<span v-show="parmas.type=='4'" style="margin-left: 5px;">
<a-range-picker :value-format="valueFormat" @change="changeDate" />
</span>
</a-col>
</a-row>
<BaseAreaLine :chartData="dataSource" height="80vh" :option="{ title: { text: '新增会员统计', left: 'center' }}" :seriesLabel="{show: true, position: 'top'}" :seriesName="'个数'"></BaseAreaLine>
</a-card>
</div>
</template>
<script lang="ts" setup>
import {ref, unref, reactive, onMounted} from 'vue';
import BaseAreaLine from '/@/components/chart/BaseAreaLine.vue';
import { xzList } from '../hy.api';
import type { Dayjs } from 'dayjs';
const valueFormat = 'YYYY-MM-DD';
const dataSource = ref([]);
const dateValue = ref();
const parmas = reactive<any>({
beginTime: '',
endTime: '',
type: '1',
})
function changeType (){
parmas.endTime=getPreviousDate(0)+' 23:59:59';
if (parmas.type === '1') {
parmas.beginTime=getPreviousDate(2)+' 00:00:00';
loadDate();
}
if (parmas.type === '2') {
parmas.beginTime=getPreviousDate(6)+' 00:00:00';
loadDate();
}
if (parmas.type === '3') {
parmas.beginTime=getPreviousDate(29)+' 00:00:00';
loadDate();
}
}
const changeDate = (date, dateString) => {
parmas.beginTime=dateString[0]+' 00:00:00';
parmas.endTime=dateString[1]+' 23:59:59';
loadDate();
};
function handleHousinges(){
loadDate();
}
async function loadDate() {
const res = await xzList(parmas);
dataSource.value = [];
for (let i = 0; i < res.length; i++) {
dataSource.value.push({
name: `${res[i].shortDay}`,
value: res[i].cn,
});
}
}
function getPreviousDate(days){
const date = new Date();
date.setDate(date.getDate() - days);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // +10-indexed
const day = String(date.getDate()).padStart(2, '0'); //
const formattedDate = `${year}-${month}-${day}`;
return formattedDate;
}
onMounted(() => {
if(parmas.type=='1'){
parmas.beginTime=getPreviousDate(2)+' 00:00:00';
parmas.endTime=getPreviousDate(0)+' 23:59:59';
}
loadDate();
})
</script>

View File

@ -1,11 +0,0 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
registerList = '/zh/hy/queryPageList',
}
/**
*
* @param params
*/
export const registerList = (params) => defHttp.get({ url: Api.registerList, params });

View File

@ -4,9 +4,17 @@
<a-row> <a-row>
<a-col :span="10"> <a-col :span="10">
<a-date-picker :value-format="valueFormat" :default-value="defaultValue" v-model:value="parmas.dateString" @change="changeDate" /> <a-date-picker :value-format="valueFormat" :default-value="defaultValue" v-model:value="parmas.dateString" @change="changeDate" />
<a-select ref="select"
placeholder="请选区域"
v-model:value="parmas.housingestateId"
style="width: 200px;margin-left: 5px;"
@change="handleHousinges">
<a-select-option value="" >全部</a-select-option>
<a-select-option :value="item.housingestateId" v-for="item in housingestates" :key="item.housingestateId">{{item.housingestateName}}</a-select-option>
</a-select>
</a-col> </a-col>
</a-row> </a-row>
<Bar :chartData="dataSource" height="80vh" :option="{ title: { text: '会员注册时间段统计', left: 'center' }}" :itemStyleLabel="false" :seriesName="'个数'"></Bar> <Bar :chartData="dataSource" height="80vh" :option="{ title: { text: '会员注册时间段统计', left: 'center' }}" :itemStyle="{ normal: {label : {show: true, position: 'top'}}}" :seriesName="'个数'"></Bar>
</a-card> </a-card>
</div> </div>
</template> </template>
@ -14,11 +22,16 @@
<script lang="ts" setup> <script lang="ts" setup>
import {ref, unref, reactive, onMounted} from 'vue'; import {ref, unref, reactive, onMounted} from 'vue';
import Bar from '/@/components/chart/Bar.vue'; import Bar from '/@/components/chart/Bar.vue';
import { registerList } from './hy.api'; import { registerList,housingestateList } from '../hy.api';
import type { Dayjs } from 'dayjs'; import type { Dayjs } from 'dayjs';
const valueFormat = 'YYYY-MM-DD'; const valueFormat = 'YYYY-MM-DD';
const dataSource = ref([]); const dataSource = ref([]);
const dateValue = ref(); const dateValue = ref();
const housingestates = ref();
async function getHousingestates(){
housingestates.value = await housingestateList();
}
const parmas = reactive<any>({ const parmas = reactive<any>({
beginTime: '', beginTime: '',
@ -29,14 +42,19 @@
const changeDate = (date, dateString) => { const changeDate = (date, dateString) => {
parmas.dateString = dateString; parmas.dateString = dateString;
loadDate();
}; };
function handleHousinges(){
loadDate();
};
async function loadDate() { async function loadDate() {
if(parmas.dateString!=''){ if(parmas.dateString!=''){
parmas.beginTime = parmas.dateString+' 00:00:00'; parmas.beginTime = parmas.dateString+' 00:00:00';
parmas.endTime = parmas.dateString+' 23:59:59'; parmas.endTime = parmas.dateString+' 23:59:59';
} }
const res = await registerList(parmas); const res = await registerList(parmas);
console.log(res);
dataSource.value = []; dataSource.value = [];
for (let i = 0; i < res.length; i++) { for (let i = 0; i < res.length; i++) {
dataSource.value.push({ dataSource.value.push({
@ -46,9 +64,19 @@
} }
} }
function getCurrent(){
const date = new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // +10-indexed
const day = String(date.getDate()).padStart(2, '0'); //
const formattedDate = `${year}-${month}-${day}`;
return formattedDate;
}
onMounted(() => { onMounted(() => {
getHousingestates();
if(parmas.dateString==''){ if(parmas.dateString==''){
parmas.dateString='2025-06-30'; parmas.dateString=getCurrent();
} }
loadDate(); loadDate();
}) })

View File

@ -0,0 +1,16 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
monitorHousingestate = '/dataMonitorApi/monitorHousingestateInfos',
monitorDevice = '/dataMonitorApi/monitorAllDeviceInfos',
monitorUser = '/dataMonitorApi/monitorUserInfos',
monitorOverflow = '/dataMonitorApi/monitorDeviceOverflowInfo',
monitorClear = '/dataMonitorApi/monitorDeviceClearLog',
monitorOrder = '/dataMonitorApi/monitorOrderInfos',
}
export const monitorHousingestate = (params) => defHttp.get({ url: Api.monitorHousingestate, params });
export const monitorDevice = (params) => defHttp.get({ url: Api.monitorDevice, params });
export const monitorUser = (params) => defHttp.get({ url: Api.monitorUser, params });
export const monitorOverflow = (params) => defHttp.get({ url: Api.monitorOverflow, params });
export const monitorClear = (params) => defHttp.get({ url: Api.monitorClear, params });
export const monitorOrder = (params) => defHttp.get({ url: Api.monitorOrder, params });