大屏可视化背景是构建整个数据大屏视觉体验和氛围的基石。它远不止是一张“好看的图片”,而是承载数据、引导视觉、传达主题的关键元素。
以下是对大屏可视化背景的全面描述,涵盖其核心作用、主要类型、设计原则和未来趋势。这是目前通过柱状、饼状、折现、列表等多种展示方式表示比对、占比、趋势等全方位的数据分析。
1、渐变柱状图
dom元素
<div :class="['card', 'cursor', selectedCardKey == 'headcount' ? 'selected' : '']" @click="selectedCard('headcount')">
<div class="card-title">
人员统计
<span class="dataTotal"
>{{ JSK.formatNumberWithCommas(populationTotal) || 0 }}
<el-icon v-if="selectedCardKey == 'headcount'" class="CaretRight"><CaretRight /></el-icon>
</span>
</div>
<v-chart ref="headcountChartRef" class="schart" :option="headcountChartOptions" :style="{ width: '296px', height: '125px' }"></v-chart>
</div>options参数
headcountChartRef.value.setOption({
xAxis: { data: headcountChartList.value.map((item) => item.year) },
series: [
{
data: headcountChartList.value.map((item, index) => {
let itemStyle;
if (index == 0) {
itemStyle = {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#00BAC5" },
{ offset: 1, color: "rgba(0, 186, 197, 0)" },
]),
};
} else if (index == 1) {
itemStyle = {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#5C7CEA" },
{ offset: 1, color: "rgba(92, 124, 234, 0)" },
]),
};
} else if (index == 2) {
itemStyle = {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#A2A1DA" },
{ offset: 1, color: "rgba(162, 161, 218, 0)" },
]),
};
} else if (index == 3) {
itemStyle = {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#D098CB" },
{ offset: 1, color: "rgba(208, 152, 203, 0)" },
]),
};
} else {
itemStyle = {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#CD6C27" },
{ offset: 1, color: "rgba(205, 108, 39, 0)" },
]),
};
}
return {
value: item.total,
itemStyle: itemStyle,
};
}),
},
],
});2、横向柱状图比例展示
dom展示
<!-- 居所管理 -->
<div :class="['card', 'cursor', selectedCardKey == 'domicile' ? 'selected' : '']" @click="selectedCard('domicile')">
<div class="card-title">
居所管理<span class="dataTotal"
>{{ JSK.formatNumberWithCommas(domicileTotal) || 0 }} <el-icon v-if="selectedCardKey == 'domicile'" class="CaretRight"><CaretRight /></el-icon
></span>
</div>
<div v-for="(item, index) in residenceList" class="residence-item" :key="index" :title="item.dictName + ':' + JSK.formatNumberWithCommas(item.total)">
<span class="lable" style="margin-right: 7px">{{ item.dictName }}</span>
<div class="myProgress" style="width: 110px; min-width: 136px">
<span :class="['inner', 'inner_color' + (index + 1)]" :style="{ width: getProportion(item.total, domicileTotal) }"></span>
</div>
<span :class="['proportionValue', 'color' + (index + 1)]"> {{ JSK.formatNumberWithCommas(item.total) }}</span>
</div>
<div v-if="!residenceList || residenceList.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
</div>css
.residence-item {
display: flex;
align-items: center;
white-space: nowrap;
line-height: 31px;
.lable {
display: inline-block;
width: 80px;
min-width: 80px;
}
}
.myProgress {
display: inline-block;
height: 10px;
background: rgba(255, 255, 255, 0.1);
position: relative;
.inner {
height: 4px;
position: absolute;
top: 3px;
}
.inner_color1 {
background: linear-gradient(270deg, #5c7cea 0%, rgba(92, 124, 234, 0.2) 100%);
}
.inner_color2 {
background: linear-gradient(270deg, #cd92c8 0%, rgba(205, 146, 200, 0.2) 100%);
}
.inner_color3 {
background: linear-gradient(270deg, #cd6c27 0%, rgba(205, 108, 39, 0.2) 100%);
}
.inner_color4 {
background: linear-gradient(270deg, #00bac5 0%, rgba(0, 186, 197, 0.15) 100%);
}
}
.proportionValue {
display: inline-block;
font-size: 14px;
width: 50px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 14px;
}3、折线图
dom
<div :class="['card', 'cursor', selectedCardKey == 'caseEvent' ? 'selected' : '']" @click="selectedCard('caseEvent')">
<div class="card-title">
案事件统计<span class="dataTotal">
<el-icon v-if="selectedCardKey == 'caseEvent'" class="CaretRight"><CaretRight /></el-icon
></span>
</div>
<div class="flex justify-end" style="margin-top: 13px">
<el-select v-model="dateClassVal_caseEvent" style="width: 80px" class="sidebar_select" @change="getCaseStatistics">
<el-option v-for="item in dateClass_caseEvent" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<v-chart ref="caseEventChartRef" class="schart" :option="caseEventOptions" :style="{ width: '296px', height: '170px' }"></v-chart>options
let dataZoom;
if (res.data.length > 12) {
dataZoom = [
{
type: "slider",
show: true,
left: "9%",
bottom: 8,
startValue: 0,
endValue: 12,
handleSize: 0, //两侧手柄大小
showDetail: false,
height: 0,
borderColor: "transparent",
showDataShadow: false,
minValueSpan: 12,
maxValueSpan: 12,
},
// { type: "inside", show: false, disabled: false },
];
} else {
dataZoom = [
{ type: "slider", show: false, disabled: true, endValue: res.data.length, bottom: 0 },
// { type: "inside", show: false, disabled: true },
];
}
caseEventChartRef.value &&
caseEventChartRef.value.setOption({
dataZoom,
xAxis: { data: res.data.map((item) => item.month || item.year || item.date) },
series: {
data: res.data.map((item) => item.num),
},
});
});4、饼状图、滚动图、联动
<!-- 人员统计详情 -->
<template>
<div class="sidebar_right_headcount" v-if="sidebarType == 'headcount'">
<!-- 基础人员 -->
<div>
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>基础人员
</div>
<div class="flex" style="align-items: center; margin-top: 12px; position: relative">
<div style="position: absolute; left: 88px; top: -29px">
总数:<span class="color2" style="font-weight: 600">{{ JSK.formatNumberWithCommas(basicPersonnelTotal) }}</span>
</div>
<v-chart
ref="populationChartRef"
class="schart"
:option="populationChartOptions"
:style="{ width: '74px', height: '74px' }"
@mousemove="populationChart_mousemove"
@mouseout="populationChart_mouseout"
></v-chart>
<ul class="ageGroup">
<li v-for="(item, index) in residentialPersonnelList" :key="item.name">
<span :class="['colorBlock', 'bg' + (index + 1)]"></span
><span style="width: 48px; margin-right: 10px" :class="['text_overflow_ellipsis', populationChartSelectIndex == index ? 'color' + (index + 1) : '']">{{ item.name }}</span>
<span :class="['color' + (index + 1)]" style="width: 48px; margin-right: 13px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
<span :class="['color' + (index + 1)]" style="width: 52px" class="text_overflow_ellipsis">{{ JSK.formatNumberWithCommas(item.num) }}</span>
</li>
</ul>
</div>
<div class="flex justify-between" style="margin-top: 20px">
<el-radio-group v-model="dateClassKey" size="small" class="sidebar_radio_group" @change="getGtreetClassPersonnelList_basic">
<el-radio-button v-for="item in dateClass" :key="item" :label="item.value">{{ item.label }}</el-radio-button>
</el-radio-group>
<el-select v-model="residentialClassKey" style="width: 80px" class="sidebar_select" @change="getGtreetClassPersonnelList_basic">
<el-option v-for="item in residentialClass" :key="item.enums" :label="item.name" :value="item.enums" />
</el-select>
</div>
<!-- <el-scrollbar height="106px" style="margin-top: 20px">
<ul style="width: 100%" class="barChart">
<li class="row" v-for="(item, index) in streetClassPersonnelList_basic" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.num)">
<span class="lable text_overflow_ellipsis" style="width: 70px; min-width: 70px; margin-right: 5px">{{ item.name }}</span>
<div class="myProgress myProgress1 color2" style="width: 128px; min-width: 128px; margin-right: 8px">
<span class="inner inner_color4" :style="{ width: (item.percent * 100).toFixed(2) + '%' }"></span>
</div>
<span class="color2" style="width: 48px; margin-right: 8px; min-width: 48px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
<span class="proportionValue color2 text_overflow_ellipsis"> {{ JSK.formatNumberWithCommas(item.num) }}</span>
</li>
</ul>
<div v-if="!streetClassPersonnelList_basic || streetClassPersonnelList_basic.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">
暂无数据
</div>
</el-scrollbar> -->
<ul
style="width: 100%; margin-top: 20px; max-height: 120px; overflow: hidden"
:class="['barChart', streetClassPersonnelList_basic.length == 1 ? 'oneRow' : streetClassPersonnelList_basic.length < 5 ? 'static' : '']"
>
<Vue3SeamlessScroll hover-stop="true" :list="streetClassPersonnelList_basic" hover limitScrollNum="5" :step="0.1">
<li class="row" v-for="(item, index) in streetClassPersonnelList_basic" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.num)">
<span class="lable text_overflow_ellipsis" style="width: 70px; min-width: 70px; margin-right: 5px">{{ item.name }}</span>
<div class="myProgress myProgress1 color2" style="width: 128px; min-width: 128px; margin-right: 8px">
<span class="inner inner_color4" :style="{ width: (item.percent * 100).toFixed(2) + '%' }"></span>
</div>
<span class="color2" style="width: 48px; margin-right: 8px; min-width: 48px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
<span class="proportionValue color2 text_overflow_ellipsis"> {{ JSK.formatNumberWithCommas(item.num) }}</span>
</li>
</Vue3SeamlessScroll>
</ul>
<div v-if="!streetClassPersonnelList_basic || streetClassPersonnelList_basic.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">
暂无数据
</div>
</div>
<!-- 重点人员 -->
<div>
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>重点人员
</div>
<div class="flex" style="margin-top: 20px">
<p style="margin-right: 20px">
在管人员:<span class="color2" style="font-weight: 600">{{ JSK.formatNumberWithCommas(inManagementPersonnel) }}</span>
</p>
<p class="flex items-center">
总数本周新增<i
:class="['iconfont', addPersonnel >= 0 ? 'icon-shangzhang' : 'icon-xiajiang']"
:style="{ fontSize: '10px', margin: ' 0 2px 0 6px', color: addPersonnel >= 0 ? '#1FC61E' : 'red' }"
/><span :style="{ color: addPersonnel >= 0 ? '#1FC61E' : 'red' }">{{ Math.abs(addPersonnel) }}</span>
</p>
</div>
<div class="flex justify-between" style="margin-top: 20px">
<el-radio-group v-model="dateClassKeyVal" size="small" class="sidebar_radio_group" @change="getLocationAggregationStatisticsFocus">
<el-radio-button v-for="item in dateClass" :key="item" :label="item.value">{{ item.label }}</el-radio-button>
</el-radio-group>
<el-select v-model="keyPersonnelVal" style="width: 80px" class="sidebar_select" @change="getLocationAggregationStatisticsFocus">
<el-option v-for="item in keyPersonnelClass" :key="item.enums" :label="item.name" :value="item.enums" />
</el-select>
</div>
<!-- <el-scrollbar height="106px" style="margin-top: 20px">
<ul style="width: 100%" class="barChart">
<li class="row" v-for="(item, index) in streetClassPersonnelList_key" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.num)">
<span class="lable text_overflow_ellipsis" style="width: 70px; min-width: 70px; margin-right: 5px">{{ item.name }}</span>
<div class="myProgress myProgress1 color3" style="width: 128px; min-width: 128px; margin-right: 8px">
<span class="inner inner_color2" :style="{ width: (item.percent * 100).toFixed(2) + '%' }"></span>
</div>
<span class="color3" style="width: 48px; margin-right: 8px; min-width: 48px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
<span class="proportionValue color3 text_overflow_ellipsis"> {{ JSK.formatNumberWithCommas(item.num) }}</span>
</li>
</ul>
<div v-if="!streetClassPersonnelList_key || streetClassPersonnelList_key.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">
暂无数据
</div>
</el-scrollbar> -->
<ul
style="width: 100%; margin-top: 20px; max-height: 120px; overflow: hidden"
:class="['barChart', streetClassPersonnelList_key.length == 1 ? 'oneRow' : streetClassPersonnelList_key.length < 5 ? 'static' : '']"
>
<Vue3SeamlessScroll hover-stop="true" :list="streetClassPersonnelList_key" hover limitScrollNum="5" :step="0.1">
<li class="row" v-for="(item, index) in streetClassPersonnelList_key" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.num)">
<span class="lable text_overflow_ellipsis" style="width: 70px; min-width: 70px; margin-right: 5px">{{ item.name }}</span>
<div class="myProgress myProgress1 color3" style="width: 128px; min-width: 128px; margin-right: 8px">
<span class="inner inner_color2" :style="{ width: (item.percent * 100).toFixed(2) + '%' }"></span>
</div>
<span class="color3" style="width: 48px; margin-right: 8px; min-width: 48px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
<span class="proportionValue color3 text_overflow_ellipsis"> {{ JSK.formatNumberWithCommas(item.num) }}</span>
</li>
</Vue3SeamlessScroll>
</ul>
<div v-if="!streetClassPersonnelList_key || streetClassPersonnelList_key.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">
暂无数据
</div>
</div>
<!-- 重点人员占比统计 -->
<div>
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>重点人员占比统计
</div>
<div class="flex" style="padding: 12px 0">
<v-chart
ref="keyPersonnelStatisticsRef"
:class="['schart', 'keyPersonnelStatisticsChart', actionIndex < 0 ? 'noAfter' : '']"
@mouseover="keyPersonnelStatistics_mouseover"
:option="keyPersonnelStatisticsOptions"
:style="{ width: '94px', height: '74px', minWidth: '94px', marginLeft: '-4px' }"
></v-chart>
<div style="margin-left: 4px">
<p :class="['color' + ((actionIndex % 6) + 1)]" style="margin-bottom: 2px">{{ keyPersonnelStatistics_crrentData.name }}</p>
<p style="margin-bottom: 2px">
总数:<span :class="['color' + ((actionIndex % 6) + 1)]">{{ JSK.formatNumberWithCommas(keyPersonnelStatistics_crrentData.num) }}</span>
</p>
<p style="margin-bottom: 2px; white-space: nowrap">
本市占比:<span :class="['color' + ((actionIndex % 6) + 1)]" style="margin-right: 8px; letter-spacing: -0.5px">{{
(keyPersonnelStatistics_crrentData.cityPercent * 100).toFixed(2) + "%"
}}</span
>外市占比:<span :class="['color' + ((actionIndex % 6) + 1)]" style="letter-spacing: -0.5px">{{ (keyPersonnelStatistics_crrentData.noCityPercent * 100).toFixed(2) + "%" }}</span>
</p>
<p class="flex items-center">
总数本周新增
<i
:class="['iconfont', keyPersonnelStatistics_crrentData.weekIncrNum >= 0 ? 'icon-shangzhang' : 'icon-xiajiang']"
:style="{ fontSize: '10px', margin: ' 0 2px 0 6px', color: keyPersonnelStatistics_crrentData.weekIncrNum >= 0 ? '#1FC61E' : 'red' }"
/><span :style="{ color: keyPersonnelStatistics_crrentData.weekIncrNum >= 0 ? '#1FC61E' : 'red' }">{{ Math.abs(keyPersonnelStatistics_crrentData.weekIncrNum) }}</span>
</p>
</div>
</div>
<ul class="ageGroup">
<li style="cursor: pointer" v-for="(item, index) in keyPersonnelStatistics" :key="item.name" @mouseover="keyPersonnelStatisticsList_mouseover(index)">
<span :class="['colorBlock', 'bg' + ((index % 6) + 1)]"></span
><span style="width: 130px; margin-right: 10px" :class="['text_overflow_ellipsis', actionIndex == index ? 'color' + ((index % 6) + 1) : '']">{{ item.name }}</span>
<span :class="['color' + ((index % 6) + 1)]" style="width: 48px; margin-right: 13px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
<span :class="['color' + ((index % 6) + 1)]" style="width: 52px" class="text_overflow_ellipsis">{{ JSK.formatNumberWithCommas(item.num) }}</span>
</li>
</ul>
</div>
<!-- 严重精神患者数据统计分析 -->
<div>
<div style="margin-top: 20px">严重精神患者数据统计分析</div>
<div class="flex justify-between" style="margin-top: 13px">
<el-radio-group v-model="dateClassKey_serious" size="small" class="sidebar_radio_group" @change="getPsychosisAggregationStatistics">
<el-radio-button v-for="item in dateClass" :key="item" :label="item.value">{{ item.label }}</el-radio-button>
</el-radio-group>
<el-select v-model="keyPersonnelVal_serious" style="width: 80px" class="sidebar_select" @change="getPsychosisAggregationStatistics">
<el-option v-for="item in keyPersonnelClass_serious" :key="item.enums" :label="item.name" :value="item.enums" />
</el-select>
</div>
<v-chart ref="seriousPersonnelChartRef" class="schart" :option="seriousPersonnelOptions" :style="{ width: '296px', height: '170px' }"></v-chart>
</div>
<div style="height: 20px"></div>
</div>
</template>
<script setup>
import { onMounted, reactive, ref, nextTick, watch, defineProps, defineExpose, computed } from "vue";
import { Vue3SeamlessScroll } from "vue3-seamless-scroll";
import { CaretRight } from "@element-plus/icons-vue";
import JSK from "@/utils/JSK";
import { categoryStatisticsPeople, locationAggregationStatistics, locationAggregationStatisticsFocus, percentAggregationStatistics, psychosisAggregationStatistics } from "@/api/map_sidebar.js";
const props = defineProps({
sidebarType: "",
data: {
type: Object,
default: {},
},
orgData: {
type: Object,
default: {},
},
});
const dateClass = [
{ label: "近一周", value: "week" },
{ label: "近一月", value: "month" },
{ label: "近一季", value: "quarter" },
{ label: "近一年", value: "year" },
];
const residentialClass = window.$dicts.getEnums("ResidentialClass");
const keyPersonnelClass = window.$dicts.getEnums("KeyPersonnelClass");
const keyPersonnelClass_serious = window.$dicts.getEnums("KeyPersonnelClassSerious");
const populationChartRef = ref(null);
const keyPersonnelStatisticsRef = ref(null);
const seriousPersonnelChartRef = ref(null);
const dateClassKey = ref("week"); //基础人员 周月季年关键字
const residentialClassKey = ref("01"); //基础人员 居住分类关键字
// const streetClassPersonnel_total = ref(0); //基础人员 按街道分类总数
const keyPersonnelVal = ref("01"); //重点人员 分类关键字
const dateClassKeyVal = ref("week"); //重点人员 周月季年关键字
// const keyPersonne_total = ref(0); //重点人员 按街道分类总数
// let keyPersonnelStatisticsData = []; //重点人员占比统计 饼图数据
const dateClassKey_serious = ref("week"); //严重精神患者人员 日周月年关键字
const keyPersonnelVal_serious = ref("01"); //严重精神患者人员 分类关键字
// 基础人员 - 按街道分类人员列表
// const streetClassPersonnelList_basic = computed(() => {
// let res = props.data.basicPersonnel.streetClassPersonnelList
// .filter((item) => item.type == residentialClassKey.value)
// .map((item) => {
// if (dateClassKey.value == 1) {
// return {
// label: item.label,
// num: item.weekNum,
// };
// } else if (dateClassKey.value == 2) {
// return {
// label: item.label,
// num: item.monthNum,
// };
// } else if (dateClassKey.value == 3) {
// return {
// label: item.label,
// num: item.quarterNum,
// };
// } else {
// return {
// label: item.label,
// num: item.yearNum,
// };
// }
// });
// // 基础人员总数
// streetClassPersonnel_total.value = res.reduce((prev, cur) => {
// return prev + cur.num;
// }, 0);
// return res;
// });
// 重点人员 - 按街道分类人员列表
// const streetClassPersonnelList_key = computed(() => {
// let res = props.data.keyPersonnel.streetClassPersonnelList
// .filter((item) => item.type == keyPersonnelVal.value)
// .map((item) => {
// if (dateClassKeyVal.value == 1) {
// return {
// label: item.label,
// num: item.weekNum,
// };
// } else if (dateClassKeyVal.value == 2) {
// return {
// label: item.label,
// num: item.monthNum,
// };
// } else if (dateClassKeyVal.value == 3) {
// return {
// label: item.label,
// num: item.quarterNum,
// };
// } else {
// return {
// label: item.label,
// num: item.yearNum,
// };
// }
// });
// // 管控人员总数
// keyPersonne_total.value = res.reduce((prev, cur) => {
// return prev + cur.num;
// }, 0);
// return res;
// });
// 重点人员占比统计-总数
// const keyPersonnelStatistics_total = computed(() => {
// return props.data.keyPersonnelStatistics.reduce((prev, cur) => {
// return prev + cur.num;
// }, 0);
// });
// 严重精神患者数据统计分析-折线图数据
// const setOption_seriousPersonnelChart = () => {
// seriousPersonnelChartRef.value &&
// seriousPersonnelChartRef.value.setOption({
// xAxis: { data: props.data.seriousPersonnel[dateClassKey_serious.value].filter((item) => item.type == keyPersonnelVal_serious.value).map((item) => item.time) },
// series: {
// data: props.data.seriousPersonnel[dateClassKey_serious.value].filter((item) => item.type == keyPersonnelVal_serious.value).map((item) => item.num),
// },
// });
// };
const basicPersonnelTotal = ref(0); //基础人员 总数
const residentialPersonnelList = ref([]); //基础人员 饼图数据
const streetClassPersonnelList_basic = ref([]); //基础人员 按人员流动分类获取列表数据
// 基础人员-按人员流动分类获取列表数据
const getGtreetClassPersonnelList_basic = () => {
locationAggregationStatistics(props.orgData.key, dateClassKey.value, residentialClassKey.value).then((res) => {
streetClassPersonnelList_basic.value = res.data;
});
};
const streetClassPersonnelList_key = ref([]); //重点人员列表数据
const inManagementPersonnel = ref(0); //重点人员-在管人员
const addPersonnel = ref(0); //重点人员-总数本周新增
// 获取重点人员列表数据
const getLocationAggregationStatisticsFocus = () => {
locationAggregationStatisticsFocus(props.orgData.key, dateClassKeyVal.value, keyPersonnelVal.value).then((res) => {
streetClassPersonnelList_key.value = res.data.data || [];
inManagementPersonnel.value = res.data.inChargeNum || 0;
addPersonnel.value = res.data.weekIncrNum || 0;
});
};
// 重点人员占比统计
const keyPersonnelStatistics = ref([]); //重点人员列表数据
// 严重精神患者数据统计
const getPsychosisAggregationStatistics = () => {
psychosisAggregationStatistics(props.orgData.key, dateClassKey_serious.value, keyPersonnelVal_serious.value).then((res) => {
let dataZoom;
if (res.data.length > 12) {
dataZoom = [
{
type: "slider",
show: true,
left: "9%",
bottom: 8,
startValue: 0,
endValue: 12,
handleSize: 0, //两侧手柄大小
showDetail: false,
height: 0,
borderColor: "transparent",
showDataShadow: false,
minValueSpan: 12,
maxValueSpan: 12,
},
// { type: "inside", show: false, disabled: false },
];
} else {
dataZoom = [
{ type: "slider", show: false, disabled: true, endValue: res.data.length, bottom: 0 },
// { type: "inside", show: false, disabled: true },
];
}
seriousPersonnelChartRef.value &&
seriousPersonnelChartRef.value.setOption({
dataZoom,
xAxis: { data: res.data.map((item) => item.hour || item.date || item.month) },
series: {
data: res.data.map((item) => item.num),
},
});
});
};
props.orgData.key;
watch(
() => props.orgData,
(val) => {
if (val && props.sidebarType == "headcount") {
// 基础人员-饼图数据
categoryStatisticsPeople(props.orgData.key).then((res) => {
basicPersonnelTotal.value = res.data.total;
residentialPersonnelList.value = res.data.detail;
populationChartRef.value &&
populationChartRef.value.setOption({
series: [
{
data: residentialPersonnelList.value.map((item) => {
return { value: item.num };
}),
},
],
});
});
// 基础人员-按人员流动分类获取列表数据
getGtreetClassPersonnelList_basic();
// 获取重点人员列表数据
getLocationAggregationStatisticsFocus();
// 重点人员占比统计
percentAggregationStatistics(props.orgData.key).then((res) => {
if (res.data && res.data.length > 0) {
actionIndex.value = 0;
keyPersonnelStatistics.value = res.data.map((item) => Object.assign(item, { value: item.num }));
keyPersonnelStatisticsRef.value &&
keyPersonnelStatisticsRef.value.setOption({
series: [
{
data: keyPersonnelStatistics.value,
},
],
});
keyPersonnelStatisticsRef.value.dispatchAction({
type: "highlight",
seriesIndex: 0,
dataIndex: actionIndex.value,
});
keyPersonnelStatistics_crrentData.value = keyPersonnelStatistics.value[0];
drawLinesFromCenterToTarget();
}
});
// 严重精神患者数据统计
getPsychosisAggregationStatistics();
}
},
// immediate: true
{ deep: true, immediate: true }
);
watch(
() => props.data,
(val) => {
if (val && props.sidebarType == "headcount") {
nextTick(() => {
// 基础人员-饼图数据
// populationChartRef.value &&
// populationChartRef.value.setOption({
// series: [
// {
// data: val.basicPersonnel.residentialPersonnelList.map((item) => {
// return { value: item.value };
// }),
// },
// ],
// });
//重点人员占比统计-饼图数据
// keyPersonnelStatisticsData = val.keyPersonnelStatistics.map((item) => {
// return { ...item, value: item.num, name: item.label };
// });
// keyPersonnelStatisticsRef.value &&
// keyPersonnelStatisticsRef.value.setOption({
// series: [
// {
// data: keyPersonnelStatisticsData,
// },
// ],
// });
// keyPersonnelStatisticsRef.value.dispatchAction({
// type: "highlight",
// seriesIndex: 0,
// dataIndex: actionIndex.value,
// });
// drawLinesFromCenterToTarget();
// keyPersonnelStatistics_crrentData.value = keyPersonnelStatisticsData[0];
// 严重精神患者数据统计分析-折线图数据
// setOption_seriousPersonnelChart();
});
}
},
// immediate: true
{ deep: true, immediate: true }
);
// 基础人员-饼图配置
const populationChartOptions = ref({
tooltip: {
show: false,
trigger: "item",
},
color: ["#5C7CEA", "#00bac5", "#d098cb", "#fd742f", "#ffaa39"],
series: [
{
type: "pie",
radius: ["54%", "86%"],
center: ["50%", "50%"],
itemStyle: {
borderRadius: 0,
borderColor: "#fff",
borderWidth: 0,
},
label: {
show: false,
},
emphasis: {
// scale: false, //禁止触摸缩放
},
labelLine: {
show: false,
},
data: [],
},
],
});
const populationChartSelectIndex = ref(-1); //基础人员-饼图被选中index
// 基础人员-饼图-鼠标进入事件
const populationChart_mousemove = (param) => {
console.log(param);
populationChartSelectIndex.value = param.dataIndex;
};
// 基础人员-饼图-鼠标离开事件
const populationChart_mouseout = () => {
populationChartSelectIndex.value = -1;
};
// 重点人员占比统计-饼图配置
const keyPersonnelStatisticsOptions = ref({
tooltip: {
show: false,
},
color: ["#5c7cea", "#00bac5", "#d098cb", "#fd742f", "#ffaa39", "#405ae9"],
series: [
{
type: "pie",
radius: ["54%", "86%"],
center: ["40%", "50%"],
avoidLabelOverlap: false,
label: {
show: false,
},
// 禁用默认引导线
labelLine: {
show: false,
},
data: [],
},
],
});
// 严重精神患者数据统计分析-折线图
const seriousPersonnelOptions = ref({
grid: {
top: "20%",
left: "5%",
right: "1%",
bottom: "1%",
containLabel: true,
},
tooltip: {
trigger: "axis",
extraCssText: "width:auto;height:auto",
valueFormatter: function (value) {
return value + "人";
},
},
xAxis: {
type: "category",
// boundaryGap: false,
axisLabel: { color: "#fff", fontSize: 9, interval: 0, rotate: 40 },
axisLine: {
show: true,
lineStyle: {
// 使用深浅的间隔色
color: "#6C8097",
},
},
axisTick: {
alignWithLabel: true,
},
data: [],
},
yAxis: {
name: "单位(人)",
nameTextStyle: {
fontSize: 10,
padding: [2, -5, -2, 5],
},
// axisLine: {
// show: false,
// },
// axisTick: {
// show: false,
// },
splitLine: {
show: true, // 先隐藏默认的分割线
lineStyle: {
// 使用深浅的间隔色
color: "rgba(108,128,151,0.3)",
},
},
axisLabel: {
color: "#999",
fontSize: 10,
margin: 3,
padding: [0, 0, 10, 0],
},
},
series: {
data: [],
type: "line",
symbolSize: 0,
itemStyle: {
color: "#FFAA39",
},
emphasis: {
focus: "series",
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: "#FFAA39",
},
{
offset: 0.9,
color: "rgba(255,170,57,0)",
},
]),
},
},
});
/*
监听高亮事件
重点人员占比统计
*/
const keyPersonnelStatistics_mouseover = (param) => {
console.log(param);
console.log(keyPersonnelStatisticsRef.value);
if (actionIndex.value == param.dataIndex) return;
// 取消高亮模块
keyPersonnelStatisticsRef.value.dispatchAction({
type: "downplay",
seriesIndex: 0,
dataIndex: actionIndex.value,
});
// 设置高亮模块
actionIndex.value = param.dataIndex;
keyPersonnelStatisticsRef.value.dispatchAction({
type: "highlight",
seriesIndex: 0,
dataIndex: actionIndex.value,
});
drawLinesFromCenterToTarget();
keyPersonnelStatistics_crrentData.value = param.data;
};
const keyPersonnelStatisticsList_mouseover = (index) => {
if (actionIndex.value == index) return;
// 取消高亮模块
keyPersonnelStatisticsRef.value.dispatchAction({
type: "downplay",
seriesIndex: 0,
dataIndex: actionIndex.value,
});
// 设置高亮模块
actionIndex.value = index;
keyPersonnelStatisticsRef.value.dispatchAction({
type: "highlight",
seriesIndex: 0,
dataIndex: actionIndex.value,
});
drawLinesFromCenterToTarget();
keyPersonnelStatistics_crrentData.value = keyPersonnelStatistics.value[index];
};
//重点人员占比统计 绘制引导线 开始------------------------------------------------------
let actionIndex = ref(-1); //高亮模块的index
let keyPersonnelStatistics_crrentData = ref({}); //重点人员占比统计 当前选中模块的数据
const targetXSlider = 90; //x位置
const targetYSlider = 10; //y位置
const startOffsetSlider = 45; //起点偏移量
const lineColorPicker = "#fff"; //线条颜色
const lineWidthSlider = 1; //线条宽度
const lineTypeSelect = "solid"; //线条类型
// 绘制从扇区中心指向同一目标点的引导线
function drawLinesFromCenterToTarget() {
// 获取饼图系列
const series = keyPersonnelStatisticsRef.value.getOption().series[0];
const center = series.center || ["40%", "50%"];
const radius = series.radius;
// 转换百分比为像素值
const chartWidth = keyPersonnelStatisticsRef.value.getWidth();
const chartHeight = keyPersonnelStatisticsRef.value.getHeight();
let centerX, centerY;
if (typeof center[0] === "string") {
centerX = (parseFloat(center[0]) / 100) * chartWidth;
} else {
centerX = center[0];
}
if (typeof center[1] === "string") {
centerY = (parseFloat(center[1]) / 100) * chartHeight;
} else {
centerY = center[1];
}
let outerRadius;
if (typeof radius === "string") {
outerRadius = (Math.min(chartWidth, chartHeight) * parseFloat(radius)) / 100;
} else if (Array.isArray(radius)) {
outerRadius = (Math.min(chartWidth, chartHeight) * parseFloat(radius[1])) / 100;
} else {
outerRadius = (Math.min(chartWidth, chartHeight) * parseFloat(radius)) / 200;
}
// 计算每个扇区的角度
const total = keyPersonnelStatistics.value.reduce((sum, item) => sum + item.value, 0);
let startAngle = -Math.PI / 2; // 从顶部开始
const angles = [];
let isAllZero = false;
// 判断数据是否都为0
let arr = keyPersonnelStatistics.value.filter((item) => item.value == 0);
if (arr.length == keyPersonnelStatistics.value.length) {
isAllZero = true;
}
if (isAllZero) {
keyPersonnelStatistics.value.forEach((item) => {
const angle = (1 / keyPersonnelStatistics.value.length) * Math.PI * 2;
angles.push({
start: startAngle,
end: startAngle + angle,
mid: startAngle + angle / 2,
});
startAngle += angle;
});
} else {
keyPersonnelStatistics.value.forEach((item) => {
const angle = (item.value / total) * Math.PI * 2;
angles.push({
start: startAngle,
end: startAngle + angle,
mid: startAngle + angle / 2,
});
startAngle += angle;
});
}
// 计算目标点位置
const targetX = (parseFloat(targetXSlider) / 100) * chartWidth;
const targetY = (parseFloat(targetYSlider) / 100) * chartHeight;
const startOffset = parseFloat(startOffsetSlider);
// 创建图形元素
const graphicElements = [];
// 为每个扇区添加引导线
angles.forEach((angle, index) => {
if (index != actionIndex.value) return;
// 计算起点(扇区内部,根据偏移量)
const startRadius = outerRadius * (startOffset / 100);
const startX = centerX + Math.cos(angle.mid) * startRadius;
const startY = centerY + Math.sin(angle.mid) * startRadius;
// 添加线条
graphicElements.push({
type: "line",
shape: {
x1: startX,
y1: startY,
x2: targetX,
y2: targetY,
},
style: {
stroke: lineColorPicker,
lineWidth: parseFloat(lineWidthSlider),
lineDash: lineTypeSelect === "dashed" ? [5, 5] : lineTypeSelect === "dotted" ? [2, 2] : null,
},
zlevel: 5,
});
// 添加起点标记
graphicElements.push({
type: "circle",
shape: {
cx: startX,
cy: startY,
r: 2,
},
style: {
fill: lineColorPicker,
},
zlevel: 999,
});
});
// 更新图表
keyPersonnelStatisticsRef.value.setOption({
graphic: graphicElements,
});
}
//重点人员占比统计 绘制引导线 结束------------------------------------------------------
// 获取比例
const getProportion = (val, total) => {
if (val && total) {
return ((val / total) * 100).toFixed(2) + "%";
} else {
return 0 + "%";
}
};
defineExpose({});
</script>
<style lang="scss" scoped>
.sidebar_right_headcount {
padding: 0 12px;
}
.title {
font-size: 12px;
color: #00bac5;
line-height: 17px;
display: flex;
align-items: center;
margin-top: 20px;
margin-left: -5px;
}
ul.barChart {
li {
display: flex;
align-items: center;
line-height: 24px;
height: 24px;
overflow: hidden;
}
&.static li {
&:first-child {
line-height: 12px;
height: 17px;
align-items: flex-start;
}
&:last-child {
line-height: 12px;
height: 17px;
align-items: flex-end;
}
}
}
.ageGroup {
flex: 1;
li {
height: 18px;
// width: 208px;
background: rgba(255, 255, 255, 0.05);
position: relative;
display: flex;
align-items: center;
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 3px;
height: 3px;
border: 1px solid rgba(255, 255, 255, 0.5);
border-right: 0;
border-bottom: 0;
}
}
li + li {
margin-top: 4px;
}
}
.colorBlock {
width: 8px;
height: 8px;
margin-left: 15px;
margin-right: 8px;
}
.bg1 {
background: #5c7cea;
}
.bg2 {
background: #00bac5;
}
.bg3 {
background: #d098cb;
}
.bg4 {
background: #fd742f;
}
.bg5 {
background: #ffaa39;
}
.bg6 {
background: #405ae9;
}
.color1 {
color: #5c7cea;
}
.color2 {
color: #00bac5;
}
.color3 {
color: #d098cb;
}
.color4 {
color: #fd742f;
}
.color5 {
color: #ffaa39;
}
.color6 {
color: #405ae9;
}
.myProgress {
display: inline-block;
height: 10px;
background: rgba(255, 255, 255, 0.1);
position: relative;
.inner {
height: 4px;
position: absolute;
top: 3px;
}
.inner_color1 {
background: linear-gradient(270deg, #5c7cea 0%, rgba(92, 124, 234, 0.2) 100%);
}
.inner_color2 {
background: linear-gradient(270deg, #d098cb 0%, rgba(205, 146, 200, 0.2) 100%);
}
.inner_color3 {
background: linear-gradient(270deg, #cd6c27 0%, rgba(205, 108, 39, 0.2) 100%);
}
.inner_color4 {
background: linear-gradient(270deg, #00bac5 0%, rgba(0, 186, 197, 0.15) 100%);
}
}
.myProgress1 {
&::before {
content: "";
position: absolute;
top: -14px;
height: 38px;
border-left: 1px solid #00bac5;
opacity: 0.5;
}
}
.myProgress1.color2::before {
border-left-color: #00bac5;
}
.myProgress1.color3::before {
border-left: 1px solid #d098cb;
}
.keyPersonnelStatisticsChart {
position: relative;
&::after {
content: "";
position: absolute;
width: 20px;
height: 1px;
background: #fff;
top: 7px;
left: 85px;
}
&.noAfter::after {
opacity: 0;
}
}
</style>
<style lang="scss">
.sidebar_radio_group {
.el-radio-button__original-radio:checked + .el-radio-button__inner {
background: rgba(0, 186, 197, 0.2);
border-color: #00bac5 !important;
color: #3ff5ff;
}
.el-radio-button__inner {
padding: 2px 6px;
border-radius: 0px !important;
background: transparent;
border-color: #417174 !important;
color: #417174;
line-height: 12.5px;
}
}
.sidebar_select {
.el-input .el-select__caret {
color: #417174;
}
.el-input__inner,
.el-input,
.el-input__wrapper {
background-color: transparent !important;
border-radius: 0px !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.el-input__wrapper {
padding-left: 6px;
padding-right: 6px;
// background-color: transparent !important;
box-shadow: 0 0 0 1px #417174 inset !important;
&:hover {
box-shadow: 0 0px 0px 0px !important;
}
}
.el-input__inner {
height: 16px;
font-size: 11px;
color: #3ff5ff !important;
}
}
</style>
5、折现、柱状融合图
<!-- 案事件统计详情 -->
<template>
<div class="sidebar_right_caseEvent" v-if="sidebarType == 'caseEvent'">
<!-- 社会治安 -->
<div style="position: relative">
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>社会治安
</div>
<p class="color2 dataTotal" style="position: absolute; top: 0; right: 10px; font-size: 18px">{{ JSK.formatNumberWithCommas(caseEvent_total) }}</p>
<el-table
:data="caseEventTypeList"
style="width: 100%; margin-top: 16px"
:header-cell-style="{
padding: 0,
height: '24px',
lineHeight: '24px',
color: '#3FF5FF',
background: '#205767',
fontSize: '12px',
fontWeight: 'normal',
borderBottom: '2px solid #000',
borderRight: '2px solid #000',
}"
:cell-style="{
padding: 0,
height: '24px',
lineHeight: '24px',
fontSize: '12px',
color: '#fff',
borderBottom: '2px solid #000',
borderRight: '2px solid #000',
}"
:row-style="setRowStyle"
class="orgDataTable"
>
<el-table-column prop="policeSituation" label="类型" show-overflow-tooltip
><template #default="scope">
<span>{{ scope.row.policeSituation == 1 ? "警情" : "非警情" }}</span>
</template>
</el-table-column>
<el-table-column prop="total" label="数量" align="center" show-overflow-tooltip />
<el-table-column prop="increment" label="本月新增数" align="center" show-overflow-tooltip />
</el-table>
</div>
<!-- 矛盾纠纷数据分析 -->
<div>
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>矛盾纠纷数据分析
</div>
<v-chart style="margin-top: 13px" ref="caseEventCompareChartRef" class="schart" :option="caseEventCompareChartOptions" :style="{ width: '290px', height: '188px' }"></v-chart>
</div>
<!-- 矛盾纠纷化解情况 -->
<div>
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>矛盾纠纷化解情况
</div>
<div class="flex justify-between" style="margin-top: 14px">
<el-radio-group v-model="dateClassKey_conflictResolution" size="small" class="sidebar_radio_group" @change="setOption_conflictResolutionChart">
<el-radio-button v-for="item in dateClass" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button>
</el-radio-group>
<el-select v-model="contradictionTypeVal" style="width: 80px" class="sidebar_select" @change="setOption_conflictResolutionChart">
<el-option v-for="item in contradictionType" :key="item.enums" :label="item.name" :value="item.enums" />
</el-select>
</div>
<v-chart style="margin-top: 13px" ref="conflictResolutionChartRef" class="schart" :option="conflictResolutionChartOptions" :style="{ width: '290px', height: '188px' }"></v-chart>
</div>
<!-- 矛盾纠纷占比及下级分类占比 -->
<div>
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>矛盾纠纷占比及下级分类占比
</div>
<div class="flex" style="align-items: center; margin-top: 18px; position: relative">
<el-popover :offset="0" effect="dark" :visible="popoverVisible" :width="290" placement="bottom-start" :show-arrow="false" trigger="hover" popper-class="subContradictionRatioPopper">
<template #reference>
<!-- 矛盾纠纷占比 饼图 -->
<v-chart
ref="contradictionRatioChartRef"
class="schart"
:option="contradictionRatioChartOptions"
:style="{ width: '74px', height: '74px' }"
@mousemove="contradictionRatioChart_mousemove"
@globalout="contradictionRatioChart_globalout"
></v-chart>
</template>
<!-- 下级分类占比 饼图 -->
<div>
<div class="title" style="color: #fff; margin-top: 0">
<el-icon class="CaretRight"><CaretRight /></el-icon>矛盾纠纷占比及下级分类占比
</div>
<v-chart ref="subContradictionRatioChartRef" class="schart" :option="subContradictionRatioChartOptions" :style="{ width: '266px', height: '137px' }"></v-chart>
</div>
</el-popover>
<ul class="ageGroup">
<li v-for="(item, index) in contradictionRatioList" :key="item.name">
<span :class="['colorBlock', 'bg' + ((index % 6) + 1)]"></span
><span style="width: 72px; margin-right: 10px" :class="['text_overflow_ellipsis', index == contradictionRatioChartSelectIndex ? 'color' + ((index % 6) + 1) : '']">{{ item.name }}</span>
<span :class="['color' + ((index % 6) + 1)]" style="width: 48px; margin-right: 13px">{{ (item.proportion * 100).toFixed(2) + "%" }}</span>
<span :class="['color' + ((index % 6) + 1)]" style="width: 48px" class="text_overflow_ellipsis">{{ JSK.formatNumberWithCommas(item.total) }}</span>
</li>
</ul>
</div>
</div>
<!-- 次生、升级、反复 -->
<div>
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>次生、升级、反复
</div>
<p style="margin-top: 16px">
次生、升级、反复占比: <span class="color5">{{ caseUp_proportion }}</span>
</p>
<p style="margin: 6px 0 12px 0">
总数本周新增
<i
:class="['iconfont', caseUp_WeekAdd >= 0 ? 'icon-shangzhang' : 'icon-xiajiang']"
:style="{ fontSize: '10px', margin: ' 0 2px 0 6px', color: caseUp_WeekAdd >= 0 ? '#1FC61E' : 'red' }"
/><span :style="{ color: caseUp_WeekAdd >= 0 ? '#1FC61E' : 'red' }">{{ Math.abs(caseUp_WeekAdd) }}</span>
</p>
<el-row :gutter="2" class="scroll-title">
<el-col v-for="item in caseUp_columns" :key="item.prop" :span="item.span"
><span :style="{ textAlign: item.align }">{{ item.label }}</span>
</el-col>
</el-row>
<div style="width: 100%; max-height: 125px; overflow: hidden; margin-top: 2px">
<Vue3SeamlessScroll hover-stop="true" :list="caseUp_List" hover limitScrollNum="5" :step="0.1">
<el-row v-for="item in caseUp_List" :key="item.uuid" :gutter="2" class="scroll-item">
<el-col v-for="item1 in caseUp_columns" :key="item1.prop" :span="item1.span"
><span class="text-overflow" :style="{ textAlign: item1.align }" :title="item1.prop == 'eventTime' ? $filters.time(item[item1.prop], 'yyyy.MM.dd') : item[item1.prop]">{{
item1.prop == "eventTime" ? $filters.time(item[item1.prop], "yyyy.MM.dd") : item[item1.prop] || "-"
}}</span>
</el-col>
</el-row>
</Vue3SeamlessScroll>
<div v-if="!caseUp_List || caseUp_List.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
<!-- <el-table
:data="caseUp_List"
style="width: 100%"
:header-cell-style="{
padding: 0,
height: '24px',
lineHeight: '24px',
color: '#3FF5FF',
background: '#205767',
fontSize: '12px',
fontWeight: 'normal',
borderBottom: '2px solid #000',
borderRight: '2px solid #000',
}"
:cell-style="{
padding: 0,
height: '24px',
lineHeight: '24px',
fontSize: '12px',
color: '#fff',
borderBottom: '2px solid #000',
borderRight: '2px solid #000',
}"
:row-style="setRowStyle"
:show-header="false"
class="orgDataTable"
>
<el-table-column prop="eventTime" label="日期" show-overflow-tooltip>
<template #default="scope">
<span>{{ $filters.time(scope.row.eventTime, "yyyy.MM.dd") || "-" }}</span>
</template>
</el-table-column>
<el-table-column prop="eventStage" label="事件阶段" align="center" show-overflow-tooltip width="53px">
<template #default="scope">
<span>{{ scope.row.eventStage || "-" }}</span>
</template>
</el-table-column>
<el-table-column prop="directExpression" label="直接表现" align="center" show-overflow-tooltip width="53px">
<template #default="scope">
<span>{{ scope.row.directExpression || "-" }}</span>
</template>
</el-table-column>
<el-table-column prop="secondaryImpact" label="次生影响" align="center" show-overflow-tooltip width="53px">
<template #default="scope">
<span>{{ scope.row.secondaryImpact || "-" }}</span>
</template>
</el-table-column>
<el-table-column prop="responseMeasure" label="应对措施" align="center" show-overflow-tooltip width="53px">
<template #default="scope">
<span>{{ scope.row.responseMeasure || "-" }}</span>
</template>
</el-table-column>
</el-table> -->
</div>
</div>
<!-- 高频高发事件Top3 -->
<div>
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>高频高发事件Top3
</div>
<!-- <el-scrollbar height="106px" style="margin-top: 20px"> -->
<ul
style="width: 100%; margin-top: 20px; max-height: 120px; overflow: hidden"
:class="['barChart', highFrequencyEventsList.length == 1 ? 'oneRow' : highFrequencyEventsList.length < 5 ? 'static' : '']"
>
<Vue3SeamlessScroll hover-stop="true" :list="highFrequencyEventsList" hover limitScrollNum="5" :step="0.1">
<li class="row" v-for="(item, index) in highFrequencyEventsList" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.total)">
<span class="lable text_overflow_ellipsis" style="width: 80px; min-width: 80px; margin-right: 5px">{{ item.name }}</span>
<div class="myProgress myProgress1 color1" style="width: 150px; min-width: 150px; margin-right: 8px">
<span class="inner inner_color1" :style="{ width: getProportion(item.total, highFrequencyEvents_total) }"></span>
</div>
<span class="proportionValue text_overflow_ellipsis" style="color: #7f94ff"> {{ JSK.formatNumberWithCommas(item.total) }}</span>
</li>
</Vue3SeamlessScroll>
</ul>
<div v-if="!highFrequencyEventsList || highFrequencyEventsList.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
<!-- </el-scrollbar> -->
</div>
<!-- 各街镇矛盾纠纷发生数 -->
<div>
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>各街镇矛盾纠纷发生数
</div>
<ul
style="width: 100%; margin-top: 20px; max-height: 120px; overflow: hidden"
:class="['barChart', streetTownStatisticsList.length == 1 ? 'oneRow' : streetTownStatisticsList.length < 5 ? 'static' : '']"
>
<Vue3SeamlessScroll hover-stop="true" :list="streetTownStatisticsList" hover limitScrollNum="5" :step="0.1">
<li class="row" v-for="(item, index) in streetTownStatisticsList" :key="index" :title="item.name + JSK.formatNumberWithCommas(item.num)">
<span class="lable text_overflow_ellipsis" style="width: 70px; min-width: 70px; margin-right: 5px">{{ item.name }}</span>
<div class="myProgress myProgress1 color2" style="width: 128px; min-width: 128px; margin-right: 8px">
<span class="inner inner_color4" :style="{ width: (item.percent * 100).toFixed(2) + '%' }"></span>
</div>
<span class="color2" style="width: 48px; margin-right: 8px; min-width: 48px">{{ (item.percent * 100).toFixed(2) + "%" }}</span>
<span class="proportionValue color2 text_overflow_ellipsis"> {{ JSK.formatNumberWithCommas(item.num) }}</span>
</li>
</Vue3SeamlessScroll>
</ul>
<div v-if="!streetTownStatisticsList || streetTownStatisticsList.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
</div>
<!-- 矛盾纠纷涉重点人员 -->
<div>
<div class="title">
<el-icon class="CaretRight"><CaretRight /></el-icon>矛盾纠纷涉重点人员
</div>
<div class="flex justify-between" style="margin-top: 14px">
<el-radio-group v-model="dateClassKey_focusPeopleStatistics" size="small" class="sidebar_radio_group" @change="getFocusPeopleStatisticsList">
<el-radio-button v-for="item in dateClass" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button>
</el-radio-group>
<el-select v-model="focusPeopleStatisticsTypeVal" style="width: 80px" class="sidebar_select" @change="getFocusPeopleStatisticsList">
<el-option v-for="item in focusPeopleStatisticsType" :key="item.enums" :label="item.name" :value="item.enums" />
</el-select>
</div>
<v-chart style="margin-top: 13px" ref="focusPeopleStatisticsChartRef" class="schart" :option="focusPeopleStatisticsChartOptions" :style="{ width: '290px', height: '160px' }"></v-chart>
</div>
<div style="height: 20px"></div>
</div>
</template>
<script setup>
import { onMounted, reactive, ref, nextTick, watch, defineProps, defineExpose, computed } from "vue";
import { CaretRight } from "@element-plus/icons-vue";
import JSK from "@/utils/JSK";
import {
policeSituationNum,
yearOnYearStatistics,
resolveSituation,
contradictionRatio,
subContradictionRatio,
secondaryOrUpgradeStatistics,
topN,
streetTownStatistics,
focusPeopleStatistics,
} from "@/api/map_sidebar.js";
import cloneDeep from "lodash/cloneDeep";
import { Vue3SeamlessScroll } from "vue3-seamless-scroll";
const props = defineProps({
sidebarType: "",
// data: {
// type: Object,
// default: {},
// },
orgData: {
type: Object,
default: {},
},
});
const dateClass = [
{ label: "近一周", value: "week" },
{ label: "近一月", value: "month" },
{ label: "近一季", value: "quarter" },
{ label: "近一年", value: "year" },
];
// 矛盾分类列表
const contradictionType = window.$dicts.getEnums("CaseEvent");
// 矛盾纠纷化解情况 日周月年关键字
const dateClassKey_conflictResolution = ref("week");
const contradictionTypeVal = ref("01");
const caseEventCompareChartRef = ref(null);
const conflictResolutionChartRef = ref(null);
const contradictionRatioChartRef = ref(null);
const subContradictionRatioChartRef = ref(null);
// 社会治安 总数
const caseEvent_total = computed(() => {
return caseEventTypeList.value.reduce((prev, cur) => {
return prev + cur.total;
}, 0);
});
// 高频高发事件 总数
const highFrequencyEvents_total = computed(() => {
return highFrequencyEventsList.value.reduce((prev, cur) => {
return prev + cur.total;
}, 0);
});
// 矛盾纠纷 总数
// const contradictionRatio_total = computed(() => {
// return props.data.contradictionRatioList.reduce((prev, cur) => {
// return prev + cur.value;
// }, 0);
// });
let caseEventTypeList = ref([]); //社会治安列表
let contradictionRatioList = ref([]); //矛盾纠纷占比列表
let caseUp_proportion = ref(""); // 次生、升级、反复占比
let caseUp_WeekAdd = ref(0); //次生、升级、反复 总数本周新增
let caseUp_List = ref([]); //次生、升级、反复 列表
let highFrequencyEventsList = ref([]); //高频高发事件Top3 列表
// 次生、升级、反复 表头列表
const caseUp_columns = [
{ prop: "eventTime", label: "日期", align: "left", span: 8 },
{ prop: "eventStage", label: "事件阶段", align: "center", span: 4 },
{ prop: "directExpression", label: "直接表现", align: "center", span: 4 },
{ prop: "secondaryImpact", label: "次生影响", align: "center", span: 4 },
{ prop: "responseMeasure", label: "应对措施", align: "center", span: 4 },
];
const conflictResolutionType = [
{ label: "化解量", key: "resolved" },
{ label: "未化解量", key: "unresolved" },
{ label: "化解率", key: "resolutionRate" },
{ label: "平均化解率", key: "avgResolutionRate" },
];
// 矛盾纠纷化解情况(折线、柱状图)
const setOption_conflictResolutionChart = () => {
resolveSituation(props.orgData.key, dateClassKey_conflictResolution.value, contradictionTypeVal.value).then((res) => {
let dataZoom;
if (res.data.length > 12) {
dataZoom = [
// { type: "slider", show: false, disabled: false, startValue: 0, endValue: 12 },
{
type: "slider",
show: true,
left: "9%",
bottom: 8,
startValue: 0,
endValue: 12,
handleSize: 0, //两侧手柄大小
showDetail: false,
height: 0,
borderColor: "transparent",
showDataShadow: false,
minValueSpan: 12,
maxValueSpan: 12,
},
// { type: "inside", show: false, disabled: false },
];
} else {
dataZoom = [
{ type: "slider", show: false, disabled: true, endValue: res.data.length, bottom: 0 },
// { type: "inside", show: false, disabled: true },
];
}
conflictResolutionChartRef.value &&
conflictResolutionChartRef.value.setOption({
dataZoom,
xAxis: {
data: res.data.map((item) => item.hour || item.date || item.month),
},
series: conflictResolutionType.map((item) => {
let obj = {
name: item.label,
};
obj.data = res.data.map((item1) => {
if (item.key == "resolutionRate" || item.key == "avgResolutionRate") {
return (item1[item.key] * 100).toFixed(2);
} else {
return item1[item.key];
}
});
return obj;
}),
});
});
// let data_ = props.data.conflictResolutionList[dateClassKey_conflictResolution.value];
// conflictResolutionChartRef.value &&
// conflictResolutionChartRef.value.setOption({
// xAxis: {
// data: data_.time,
// },
// series: data_.dataList.map((item) => {
// return { data: item };
// }),
// });
};
// 各街镇矛盾纠纷发生数列表
const streetTownStatisticsList = ref([]);
// 矛盾纠纷涉重点人员 日周月年关键字
const dateClassKey_focusPeopleStatistics = ref("week");
const focusPeopleStatisticsTypeVal = ref("01");
// 矛盾纠纷涉重点人员 分类列表
const focusPeopleStatisticsType = window.$dicts.getEnums("KeyPersonnelClass");
// 获取矛盾纠纷涉重点人员列表
const getFocusPeopleStatisticsList = () => {
focusPeopleStatistics(props.orgData.key, dateClassKey_focusPeopleStatistics.value, focusPeopleStatisticsTypeVal.value).then((res) => {
focusPeopleStatisticsChartRef.value &&
focusPeopleStatisticsChartRef.value.setOption({
xAxis: {
data: res.data.map((item) => item.name),
},
series: { data: res.data.map((item) => item.num) },
});
});
};
watch(
() => props.orgData,
(val) => {
if (val && props.sidebarType == "caseEvent") {
// 社会治安
policeSituationNum(val.key).then((res) => {
caseEventTypeList.value = res.data;
});
// 矛盾纠纷数据分析(折线图)
yearOnYearStatistics(val.key).then((res) => {
//平均发生数数据
let average = {
year: "平均发生数",
detail: [],
};
let averageObj = {};
res.data.forEach((item) => {
item.detail.forEach((item1) => {
if (averageObj[item1.name]) {
averageObj[item1.name].total = averageObj[item1.name].total + item1.total;
} else {
averageObj[item1.name] = cloneDeep(item1);
}
});
});
res.data[0].detail.forEach((item) => {
averageObj[item.name].total = (averageObj[item.name].total / res.data.length).toFixed(2);
average.detail.push(averageObj[item.name]);
});
res.data.push(average);
caseEventCompareChartRef.value &&
caseEventCompareChartRef.value.setOption({
legend: {
data: res.data.map((item) => {
let obj = {
name: item.year,
itemStyle: {
opacity: "0",
},
};
if (item.year == "平均发生数") {
obj.lineStyle = {
type: [1.7, 1.7],
};
}
return obj;
}),
},
xAxis: { data: res.data[0].detail.map((item) => item.name) },
series: res.data.map((item) => {
let obj = { name: item.year };
obj.data = item.detail.map((item) => item.total);
return obj;
}),
});
});
// 矛盾纠纷化解情况(折线、柱状图)
setOption_conflictResolutionChart();
// 矛盾纠纷占比 饼图
contradictionRatio(val.key).then((res) => {
contradictionRatioList.value = res.data;
contradictionRatioChartRef.value &&
contradictionRatioChartRef.value.setOption({
series: [
{
data: res.data.map((item) => {
return Object.assign(item, { value: item.total });
}),
},
],
});
});
// 次生、升级、反复 表格数据
secondaryOrUpgradeStatistics(val.key).then((res) => {
caseUp_proportion.value = (res.data.proportion * 100).toFixed(2) + "%";
caseUp_WeekAdd.value = res.data.increment;
caseUp_List.value = res.data.list;
});
// 高频高发事件Top3
topN(val.key).then((res) => {
highFrequencyEventsList.value = res.data;
});
// 各街镇矛盾纠纷发生数
streetTownStatistics(val.key).then((res) => {
streetTownStatisticsList.value = res.data;
});
getFocusPeopleStatisticsList();
}
},
{ deep: true, immediate: true }
);
// watch(
// () => props.data,
// (val) => {
// if (val && props.sidebarType == "caseEvent") {
// nextTick(() => {
// 社会治安-矛盾纠纷数据分析(折线图)
// caseEventCompareChartRef.value &&
// caseEventCompareChartRef.value.setOption({
// legend: { data: props.data.caseEventCompareList.map((item) => item.time) },
// xAxis: { data: props.data.caseEventCompareList[0].label },
// series: props.data.caseEventCompareList.map((item) => {
// return {
// name: item.time,
// data: item.nums,
// };
// }),
// });
// 矛盾纠纷化解情况(折线、柱状图)
// setOption_conflictResolutionChart();
// 矛盾纠纷占比 饼图
// contradictionRatioChartRef.value &&
// contradictionRatioChartRef.value.setOption({
// series: [
// {
// data: val.contradictionRatioList.map((item) => {
// return { value: item.value };
// }),
// },
// ],
// });
// });
// }
// },
// { deep: true, immediate: true }
// );
const caseEventCompareChartOptions = ref({
tooltip: {
trigger: "axis",
extraCssText: "width:auto;height:auto",
confine: true,
},
legend: {
textStyle: {
fontSize: 11,
color: "#fff",
},
left: "7%",
itemWidth: 12,
itemHeight: 4,
itemGap: 14,
data: [],
},
grid: {
left: "3%",
right: "0%",
bottom: "3%",
top: "30%",
containLabel: true,
},
xAxis: {
type: "category",
// boundaryGap: true,
axisLabel: { color: "#fff", fontSize: 9, interval: -10, rotate: 35 },
axisLine: {
show: true,
lineStyle: {
// 使用深浅的间隔色
color: "#6C8097",
},
},
axisTick: {
alignWithLabel: true,
},
data: [],
},
yAxis: {
name: "单位(件)",
nameTextStyle: {
fontSize: 10,
padding: [2, 0, -2, 0],
},
axisLine: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: true, // 先隐藏默认的分割线
lineStyle: {
// 使用深浅的间隔色
color: "rgba(108,128,151,0.3)",
},
},
axisLabel: {
color: "#999",
fontSize: 10,
margin: 3,
padding: [0, 0, 10, 0],
},
},
series: [
{
name: "",
type: "line",
// stack: "Total",//是否堆叠
symbolSize: 0,
itemStyle: {
color: "#3FF5FF",
},
data: [],
},
{
name: "",
type: "line",
// stack: "Total",//是否堆叠
symbolSize: 0,
itemStyle: {
color: "#7F94FF",
},
data: [],
},
{
name: "",
type: "line",
// stack: "Total",//是否堆叠
symbolSize: 0,
itemStyle: {
color: "#D098CB",
},
data: [],
},
{
name: "",
type: "line",
// stack: "Total",//是否堆叠
symbolSize: 0,
lineStyle: {
type: [2, 2],
},
itemStyle: {
color: "#FFAA39",
},
data: [],
},
],
});
const conflictResolutionChartOptions = ref({
tooltip: {
trigger: "axis",
extraCssText: "width:auto;height:auto",
confine: true,
},
legend: {
textStyle: {
fontSize: 11,
color: "#fff",
},
itemWidth: 12,
itemHeight: 4,
itemGap: 14,
data: [
{
name: "化解量",
icon: "rect",
},
{
name: "未化解量",
icon: "rect",
},
{
name: "化解率",
itemStyle: {
opacity: "0",
},
},
{
name: "平均化解率",
lineStyle: {
type: [1.7, 1.7],
},
itemStyle: {
opacity: "0",
},
},
],
},
grid: {
left: "3%",
right: "0%",
bottom: "3%",
top: "30%",
containLabel: true,
},
xAxis: {
type: "category",
axisLabel: { color: "#fff", fontSize: 9, interval: 0, rotate: 40 },
axisLine: {
show: true,
lineStyle: {
// 使用深浅的间隔色
color: "#6C8097",
},
},
axisTick: {
alignWithLabel: true,
},
data: [],
},
yAxis: [
{
type: "value",
name: "单位(件)",
nameTextStyle: {
fontSize: 10,
padding: [2, 0, -2, 0],
},
splitLine: {
show: true, // 先隐藏默认的分割线
lineStyle: {
// 使用深浅的间隔色
color: "rgba(108,128,151,0.3)",
},
},
},
{
type: "value",
name: "",
splitLine: false,
axisLabel: {
formatter: "{value}%",
},
},
],
series: [
{
name: "化解量",
type: "bar",
stack: "Ad",
barWidth: 9,
itemStyle: {
color: "#00BAC5",
},
emphasis: {
focus: "series",
},
data: [],
},
{
name: "未化解量",
type: "bar",
stack: "Ad",
barWidth: 9,
itemStyle: {
color: "#D098CB",
},
emphasis: {
focus: "series",
},
data: [],
},
{
name: "化解率",
type: "line",
symbolSize: 0,
yAxisIndex: 1,
data: [],
itemStyle: {
color: "#7F94FF",
},
tooltip: {
valueFormatter: function (value) {
return value + "%";
},
},
},
{
name: "平均化解率",
type: "line",
symbolSize: 0,
yAxisIndex: 1,
lineStyle: {
type: [2, 2],
},
itemStyle: {
color: "#FFAA39",
},
data: [],
tooltip: {
valueFormatter: function (value) {
return value + "%";
},
},
},
],
});
const contradictionRatioChartOptions = ref({
tooltip: {
show: false,
trigger: "item",
},
color: ["#5c7cea", "#00bac5", "#d098cb", "#fd742f", "#ffaa39", "#405ae9"],
series: [
{
type: "pie",
radius: ["50%", "80%"],
center: ["47%", "50%"],
itemStyle: {
borderRadius: 0,
borderColor: "#fff",
borderWidth: 0,
},
label: {
show: false,
},
emphasis: {
scale: true, //禁止触摸缩放
},
labelLine: {
show: false,
},
data: [],
},
],
});
const subContradictionRatioChartOptions = ref({
tooltip: {
show: false,
trigger: "item",
},
color: ["#5C7CEA", "#00BAC5", "#D098CB", "#CD6C27", "#FFAA39"],
series: [
{
type: "pie",
radius: ["28%", "45%"],
center: ["50%", "50%"],
itemStyle: {
borderRadius: 0,
borderColor: "#fff",
borderWidth: 0,
},
label: {
width: 120,
height: 12,
formatter: function (params) {
return `{before|}{block${params.dataIndex + 1}|}{after|}\n{down|} {pro|${params.data.name}}\n{val${params.dataIndex + 1}|${(params.data.proportion * 100).toFixed(2)}%} {val${
params.dataIndex + 1
}|${params.data.value}}`;
},
rich: {
before: {
padding: [0, -4, 0, 0],
},
after: {
padding: [0, -4, 0, 0],
},
down: {
padding: [10, 0, -20, 0],
},
circle: {
backgroundColor: "auto", //背景
borderColor: "auto", //环颜色等于对应的部分颜色
borderRadius: 4, //环的半径
borderWidth: 1, //环的宽度
width: 1, //强制撑开内容
height: 1,
},
pro: {
color: "#fff",
fontSize: 12,
lineHeight: 0,
padding: [0, 6, 22, 4],
},
val1: {
color: "#5C7CEA",
padding: [-12, 0, 0, 0],
},
block1: {
backgroundColor: "#5C7CEA",
width: 8,
height: 8,
padding: [0, 0, 0, 0],
},
val2: {
color: "#00BAC5",
padding: [-12, 0, 0, 0],
},
block2: {
backgroundColor: "#00BAC5",
width: 8,
height: 8,
},
val3: {
color: "#D098CB",
padding: [-12, 0, 0, 0],
},
block3: {
backgroundColor: "#D098CB",
width: 8,
height: 8,
},
val4: {
color: "#CD6C27",
padding: [-12, 0, 0, 0],
},
block4: {
backgroundColor: "#CD6C27",
width: 8,
height: 8,
},
val5: {
color: "#FFAA39",
padding: [-12, 0, 0, 0],
},
block5: {
backgroundColor: "#FFAA39",
width: 8,
height: 8,
},
},
},
labelLine: {
show: true,
symbol: "circle",
symbolSize: 12,
symbolKeepAspect: true,
lineStyle: {
color: "#fff",
cap: "circle",
// dashOffset:
},
},
emphasis: {
scale: false, //禁止触摸缩放
},
data: [
{ value: 1048, name: "婚恋纠纷" },
{ value: 735, name: "家庭纠纷" },
{ value: 580, name: "邻里纠纷" },
{ value: 484, name: "其它一般性矛盾纠纷" },
],
},
],
});
// const subContradictionRatio_Total = ref(800);
// 矛盾纠纷占比 选中的模块index
const contradictionRatioChartSelectIndex = ref(-1);
// 矛盾纠纷占比-饼图-鼠标进入事件
const contradictionRatioChart_mousemove = (param) => {
if (contradictionRatioChartSelectIndex.value == param.dataIndex) return;
contradictionRatioChartSelectIndex.value = param.dataIndex;
popoverVisible.value = true;
subContradictionRatio(props.orgData.key, param.data.firstDictCode).then((res) => {
subContradictionRatioChartRef.value &&
subContradictionRatioChartRef.value.setOption({
series: [
{
data: res.data.map((item) => Object.assign(item, { value: item.total })),
// data: props.data.contradictionRatioList[param.dataIndex].subContradictionRatioList,
},
],
});
});
};
// 矛盾纠纷占比-饼图-鼠标离开事件
const contradictionRatioChart_globalout = () => {
contradictionRatioChartSelectIndex.value = -1;
popoverVisible.value = false;
};
const focusPeopleStatisticsChartRef = ref(null);
// 矛盾纠纷涉重点人员 - 折线图
const focusPeopleStatisticsChartOptions = ref({
tooltip: {
trigger: "axis",
extraCssText: "width:auto;height:auto",
confine: true,
},
grid: {
left: "3%",
right: "0%",
bottom: "3%",
top: "20%",
containLabel: true,
},
xAxis: {
type: "category",
// boundaryGap: true,
axisLabel: { color: "#fff", fontSize: 9, interval: -10, rotate: 35 },
axisLine: {
show: true,
lineStyle: {
// 使用深浅的间隔色
color: "#6C8097",
},
},
axisTick: {
alignWithLabel: true,
},
data: [],
},
yAxis: {
name: "单位(人)",
nameTextStyle: {
fontSize: 10,
padding: [2, 0, -2, 0],
},
axisLine: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
show: true, // 先隐藏默认的分割线
lineStyle: {
// 使用深浅的间隔色
color: "rgba(108,128,151,0.3)",
},
},
axisLabel: {
color: "#999",
fontSize: 10,
margin: 3,
padding: [0, 0, 10, 0],
},
},
series: {
name: "",
type: "line",
symbolSize: 0,
itemStyle: {
color: "#3FF5FF",
},
data: [],
},
});
const popoverVisible = ref(false);
// 获取比例
const getProportion = (val, total) => {
if (val && total) {
return ((val / total) * 100).toFixed(2) + "%";
} else {
return 0 + "%";
}
};
// 表单行样式
const setRowStyle = ({ row, rowIndex }) => {
return {
backgroundColor: rowIndex % 2 == 0 ? "#092F3A" : "#01222C",
};
};
defineExpose({});
</script>
<style lang="scss" scoped>
.sidebar_right_caseEvent {
padding: 0 12px;
}
.title {
font-size: 12px;
color: #00bac5;
line-height: 17px;
display: flex;
align-items: center;
margin-top: 20px;
margin-left: -5px;
}
ul.barChart {
li {
display: flex;
align-items: center;
line-height: 24px;
height: 24px;
overflow: hidden;
}
&.static li {
&:first-child {
line-height: 12px;
height: 17px;
align-items: flex-start;
}
&:last-child {
line-height: 12px;
height: 17px;
align-items: flex-end;
}
}
&.oneRow li {
line-height: 12px;
height: 17px;
align-items: center;
}
}
.ageGroup {
flex: 1;
li {
height: 18px;
// width: 208px;
background: rgba(255, 255, 255, 0.05);
position: relative;
display: flex;
align-items: center;
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 3px;
height: 3px;
border: 1px solid rgba(255, 255, 255, 0.5);
border-right: 0;
border-bottom: 0;
}
}
li + li {
margin-top: 4px;
}
}
.colorBlock {
width: 8px;
height: 8px;
margin-left: 15px;
margin-right: 8px;
}
.bg1 {
background: #5c7cea;
}
.bg2 {
background: #00bac5;
}
.bg3 {
background: #d098cb;
}
.bg4 {
background: #fd742f;
}
.bg5 {
background: #ffaa39;
}
.bg6 {
background: #405ae9;
}
.color1 {
color: #5c7cea;
}
.color2 {
color: #00bac5;
}
.color3 {
color: #d098cb;
}
.color4 {
color: #fd742f;
}
.color5 {
color: #ffaa39;
}
.color6 {
color: #405ae9;
}
.myProgress {
display: inline-block;
height: 10px;
background: rgba(255, 255, 255, 0.1);
position: relative;
.inner {
height: 4px;
position: absolute;
top: 3px;
}
.inner_color1 {
background: linear-gradient(270deg, #5c7cea 0%, rgba(92, 124, 234, 0.2) 100%);
}
.inner_color2 {
background: linear-gradient(270deg, #d098cb 0%, rgba(205, 146, 200, 0.2) 100%);
}
.inner_color3 {
background: linear-gradient(270deg, #cd6c27 0%, rgba(205, 108, 39, 0.2) 100%);
}
.inner_color4 {
background: linear-gradient(270deg, #00bac5 0%, rgba(0, 186, 197, 0.15) 100%);
}
}
.myProgress1 {
&::before {
content: "";
position: absolute;
top: -14px;
height: 38px;
border-left: 1px solid #00bac5;
opacity: 0.5;
}
}
.myProgress1.color1::before {
border-left-color: #5c7cea;
}
.myProgress1.color2::before {
border-left-color: #00bac5;
}
.myProgress1.color3::before {
border-left: 1px solid #d098cb;
}
.dataTotal {
font-family: DINAlternate, DINAlternate;
font-weight: bold;
font-size: 18px;
}
.scroll-title {
.el-col {
height: 48px;
& > span {
padding: 0 10px;
color: rgb(63, 245, 255);
background: rgb(32, 87, 103);
width: 100%;
height: 100%;
display: flex;
align-items: center;
box-sizing: border-box;
}
}
}
.scroll-item {
height: 23px;
margin-bottom: 2px;
}
.scroll-item:nth-child(2n) .el-col > span {
width: 100%;
display: inline-block;
background: #01222c;
line-height: 23px;
padding: 0 10px;
box-sizing: border-box;
}
.scroll-item:nth-child(2n + 1) .el-col > span {
width: 100%;
display: inline-block;
background: #092f3a;
line-height: 23px;
padding: 0 10px;
box-sizing: border-box;
}
.text-overflow {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
<style lang="scss">
.sidebar_radio_group {
.el-radio-button__original-radio:checked + .el-radio-button__inner {
background: rgba(0, 186, 197, 0.2);
border-color: #00bac5 !important;
color: #3ff5ff;
}
.el-radio-button__inner {
padding: 2px 6px;
border-radius: 0px !important;
background: transparent;
border-color: #417174 !important;
color: #417174;
line-height: 12.5px;
}
}
.sidebar_select {
.el-input .el-select__caret {
color: #417174;
}
.el-input__inner,
.el-input,
.el-input__wrapper {
background-color: transparent !important;
border-radius: 0px !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.el-input__wrapper {
padding-left: 6px;
padding-right: 6px;
// background-color: transparent !important;
box-shadow: 0 0 0 1px #417174 inset !important;
&:hover {
box-shadow: 0 0px 0px 0px !important;
}
}
.el-input__inner {
height: 16px;
font-size: 11px;
color: #3ff5ff !important;
}
}
.custom-legend {
background-color: #f0f0f0; /* 设置背景颜色 */
border: 1px solid #ccc; /* 设置边框 */
padding: 5px; /* 设置内边距 */
}
.subContradictionRatioPopper.el-popover.el-popper.is-dark {
background: #1e2236;
box-shadow: 0px 2px 4px 2px #000000;
}
</style>
6、滚动的水滴球
<!-- 安防设施详情 -->
<template>
<div class="sidebar_right_securityEquipment" v-if="sidebarType == 'securityEquipment'">
<div class="flex" style="flex-wrap: wrap; justify-content: center; margin-top: 4px">
<div v-for="(item, index) in deviceFunctionList" :key="index" style="margin: 16px 0px; display: flex; flex-direction: column; align-items: center; width: 33.33%">
<div class="Echart" :ref="(el) => setTableCompRefs(el, item.name)" :style="{ width: '70px', height: '70px' }"></div>
<p style="text-align: center; margin-top: 10px">{{ item.name }}</p>
</div>
<div v-if="!deviceFunctionList || deviceFunctionList.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
</div>
<!-- <div class="flex" style="flex-wrap: wrap; justify-content: center; margin-top: 4px">
<div v-for="(item, index) in deviceFunctionList" :key="index" style="margin: 16px 0px; text-align: center; width: 33.33%">
<el-progress type="circle" :percentage="(item.percent * 100).toFixed(2)" stroke-linecap="butt" color="#3CE0EB" :width="44" :stroke-width="4">
<span>{{ (item.percent * 100).toFixed(2) + "%" }}</span>
</el-progress>
<p style="text-align: center; margin-top: 10px">{{ item.name }}</p>
</div>
<div v-if="!deviceFunctionList || deviceFunctionList.length == 0" style="text-align: center; color: var(--el-text-color-secondary); line-height: 60px; font-size: 14px">暂无数据</div>
</div> -->
</div>
</template>
<script setup>
import { onMounted, reactive, ref, nextTick, watch, defineProps, defineExpose, computed } from "vue";
import { CaretRight } from "@element-plus/icons-vue";
import JSK from "@/utils/JSK";
// import "echarts-liquidfill";
import { deviceRatio } from "@/api/map_sidebar.js";
const props = defineProps({
sidebarType: "",
// data: {
// type: Object,
// default: {},
// },
orgData: {
type: Object,
default: {},
},
});
const tableCompRefs = ref({});
const deviceFunctionList = ref([]);
const setTableCompRefs = (el, label) => {
if (el) {
tableCompRefs.value[label] = el;
}
};
var charts_ = {};
watch(
() => props.orgData,
(val) => {
if (val && props.sidebarType == "securityEquipment") {
deviceRatio(props.orgData.key).then((res) => {
deviceFunctionList.value = res.data;
nextTick(() => {
for (let key in tableCompRefs.value) {
var chart = echarts.init(tableCompRefs.value[key]);
chart.setOption(eviceFunctionChartOptions.value);
charts_[key] = chart;
renderWaterPoloChart(key);
}
});
});
}
},
{ deep: true, immediate: true }
);
// 渲染水球图
const renderWaterPoloChart = (key) => {
let data_ = deviceFunctionList.value.filter((item) => item.name == key)[0];
charts_[key] &&
charts_[key].setOption({
series: [
{ data: [data_.percent, data_.percent] },
{
data: [
{
name: "val1",
value: (data_.percent * 100).toFixed(0), //外环数值
labelLine: {
show: false,
},
itemStyle: {
//外环颜色
color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [
{
offset: 0,
color: "rgba(227, 253, 255, 0)",
},
{
offset: 1,
color: "#00BAC5",
},
]),
},
},
{
//画中间的图标
name: "",
value: 0.8,
labelLine: {
show: false,
},
itemStyle: {
color: "#00BAC5",
normal: {
color: "#00BAC5",
borderColor: "#00BAC5",
borderWidth: 2, //外部圆小球大小
borderRadius: 1,
},
},
label: {
borderRadius: "100%",
},
emphasis: {
labelLine: {
show: false,
},
itemStyle: {
color: "#00BAC5",
},
},
},
{
//画剩余的刻度圆环
name: "val2",
value: ((1 - data_.percent) * 100).toFixed(0), //剩余的刻度圆环数值
itemStyle: {
color: "transparent",
},
label: {
show: false,
},
labelLine: {
show: false,
},
emphasis: {
labelLine: {
show: false,
},
itemStyle: {
color: "#050038",
},
},
},
],
},
],
});
};
const eviceFunctionChartOptions = ref({
series: [
{
type: "liquidFill",
radius: "80%",
center: ["50%", "50%"],
data: [0, 0], //水球占比 data个数代表波浪数[0.7, 0.7]
color: ["rgb(94, 246, 255, 0.65)", "rgb(2, 151, 160, 0.8)"], //波浪的颜色
backgroundStyle: {
color: {
type: "radial",
x: 0.5,
y: 0.5,
r: 0.55,
colorStops: [
{
offset: 0.5,
color: "rgb(13, 15, 26) ", // 0% 处的颜色
},
{
offset: 0.8,
color: "rgb(40, 53, 69)", // 100% 处的颜色
},
{
offset: 1,
color: "rgb(68, 111, 124)", // 100% 处的颜色
},
],
globalCoord: false, // 缺省为 false
},
},
label: {
normal: {
formatter: function (value) {
if (!value.value) {
return "加载中";
} else {
return (value.value * 100).toFixed(0) + "%";
}
},
textStyle: {
fontSize: 10, //中间文字大小
color: "#fff",
},
},
},
outline: {
show: false,
},
},
{
type: "pie",
center: ["50%", "50%"],
radius: ["90%", "94%"],
hoverAnimation: false,
data: [
{
name: "val1",
value: 0, //外环数值60
labelLine: {
show: false,
},
itemStyle: {
//外环颜色
color: new echarts.graphic.LinearGradient(0, 0, 0.5, 1, [
{
offset: 0,
color: "rgba(227, 253, 255, 0)",
},
{
offset: 1,
color: "#00BAC5",
},
]),
},
},
{
//画中间的图标
name: "",
value: 0.8,
labelLine: {
show: false,
},
itemStyle: {
color: "#00BAC5",
normal: {
color: "#00BAC5",
borderColor: "#00BAC5",
borderWidth: 2, //外部圆小球大小
borderRadius: 1,
},
},
label: {
borderRadius: "100%",
},
emphasis: {
labelLine: {
show: false,
},
itemStyle: {
color: "#00BAC5",
},
},
},
{
//画剩余的刻度圆环
name: "val2",
value: 100, //剩余的刻度圆环数值40
itemStyle: {
color: "transparent",
},
label: {
show: false,
},
labelLine: {
show: false,
},
emphasis: {
labelLine: {
show: false,
},
itemStyle: {
color: "#050038",
},
},
},
],
},
],
});
defineExpose({});
</script>
<style lang="scss" scoped>
.sidebar_right_securityEquipment {
padding: 0 12px;
.e-schart {
position: relative;
}
.Echart {
position: relative;
}
}
.title {
font-size: 12px;
color: #00bac5;
line-height: 17px;
display: flex;
align-items: center;
margin-top: 20px;
margin-left: -5px;
}
ul.barChart {
li {
display: flex;
align-items: center;
line-height: 24px;
height: 24px;
overflow: hidden;
}
&.static li {
&:first-child {
line-height: 12px;
height: 17px;
align-items: flex-start;
}
&:last-child {
line-height: 12px;
height: 17px;
align-items: flex-end;
}
}
&.oneRow li {
line-height: 12px;
height: 17px;
align-items: center;
}
}
.ageGroup {
flex: 1;
li {
height: 18px;
// width: 208px;
background: rgba(255, 255, 255, 0.05);
position: relative;
display: flex;
align-items: center;
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 3px;
height: 3px;
border: 1px solid rgba(255, 255, 255, 0.5);
border-right: 0;
border-bottom: 0;
}
}
li + li {
margin-top: 4px;
}
}
.colorBlock {
width: 8px;
height: 8px;
margin-left: 15px;
margin-right: 8px;
}
.bg1 {
background: #5c7cea;
}
.bg2 {
background: #00bac5;
}
.bg3 {
background: #d098cb;
}
.bg4 {
background: #fd742f;
}
.bg5 {
background: #ffaa39;
}
.bg6 {
background: #405ae9;
}
.color1 {
color: #5c7cea;
}
.color2 {
color: #00bac5;
}
.color3 {
color: #d098cb;
}
.color4 {
color: #fd742f;
}
.color5 {
color: #ffaa39;
}
.color6 {
color: #405ae9;
}
.myProgress {
display: inline-block;
height: 10px;
background: rgba(255, 255, 255, 0.1);
position: relative;
.inner {
height: 4px;
position: absolute;
top: 3px;
}
.inner_color1 {
background: linear-gradient(270deg, #5c7cea 0%, rgba(92, 124, 234, 0.2) 100%);
}
.inner_color2 {
background: linear-gradient(270deg, #d098cb 0%, rgba(205, 146, 200, 0.2) 100%);
}
.inner_color3 {
background: linear-gradient(270deg, #cd6c27 0%, rgba(205, 108, 39, 0.2) 100%);
}
.inner_color4 {
background: linear-gradient(270deg, #00bac5 0%, rgba(0, 186, 197, 0.15) 100%);
}
}
.myProgress1 {
&::before {
content: "";
position: absolute;
top: -14px;
height: 38px;
border-left: 1px solid #00bac5;
opacity: 0.5;
}
}
.myProgress1.color2::before {
border-left-color: #00bac5;
}
.myProgress1.color3::before {
border-left: 1px solid #d098cb;
}
</style>
<style lang="scss">
.sidebar_radio_group {
.el-radio-button__original-radio:checked + .el-radio-button__inner {
background: rgba(0, 186, 197, 0.2);
border-color: #00bac5 !important;
color: #3ff5ff;
}
.el-radio-button__inner {
padding: 2px 6px;
border-radius: 0px !important;
background: transparent;
border-color: #417174 !important;
color: #417174;
line-height: 12.5px;
}
}
.sidebar_select {
.el-input .el-select__caret {
color: #417174;
}
.el-input__inner,
.el-input,
.el-input__wrapper {
background-color: transparent !important;
border-radius: 0px !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.el-input__wrapper {
padding-left: 6px;
padding-right: 6px;
// background-color: transparent !important;
box-shadow: 0 0 0 1px #417174 inset !important;
&:hover {
box-shadow: 0 0px 0px 0px !important;
}
}
.el-input__inner {
height: 16px;
font-size: 11px;
color: #3ff5ff !important;
}
}
</style>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。