人员缴费分析

This commit is contained in:
Zy
2026-04-27 15:45:38 +08:00
parent c7e3d9695b
commit 124affcb25
2 changed files with 976 additions and 8 deletions

View File

@@ -1,12 +1,515 @@
<template>
<div class="p-2 h-full flex flex-col">
<Pageheader></Pageheader>
<div class="flex-1 color-gray flex h-full font-size-6 flex-center">系统还在维护中敬请期待...</div>
<div class="personnel p-2 h-full flex flex-col">
<div class="flex flex-items-center searchBox">
<div class="searchLabel">选择日期</div>
<el-date-picker
v-model="date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width: 400px; margin-right: 10px"
></el-date-picker>
<el-button type="primary">查询</el-button>
<el-button>重置</el-button>
</div>
<el-row :gutter="20">
<el-col :span="16">
<div class="cardbox">
<div>收入来源</div>
<div class="cardbody" style="height: 200px">
<div class="flex flex-col flex-items-center flex-1">
<Echarts :options="options"></Echarts>
<div>停车费</div>
</div>
<div class="flex flex-col flex-items-center flex-1">
<Echarts :options="options"></Echarts>
<div>物业费</div>
</div>
<div class="flex flex-col flex-items-center flex-1">
<Echarts :options="options"></Echarts>
<div>商铺租金</div>
</div>
<div class="flex flex-col flex-items-center flex-1">
<Echarts :options="options"></Echarts>
<div>水电费</div>
</div>
<div class="flex flex-col flex-items-center flex-1">
<Echarts :options="options"></Echarts>
<div>维修费</div>
</div>
</div>
</div>
</el-col>
<el-col :span="8">
<div class="cardbox">
<div>缴费方式</div>
<div class="cardbody w-full" style="height: 150px">
<Echarts :options="options2"></Echarts>
</div>
</div>
</el-col>
</el-row>
<div class="" style="height: 20px"></div>
<el-row :gutter="20">
<el-col :span="16">
<div class="cardbox w-full" style="height: 400px">
<Echarts :options="options3"></Echarts>
</div>
</el-col>
<el-col :span="8">
<div class="cardbox w-full" style="height: 400px">
<div class="" style="height: 50px; margin-bottom: 20px">缴费方式</div>
<div flex flex-items-center justify-center>
<ul class="legendbox">
<li>小程序</li>
<li>物业缴费</li>
<li>手机APP</li>
</ul>
<ul class="circlebox">
<li>70%</li>
<li>20%</li>
<li>10%</li>
</ul>
</div>
</div>
</el-col>
</el-row>
<div class="" style="height: 20px"></div>
<el-row>
<el-col :span="24">
<div class="cardbox w-full" style="height: 450px">
<Echarts :options="flowLineOption"></Echarts>
</div>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Echarts from '@/components/Echarts/index.vue';
import { EChartsOption } from 'echarts';
import { getLast7Days, getLastNDays } from '@/utils/time';
/** 入住情况 */
const options = shallowRef<EChartsOption>({
tooltip: { show: false },
series: [
// 1. 底层细圆环(灰色底色)
{
type: 'pie',
radius: ['60%', '80%'], // 细一点
clockwise: true,
data: [100],
itemStyle: { color: '#ebeef5' },
emphasis: { disabled: true },
label: { show: false }
},
// 2. 上层粗圆环(进度条)
{
type: 'pie',
radius: ['60%', '85%'], // 粗一点
clockwise: true,
itemStyle: {
color: '#409eff',
borderRadius: 10
},
emphasis: { disabled: true },
label: {
show: true,
position: 'center',
fontSize: 20,
fontWeight: 'bold',
formatter: '85%'
},
data: [{ value: 85 }, { value: 15, itemStyle: { color: 'transparent' } }]
}
]
});
/** 入住类型 */
const options1 = shallowRef<EChartsOption>({
legend: {
top: 'center',
left: '0',
orient: 'vertical',
itemGap: 50
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['50%', '100%'],
avoidLabelOverlap: false,
left: '50%',
label: {
show: false,
position: 'center'
},
tooltip: {
show: false
},
emphasis: {
label: {
show: false,
fontSize: 20,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 25, name: '业主', itemStyle: { color: '#0a74fe' } },
{ value: 25, name: '租户', itemStyle: { color: '#fe3d4e' } },
{ value: 25, name: '亲属', itemStyle: { color: '#00cf7f' } },
{ value: 25, name: '其他', itemStyle: { color: '#f48e3b' } }
]
}
]
});
/** 缴费方式 */
const options2 = shallowRef<EChartsOption>({
legend: {
top: 'center',
left: '0',
orient: 'vertical',
itemGap: 50
},
clockwise: true,
series: [
{
name: 'Access From',
type: 'pie',
startAngle: 280,
radius: ['50%', '100%'],
avoidLabelOverlap: false,
left: '50%',
label: {
show: false,
position: 'center'
},
tooltip: {
show: false
},
emphasis: {
label: {
show: false,
fontSize: 20,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 50, name: '支付宝', itemStyle: { color: '#0a74fe' } },
{ value: 30, name: '微信', itemStyle: { color: '#f74043' } },
{ value: 20, name: '线下', itemStyle: { color: '#fcd152' } }
]
}
]
});
// ! 年龄分布 -------------------------------------------------------------------------------------
const agesX = ['6岁以下', '6-11岁', '11-14岁', '15-24岁', '25-30岁', '31-40岁', '41-50岁', '51-61岁', '61-65岁', '65-70岁', '70岁以上'];
const agesData = [520, 680, 460, 750, 1350, 1860, 1420, 960, 630, 380, 210];
// 🔥 关键:灰色背景数据 = 最大值 - 真实数据互补100%
const maxValue = 2000; // 设定总高度(可自行调整)
const middleHeight = 20;
const barwidth = '10%';
const bgData = agesData.map((item) => maxValue - item - middleHeight);
const bgData2 = agesData.map((item) => middleHeight);
/** 年龄分布 */
const options3 = shallowRef<EChartsOption>({
title: {
text: '住户年龄分布',
left: '0',
top: '0',
textStyle: { fontSize: 16, fontWeight: 'bold' }
},
grid: {
left: '3%',
right: '4%',
top: '12%',
bottom: '8%',
containLabel: true
},
tooltip: {
trigger: 'item',
confine: true,
axisPointer: { type: 'shadow' }
},
// 鼠标悬浮高亮当前,其他隐藏
xAxis: { type: 'category', data: agesX, axisLabel: { interval: 0 } },
yAxis: {
type: 'value',
splitLine: { show: true, lineStyle: { color: '#eee' } }
},
series: [
// 蓝色真实数据
{
type: 'bar',
barWidth: barwidth,
data: agesData,
itemStyle: {
color: '#409eff',
borderRadius: [4, 4, 0, 0]
},
stack: 'total'
},
{
type: 'bar',
barWidth: barwidth,
data: bgData2,
tooltip: { show: false }, // 关键:不显示提示
itemStyle: {
color: '#fff'
},
stack: 'total'
},
{
type: 'bar',
barWidth: barwidth,
data: bgData,
tooltip: { show: false }, // 关键:不显示提示
itemStyle: {
color: 'rgba(180, 180, 180, 0.2)'
},
stack: 'total'
}
]
});
// ! 年龄分布 -------------------------------------------------------------------------------------
// ! 职业分布比例 -------------------------------------------------------------------------------------
// ! 职业分布比例 -------------------------------------------------------------------------------------
// ! 人员流动情况 -------------------------------------------------------------------------------------
// 近10天日期
const dayX = getLast7Days(10);
// 小区真实规模:人员流入 / 流出 数据
const inData = [97, 145, 63, 112, 88, 156, 74, 131, 102, 59];
const outData = [121, 85, 142, 71, 135, 93, 116, 66, 148, 104];
/** 进十日缴费数量情况 - 平滑折线图 */
const flowLineOption = shallowRef<EChartsOption>({
title: {
text: '进十日缴费数量趋势',
left: 0,
top: 0,
textStyle: { fontSize: 14, fontWeight: 'normal' }
},
tooltip: {
trigger: 'axis',
confine: true
},
grid: {
left: '3%',
right: '4%',
top: '12%',
bottom: '8%',
containLabel: true
},
legend: {
data: ['已缴费', '未缴费'],
right: 10,
top: 0
},
xAxis: {
type: 'category',
data: dayX,
boundaryGap: false,
axisLine: { lineStyle: { color: '#eee' } }
},
yAxis: {
type: 'value',
splitLine: {
show: true,
lineStyle: { color: '#eee' }
}
},
series: [
{
name: '已缴费',
type: 'line',
smooth: true, // 平滑折线 核心
data: inData,
symbol: 'circle',
symbolSize: 6,
itemStyle: { color: '#f94144' },
lineStyle: { width: 4 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#fbd7d9' },
{ offset: 1, color: '#fbd7d9' }
]
}
}
},
{
name: '未缴费',
type: 'line',
smooth: true, // 平滑折线 核心
data: outData,
symbol: 'circle',
symbolSize: 6,
itemStyle: { color: '#f9c74f' },
lineStyle: { width: 4 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#fcf2dc' },
{ offset: 1, color: '#fcf2dc' }
]
}
}
}
]
});
// ! 人员流动情况 -------------------------------------------------------------------------------------
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.personnel {
background: linear-gradient(180deg, #deedff 0%, #f8f9fe 50%);
.el-button {
width: 80px;
height: 32px;
border-radius: 4px;
}
.searchBox {
margin-bottom: 20px;
}
.searchLabel {
width: 100px;
font-size: 0.8rem;
}
// 入住清空
.cardbox {
background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, #ffffff 70%);
border-radius: 16px 16px 16px 16px;
border: 2px solid #ffffff;
font-size: 20px;
padding: 20px;
min-height: 275px;
.cardbody {
padding: 0 30px;
display: flex;
align-items: center;
justify-content: space-between;
.card_leftbox {
text-align: center;
.labelbox {
font-size: 16px;
}
.valuebox {
font-weight: 600;
font-size: 36px;
color: #222222;
}
}
.echartsbox {
width: 400px;
}
}
}
.legendbox {
font-size: 0.8rem;
li {
margin-bottom: 20px;
position: relative;
width: 100px;
height: 50px;
line-height: 50px;
text-align: left;
padding-left: 30px;
&::after {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
display: block;
width: 20px;
height: 20px;
border-radius: 50%;
background: #c893fd;
}
&:first-of-type {
&::after {
background: #4a3aff;
}
}
&:last-of-type {
&::after {
background: #1e1b39;
}
}
}
}
.circlebox {
margin-left: 60px;
position: relative;
width: 300px;
height: 200px;
transform: translateY(-40px);
li {
position: absolute;
right: 0;
top: 0;
width: 150px;
height: 150px;
border-radius: 50%;
background-color: #c893fd;
border: 3px solid white;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 1.4rem;
font-weight: 450;
z-index: 10;
&:nth-child(1) {
width: 200px;
height: 200px;
right: inherit;
left: 0px;
font-size: 2rem;
top: 30px;
z-index: 9;
background-color: #4a3aff;
}
&:nth-child(3) {
width: 100px;
height: 100px;
left: inherit;
top: inherit;
right: 50px;
font-size: 1.1rem;
bottom: -20px;
z-index: 11;
background-color: #1e1b39;
}
}
}
}
</style>

View File

@@ -1,12 +1,477 @@
<template>
<div class="p-2 h-full flex flex-col">
<Pageheader></Pageheader>
<div class="flex-1 color-gray flex h-full font-size-6 flex-center">系统还在维护中敬请期待...</div>
<div class="personnel p-2 h-full flex flex-col">
<div class="flex flex-items-center searchBox">
<div class="searchLabel">选择日期</div>
<el-date-picker
v-model="date"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd"
style="width: 400px; margin-right: 10px"
></el-date-picker>
<el-button type="primary">查询</el-button>
<el-button>重置</el-button>
</div>
<el-row :gutter="20">
<el-col :span="8">
<div class="cardbox">
<div>入住情况</div>
<div class="cardbody" style="height: 200px">
<div class="card_leftbox">
<div class="valuebox">4889</div>
<div class="labelbox">入住人数</div>
</div>
<div class="echartsbox h-full">
<Echarts :options="options"></Echarts>
</div>
</div>
</div>
</el-col>
<el-col :span="8">
<div class="cardbox">
<div>住户分析情况</div>
<div class="cardbody w-full" style="height: 150px">
<Echarts :options="options1"></Echarts>
</div>
</div>
</el-col>
<el-col :span="8">
<div class="cardbox">
<div>男女比例</div>
<div class="cardbody w-full" style="height: 150px">
<Echarts :options="options2"></Echarts>
</div>
</div>
</el-col>
</el-row>
<div class="" style="height: 20px"></div>
<el-row :gutter="20">
<el-col :span="16">
<div class="cardbox w-full" style="height: 400px">
<Echarts :options="options3"></Echarts>
</div>
</el-col>
<el-col :span="8">
<div class="cardbox w-full" style="height: 400px">
<Echarts :options="options4"></Echarts>
</div>
</el-col>
</el-row>
<div class="" style="height: 20px"></div>
<el-row>
<el-col :span="24">
<div class="cardbox w-full" style="height: 450px">
<Echarts :options="flowLineOption"></Echarts>
</div>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import Echarts from '@/components/Echarts/index.vue';
import { EChartsOption } from 'echarts';
import { getLast7Days, getLastNDays } from '@/utils/time';
/** 入住情况 */
const options = shallowRef<EChartsOption>({
tooltip: { show: false },
series: [
// 1. 底层细圆环(灰色底色)
{
type: 'pie',
radius: ['68%', '90%'], // 细一点
clockwise: true,
data: [100],
itemStyle: { color: '#ebeef5' },
emphasis: { disabled: true },
label: { show: false }
},
// 2. 上层粗圆环(进度条)
{
type: 'pie',
radius: ['65%', '98%'], // 粗一点
clockwise: true,
itemStyle: {
color: '#409eff',
borderRadius: 10
},
emphasis: { disabled: true },
label: {
show: true,
position: 'center',
fontSize: 20,
fontWeight: 'bold',
formatter: ' 85% \n\n 入住率'
},
data: [{ value: 85 }, { value: 15, itemStyle: { color: 'transparent' } }]
}
]
});
function getRingOption(percent) {
return {
tooltip: { show: false },
series: [
{
type: 'pie',
radius: ['78%', '88%'],
data: [100],
itemStyle: { color: '#ebeef5' },
emphasis: { disabled: true },
label: { show: false }
},
{
type: 'pie',
radius: ['75%', '90%'],
itemStyle: { color: '#409eff', borderRadius: 10 },
emphasis: { disabled: true },
label: {
show: true,
position: 'center',
fontSize: 18,
fontWeight: 'bold',
formatter: percent + '%'
},
data: [{ value: percent }, { value: 100 - percent, itemStyle: { color: 'transparent' } }]
}
]
};
}
/** 入住类型 */
const options1 = shallowRef<EChartsOption>({
legend: {
top: 'center',
left: '0',
orient: 'vertical',
itemGap: 50
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['50%', '100%'],
avoidLabelOverlap: false,
left: '50%',
label: {
show: false,
position: 'center'
},
tooltip: {
show: false
},
emphasis: {
label: {
show: false,
fontSize: 20,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 25, name: '业主', itemStyle: { color: '#0a74fe' } },
{ value: 25, name: '租户', itemStyle: { color: '#fe3d4e' } },
{ value: 25, name: '亲属', itemStyle: { color: '#00cf7f' } },
{ value: 25, name: '其他', itemStyle: { color: '#f48e3b' } }
]
}
]
});
/** 男女比例 */
const options2 = shallowRef<EChartsOption>({
legend: {
top: 'center',
left: '0',
orient: 'vertical',
itemGap: 50
},
clockwise: true,
series: [
{
name: 'Access From',
type: 'pie',
startAngle: 280,
radius: ['50%', '100%'],
avoidLabelOverlap: false,
left: '50%',
label: {
show: false,
position: 'center'
},
tooltip: {
show: false
},
emphasis: {
label: {
show: false,
fontSize: 20,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 60, name: '男', itemStyle: { color: '#0a74fe' } },
{ value: 40, name: '女', itemStyle: { color: '#f74043' } }
]
}
]
});
// ! 年龄分布 -------------------------------------------------------------------------------------
const agesX = ['6岁以下', '6-11岁', '11-14岁', '15-24岁', '25-30岁', '31-40岁', '41-50岁', '51-61岁', '61-65岁', '65-70岁', '70岁以上'];
const agesData = [520, 680, 460, 750, 1350, 1860, 1420, 960, 630, 380, 210];
// 🔥 关键:灰色背景数据 = 最大值 - 真实数据互补100%
const maxValue = 2000; // 设定总高度(可自行调整)
const middleHeight = 20;
const barwidth = '10%';
const bgData = agesData.map((item) => maxValue - item - middleHeight);
const bgData2 = agesData.map((item) => middleHeight);
/** 年龄分布 */
const options3 = shallowRef<EChartsOption>({
title: {
text: '住户年龄分布',
left: '0',
top: '0',
textStyle: { fontSize: 16, fontWeight: 'bold' }
},
grid: {
left: '3%',
right: '4%',
top: '12%',
bottom: '8%',
containLabel: true
},
tooltip: {
trigger: 'item',
confine: true,
axisPointer: { type: 'shadow' }
},
// 鼠标悬浮高亮当前,其他隐藏
xAxis: { type: 'category', data: agesX, axisLabel: { interval: 0 } },
yAxis: {
type: 'value',
splitLine: { show: true, lineStyle: { color: '#eee' } }
},
series: [
// 蓝色真实数据
{
type: 'bar',
barWidth: barwidth,
data: agesData,
itemStyle: {
color: '#409eff',
borderRadius: [4, 4, 0, 0]
},
stack: 'total'
},
{
type: 'bar',
barWidth: barwidth,
data: bgData2,
tooltip: { show: false }, // 关键:不显示提示
itemStyle: {
color: '#fff'
},
stack: 'total'
},
{
type: 'bar',
barWidth: barwidth,
data: bgData,
tooltip: { show: false }, // 关键:不显示提示
itemStyle: {
color: 'rgba(180, 180, 180, 0.2)'
},
stack: 'total'
}
]
});
// ! 年龄分布 -------------------------------------------------------------------------------------
// ! 职业分布比例 -------------------------------------------------------------------------------------
// 真实小区职业分布数据(上班族最多,依次递减)
const realData = [120, 260, 580, 620, 1480];
const options4 = shallowRef<EChartsOption>({
title: {
text: '职业分布比例',
left: '0',
top: '0',
textStyle: { fontSize: 16, fontWeight: 'bold' }
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01]
},
yAxis: {
type: 'category',
data: ['其他', '自由职业', '学生', '全职妈妈', '上班族']
},
series: [
{
name: '2011',
type: 'bar',
itemStyle: {
color: '#fe3d4e'
},
data: realData
}
]
});
// ! 职业分布比例 -------------------------------------------------------------------------------------
// ! 人员流动情况 -------------------------------------------------------------------------------------
// 近10天日期
const dayX = getLast7Days(10);
// 小区真实规模:人员流入 / 流出 数据
// 【全新打乱随机数据】小区人员流入 / 流出
const inData = [132, 76, 158, 91, 125, 68, 143, 84, 107, 162];
const outData = [95, 136, 72, 118, 83, 149, 65, 124, 101, 77];
/** 近十天人员流入流出 - 平滑折线图 */
const flowLineOption = shallowRef<EChartsOption>({
title: {
text: '近十日人员流动趋势',
left: 0,
top: 0,
textStyle: { fontSize: 14, fontWeight: 'normal' }
},
tooltip: {
trigger: 'axis',
confine: true
},
grid: {
left: '3%',
right: '4%',
top: '12%',
bottom: '8%',
containLabel: true
},
legend: {
data: ['人员流入', '人员流出'],
right: 10,
top: 0
},
xAxis: {
type: 'category',
data: dayX,
boundaryGap: false,
axisLine: { lineStyle: { color: '#eee' } }
},
yAxis: {
type: 'value',
splitLine: {
show: true,
lineStyle: { color: '#eee' }
}
},
series: [
{
name: '人员流入',
type: 'line',
smooth: true, // 平滑折线 核心
data: inData,
symbol: 'circle',
symbolSize: 6,
itemStyle: { color: '#f94144' },
lineStyle: { width: 4 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#fbd7d9' },
{ offset: 1, color: '#fbd7d9' }
]
}
}
},
{
name: '人员流出',
type: 'line',
smooth: true, // 平滑折线 核心
data: outData,
symbol: 'circle',
symbolSize: 6,
itemStyle: { color: '#f9c74f' },
lineStyle: { width: 4 },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#fcf2dc' },
{ offset: 1, color: '#fcf2dc' }
]
}
}
}
]
});
// ! 人员流动情况 -------------------------------------------------------------------------------------
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.personnel {
background: linear-gradient(180deg, #deedff 0%, #f8f9fe 50%);
.el-button {
width: 80px;
height: 32px;
border-radius: 4px;
}
.searchBox {
margin-bottom: 20px;
}
.searchLabel {
width: 100px;
font-size: 0.8rem;
}
// 入住清空
.cardbox {
background: linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, #ffffff 70%);
border-radius: 16px 16px 16px 16px;
border: 2px solid #ffffff;
font-size: 20px;
padding: 20px;
min-height: 275px;
.cardbody {
padding: 0 30px;
display: flex;
align-items: center;
justify-content: space-between;
.card_leftbox {
text-align: center;
.labelbox {
font-size: 16px;
}
.valuebox {
font-weight: 600;
font-size: 36px;
color: #222222;
}
}
.echartsbox {
width: 400px;
}
}
}
}
</style>