499 lines
12 KiB
Vue
499 lines
12 KiB
Vue
<template>
|
||
<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">
|
||
// TODO 缴费分析
|
||
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: 14, fontWeight: 'normal' }
|
||
},
|
||
xAxis: {},
|
||
grid: {
|
||
left: '3%',
|
||
right: '4%',
|
||
top: '12%',
|
||
bottom: '8%',
|
||
containLabel: true
|
||
},
|
||
yAxis: {},
|
||
series: [
|
||
{
|
||
symbolSize: 20,
|
||
data: [
|
||
[10.0, 8.04],
|
||
[8.07, 6.95],
|
||
[13.0, 7.58],
|
||
[9.05, 8.81],
|
||
[11.0, 8.33],
|
||
[14.0, 7.66],
|
||
[13.4, 6.81],
|
||
[10.0, 6.33],
|
||
[14.0, 8.96],
|
||
[12.5, 6.82],
|
||
[9.15, 7.2],
|
||
[11.5, 7.2],
|
||
[3.03, 4.23],
|
||
[12.2, 7.83],
|
||
[2.02, 4.47],
|
||
[1.05, 3.33],
|
||
[4.05, 4.96],
|
||
[6.03, 7.24],
|
||
[12.0, 6.26],
|
||
[12.0, 8.84],
|
||
[7.08, 5.82],
|
||
[5.02, 5.68]
|
||
],
|
||
type: 'scatter'
|
||
}
|
||
]
|
||
});
|
||
|
||
// ! 进十日缴费数量情况 -------------------------------------------------------------------------------------
|
||
// 近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">
|
||
.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>
|