新增页面
This commit is contained in:
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div class="progress-list">
|
||||
<div class="list-item" v-for="(item, index) in listData" :key="index">
|
||||
<div class="label">{{ item.label }}</div>
|
||||
<div class="bar-container">
|
||||
<div class="bar-bg">
|
||||
<div class="bar-fill" :style="{ width: (item.value / maxValue * 100) + '%' }">
|
||||
<span class="value-text">{{ item.value }}个</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed,defineProps,watch } from 'vue'
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
const listData = ref([])
|
||||
const maxValue = computed(() => {
|
||||
const max = Math.max(...listData.value.map(item => item.value))
|
||||
return max > 0 ? max : 100
|
||||
})
|
||||
|
||||
watch(() => props.data, (newVal) => {
|
||||
listData.value = newVal
|
||||
},{immediate:true})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.progress-list {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
.list-item {
|
||||
width: 98%;
|
||||
margin-bottom: 10px;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.bar-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.bar-bg {
|
||||
flex: 1;
|
||||
height: 16px;
|
||||
background-color: #F5F7FA;
|
||||
border-radius: 6px;
|
||||
margin-right: 15px;
|
||||
.bar-fill {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(90deg, #FF6B22 0%, #FFC94B 100%);
|
||||
transition: width 0.5s ease-out;
|
||||
.value-text {
|
||||
white-space: nowrap;
|
||||
position: absolute;
|
||||
left: 101%;
|
||||
font-size: 14px;
|
||||
color: #FF6B22;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="echratsBox">
|
||||
<div class="chart-container" ref="echartsRef"></div>
|
||||
<div class="legend-container">
|
||||
<div class="legend-item" v-for="(item, index) in chartData" :key="index">
|
||||
<div class="left-part">
|
||||
<span class="dot" :style="{ backgroundColor: colors[index] }"></span>
|
||||
<span class="name">{{ item.name }}</span>
|
||||
<span class="value">{{ item.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import 'echarts-gl' // Ensure GL is available if needed, though we use custom series for better labels
|
||||
|
||||
const echartsRef = ref(null)
|
||||
const chartData = ref([
|
||||
{ name: '未签收', value: 18 },
|
||||
{ name: '已签收', value: 18 },
|
||||
{ name: '已反馈', value: 18 }
|
||||
])
|
||||
const colors = ['#FF4D4F', '#1890FF', '#00E6B8'] // Red, Blue, Cyan/Teal
|
||||
let myChart = null
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
window.addEventListener('resize', resizeChart)
|
||||
})
|
||||
|
||||
const initChart = () => {
|
||||
if (!echartsRef.value) return
|
||||
myChart = echarts.init(echartsRef.value)
|
||||
const data = chartData.value.map((item, index) => ({
|
||||
name: item.name,
|
||||
value: item.value,
|
||||
itemStyle: {
|
||||
color: colors[index]
|
||||
}
|
||||
}));
|
||||
const option = getPie3DOptions(data, 0.5);
|
||||
option.series.push({
|
||||
name: 'labelPie',
|
||||
type: 'pie',
|
||||
radius: ['70%', '85%'],
|
||||
center: ['45%', '50%'],
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
itemStyle: {
|
||||
opacity: 0 // Invisible slices
|
||||
},
|
||||
data: data,
|
||||
z: 100
|
||||
});
|
||||
|
||||
myChart.setOption(option)
|
||||
}
|
||||
// 生成3D饼图的配置
|
||||
const getPie3DOptions = (pieData, internalRadiusRatio) => {
|
||||
const series = [];
|
||||
let sumValue = 0;
|
||||
let startValue = 0;
|
||||
let endValue = 0;
|
||||
const k = internalRadiusRatio || 1 / 3;
|
||||
|
||||
for (let i = 0; i < pieData.length; i++) {
|
||||
sumValue += pieData[i].value;
|
||||
const seriesItem = {
|
||||
name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
|
||||
type: 'surface',
|
||||
parametric: true,
|
||||
wireframe: {
|
||||
show: false
|
||||
},
|
||||
pieData: pieData[i],
|
||||
pieStatus: {
|
||||
selected: false,
|
||||
hovered: false,
|
||||
k: k
|
||||
},
|
||||
shading: 'lambert', // Add realistic shading
|
||||
itemStyle: {
|
||||
opacity: 1
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof pieData[i].itemStyle != 'undefined') {
|
||||
const itemStyle = {};
|
||||
typeof pieData[i].itemStyle.color != 'undefined' ? itemStyle.color = pieData[i].itemStyle.color : null;
|
||||
typeof pieData[i].itemStyle.opacity != 'undefined' ? itemStyle.opacity = pieData[i].itemStyle.opacity : null;
|
||||
seriesItem.itemStyle = { ...seriesItem.itemStyle, ...itemStyle };
|
||||
}
|
||||
series.push(seriesItem);
|
||||
}
|
||||
|
||||
for (let i = 0; i < series.length; i++) {
|
||||
endValue = startValue + series[i].pieData.value;
|
||||
series[i].pieData.startRatio = startValue / sumValue;
|
||||
series[i].pieData.endRatio = endValue / sumValue;
|
||||
series[i].parametricEquation = getParametricEquation(series[i].pieData.startRatio, series[i].pieData.endRatio, false, false, k, series[i].pieData.value);
|
||||
startValue = endValue;
|
||||
}
|
||||
|
||||
return {
|
||||
tooltip: {
|
||||
formatter: params => {
|
||||
if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== 'labelPie') {
|
||||
return `${params.seriesName}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${series[params.seriesIndex].pieData.value}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
xAxis3D: { min: -1, max: 1 },
|
||||
yAxis3D: { min: -1, max: 1 },
|
||||
zAxis3D: { min: -1, max: 1 },
|
||||
grid3D: {
|
||||
show: false,
|
||||
boxHeight: 10,
|
||||
top: '-10%',
|
||||
light: {
|
||||
main: {
|
||||
intensity: 1.2,
|
||||
shadow: true,
|
||||
shadowQuality: 'high',
|
||||
alpha: 40,
|
||||
beta: -30
|
||||
},
|
||||
ambient: {
|
||||
intensity: 0.5
|
||||
}
|
||||
},
|
||||
viewControl: {
|
||||
alpha: 30,
|
||||
beta: 40,
|
||||
distance: 180,
|
||||
rotateSensitivity: 1,
|
||||
zoomSensitivity: 0,
|
||||
panSensitivity: 0,
|
||||
autoRotate: false
|
||||
}
|
||||
},
|
||||
series: series
|
||||
};
|
||||
}
|
||||
|
||||
function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h) {
|
||||
const midRatio = (startRatio + endRatio) / 2;
|
||||
const startRadian = startRatio * Math.PI * 2;
|
||||
const endRadian = endRatio * Math.PI * 2;
|
||||
const midRadian = midRatio * Math.PI * 2;
|
||||
|
||||
const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
|
||||
const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
|
||||
const hoverRate = isHovered ? 1.05 : 1;
|
||||
|
||||
return {
|
||||
u: {
|
||||
min: -Math.PI,
|
||||
max: Math.PI * 3,
|
||||
step: Math.PI / 32
|
||||
},
|
||||
v: {
|
||||
min: 0,
|
||||
max: Math.PI * 2,
|
||||
step: Math.PI / 20
|
||||
},
|
||||
x: function(u, v) {
|
||||
if (u < startRadian) {
|
||||
return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
|
||||
}
|
||||
if (u > endRadian) {
|
||||
return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
|
||||
}
|
||||
return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
|
||||
},
|
||||
y: function(u, v) {
|
||||
if (u < startRadian) {
|
||||
return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
|
||||
}
|
||||
if (u > endRadian) {
|
||||
return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
|
||||
}
|
||||
return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
|
||||
},
|
||||
z: function(u, v) {
|
||||
if (u < -Math.PI * 0.5) {
|
||||
return Math.sin(u);
|
||||
}
|
||||
if (u > Math.PI * 2.5) {
|
||||
return Math.sin(u);
|
||||
}
|
||||
return Math.sin(v) * 1.5;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const resizeChart = () => {
|
||||
myChart && myChart.resize()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.echratsBox {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.chart-container {
|
||||
flex: 1.5; // Give more space to chart
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.legend-container {
|
||||
flex: 0.8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-left: 20px;
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px dashed #E0E0E0;
|
||||
|
||||
&:first-child {
|
||||
border-top: 1px dashed #E0E0E0;
|
||||
}
|
||||
|
||||
.left-part {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.dot {
|
||||
width: 12px;
|
||||
height: 6px;
|
||||
border-radius: 2px;
|
||||
margin-right: 10px;
|
||||
transform: skewX(-20deg);
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
.value {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div class="top-head">
|
||||
<el-form :inline="true" :model="formSearch" class="form-search">
|
||||
<el-form-item label="选择部门">
|
||||
<MOSTY.Department clearable v-model="formSearch.ssbmdm" />
|
||||
</el-form-item>
|
||||
<el-form-item label="">
|
||||
<div class="filter-section">
|
||||
<el-radio-group v-model="radioTime" @change="changeRadio">
|
||||
<el-radio v-for="(it,idx) in timeList" :key="idx" :label="it.num" >{{ it.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
<span class="ml5 mr5">自定义:</span>
|
||||
<el-date-picker @change="handleDateChange" v-model="formSearch.dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSubmit">查询</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { timeValidate, timeSlotChange } from "@/utils/tools.js";
|
||||
import * as MOSTY from "@/components/MyComponents/index";
|
||||
import { ref,defineEmits } from "vue";
|
||||
const emit = defineEmits(['change'])
|
||||
const timeList = ref([
|
||||
{ label: "日", num: 0 },
|
||||
{ label: "月", num: 1 },
|
||||
{ label: "季", num: 2 },
|
||||
{ label: "年", num: 3 }
|
||||
]);
|
||||
const radioTime = ref('')
|
||||
const formSearch = ref({})
|
||||
|
||||
// 单选
|
||||
const changeRadio = (val) =>{
|
||||
switch(val){
|
||||
case 0: //日
|
||||
formSearch.value.dateRange = [timeValidate(),timeValidate()]
|
||||
break;
|
||||
case 1: //月
|
||||
formSearch.value.dateRange = timeSlotChange('本月')
|
||||
break;
|
||||
case 2: //季度
|
||||
formSearch.value.dateRange = timeSlotChange('本季度')
|
||||
break;
|
||||
case 3: //年
|
||||
formSearch.value.dateRange = timeSlotChange('本年')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义
|
||||
const handleDateChange = (val) => {
|
||||
radioTime.value = '';
|
||||
if(val[0] == timeSlotChange('天')[0] && val[1] == timeSlotChange('天')[1]) radioTime.value = 0;
|
||||
if(val[0] == timeSlotChange('本月')[0] && val[1] == timeSlotChange('本月')[1]) radioTime.value = 1;
|
||||
if(val[0] == timeSlotChange('本季度')[0] && val[1] == timeSlotChange('本季度')[1]) radioTime.value = 2;
|
||||
if(val[0] == timeSlotChange('本年')[0] && val[1] == timeSlotChange('本年')[1]) radioTime.value = 3;
|
||||
};
|
||||
|
||||
// 查询
|
||||
const handleSubmit = () => {
|
||||
emit('change', formSearch.value)
|
||||
}
|
||||
// 重置
|
||||
const resetForm = () => {
|
||||
radioTime.value = '';
|
||||
formSearch.value = { ssbmdm: '', dateRange: null }
|
||||
emit('change', formSearch.value)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.top-head {
|
||||
background-color: #fff;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
padding: 10px 10px 0;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
.filter-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
::v-deep .el-radio{
|
||||
--el-radio-input-border-radius: 10%;
|
||||
}
|
||||
::v-deep .el-radio__label{
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<div class="echratsBox">
|
||||
<div class="chart-container" :id="props.id"></div>
|
||||
<div class="legend-container">
|
||||
<div class="legend-item" v-for="(item, i) in legendList" :key="i">
|
||||
<span class="dot" :style="{ background: item.dotColor }"></span>
|
||||
<span class="name">{{ item.name }}</span>
|
||||
<span class="value">{{ item.value }}</span>
|
||||
<span class="label">占比</span>
|
||||
<span class="percent">{{ item.percent }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { nextTick, ref ,defineProps,watch} from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ name: '失控', value: 85 ,color: '#D66A8D'},
|
||||
{ name: '在控', value: 55 ,color: '#17C0AE'},
|
||||
]
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
default: 'echartsRef'
|
||||
}
|
||||
})
|
||||
const legendList = ref([])
|
||||
watch(() => props.data, (newVal) => {
|
||||
console.log(newVal,'-------------监听');
|
||||
if (newVal.length) {
|
||||
const total = newVal.reduce((s, v) => s + v.value, 0);
|
||||
legendList.value = newVal.map((d, i) => ({
|
||||
name: d.name,
|
||||
value: 20,
|
||||
percent: Math.round((d.value / total )* 100),
|
||||
dotColor: i === 0 ? 'linear-gradient(90deg, #FF8DA7 0%, #D66A8D 100%)': d.color
|
||||
}))
|
||||
nextTick(() => {
|
||||
initChart(newVal, total)
|
||||
})
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
const initChart = (data, total) => {
|
||||
let chart = echarts.init(document.getElementById(props.id))
|
||||
if (!chart) return;
|
||||
const seriesData = data.map((d) => ({
|
||||
name: d.name,
|
||||
value: d.value,
|
||||
itemStyle: { color: d.color }
|
||||
}))
|
||||
|
||||
const option = {
|
||||
tooltip: { show: false },
|
||||
legend: { show: false },
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['50%', '78%'],
|
||||
center: ['50%', '48%'],
|
||||
startAngle:56,
|
||||
roseType: 'radius',
|
||||
label: { show: false },
|
||||
labelLine: { show: false },
|
||||
itemStyle: { borderColor: '#FFFFFF', borderWidth: 0 },
|
||||
data: seriesData
|
||||
}
|
||||
],
|
||||
graphic: [
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '32%',
|
||||
style: {
|
||||
text: `${total}`,
|
||||
fill: '#2F88FF',
|
||||
fontSize: 24,
|
||||
fontWeight: 700,
|
||||
textAlign: 'center'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
left: 'center',
|
||||
top: '52%',
|
||||
style: {
|
||||
text: '总数',
|
||||
fill: '#666666',
|
||||
fontSize: 14,
|
||||
fontWeight: 400,
|
||||
textAlign: 'center'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
chart.setOption(option)
|
||||
window.addEventListener('resize', () => chart.resize())
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.echratsBox {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.legend-container {
|
||||
margin-top: 0;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.08) inset;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.value {
|
||||
margin-right: 8px;
|
||||
color: #333333;
|
||||
font-weight: 500;
|
||||
}
|
||||
.label {
|
||||
margin-right: 4px;
|
||||
color: #666666;
|
||||
}
|
||||
.percent {
|
||||
color: #2F88FF;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,282 +1,32 @@
|
||||
<template>
|
||||
<div class="statistical-analysis">
|
||||
<!-- 左侧树形菜单 -->
|
||||
<div class="left-menu">
|
||||
<!-- 这个部分用的是组件-后期替换 -->
|
||||
<MOSTY.DepartmentTree width="310px" @change="init" placeholder="管理部门" clearable filterable :isBmId="false"
|
||||
v-model="listQuery.ssbmdm" />
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容区 -->
|
||||
<div class="right-content">
|
||||
<!-- 顶部筛选 -->
|
||||
<div class="filter-section">
|
||||
<el-radio-group v-model="radio" @change="changeRadio">
|
||||
<el-radio v-for="(it, idx) in timeList" :key="idx" :label="it.num">{{ it.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
<el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期"
|
||||
end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" @change="handleDateChange" />
|
||||
<el-button type="primary" @click="init">查询</el-button>
|
||||
<el-button type="primary" @click="handleRest">重置</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 统计图表区域 -->
|
||||
<div class="charts-container">
|
||||
<div class="chart-item">
|
||||
<div class="chart-title">
|
||||
<span>信息分组统计</span>
|
||||
<!-- <el-button type="primary">导出统计表</el-button> -->
|
||||
</div>
|
||||
<div class="chart">
|
||||
<PieEcharts echartsId="pieChart" color="#333" :data="obj.xsfzList" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-item">
|
||||
<div class="chart-title">
|
||||
<span>预警等级统计</span>
|
||||
<!-- <el-button type="primary">导出统计表</el-button> -->
|
||||
</div>
|
||||
<div class="chart">
|
||||
<!-- {{ obj.yjdjList }} -->
|
||||
<DbarEcharts echartsId="bar3DChart" :data="obj.yjdjList" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-item">
|
||||
<div class="chart-title">
|
||||
<span>线索来源统计</span>
|
||||
<!-- <el-button type="primary">导出统计表</el-button> -->
|
||||
</div>
|
||||
<ul class="chart mt8">
|
||||
<li v-for="(it, idx) in obj.xslyList" :key="idx" class="mb6">
|
||||
<div style="color: #333">{{ it.label }}</div>
|
||||
<el-progress :text-inside="true" :stroke-width="20" :percentage="calculatePercentage(it.value)"
|
||||
status="exception">
|
||||
<span><span style="color: #e37233">{{ it.value }}</span> 个</span>
|
||||
</el-progress>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="chart-item">
|
||||
<div class="chart-title">
|
||||
<span>研判统计</span>
|
||||
<!-- <el-button type="primary">导出统计表</el-button> -->
|
||||
</div>
|
||||
<DbarEcharts echartsId="bar3DCharts" :data="obj.ypList" />
|
||||
<!-- <lineEcharts color="#333" echartsId="areaChart" :data="obj.ypList" /> -->
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<PageTitle :malginLeft="10" :height="35" backgroundColor="#ffff" :marginBottom="5" :marginTop="5">
|
||||
<template #left>
|
||||
<el-button v-for="(item,index) in butList" :key="index" :type="qh == item ? 'primary' : 'default'" @click="qh = item" size="small">{{item}}</el-button>
|
||||
</template>
|
||||
</PageTitle>
|
||||
<div class="countBox">
|
||||
<QbtjCount v-if="qh=='情报统计分析'" />
|
||||
<YjCount v-if="qh=='预警统计'" />
|
||||
<QygktjCount v-if="qh=='全域管控统计'" />
|
||||
<XxhjCount v-if="qh=='信息汇聚统计'" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { timeValidate, timeSlotChange } from "@/utils/tools.js";
|
||||
import * as MOSTY from "@/components/MyComponents/index";
|
||||
import lineEcharts from "@/views/home/echarts/lineEcharts.vue";
|
||||
import PieEcharts from "@/views/home/echarts/pieEcharts.vue";
|
||||
import DbarEcharts from "@/views/home/echarts/3DbarEcharts.vue";
|
||||
import { qcckPost, qcckGet } from "@/api/qcckApi.js";
|
||||
import { reactive, ref, onMounted } from "vue";
|
||||
|
||||
const dateRange = ref([timeValidate(new Date(), 'ymd'), timeValidate(new Date(), 'ymd')]);// 日期范围
|
||||
const radio = ref(0); //当天
|
||||
const timeList = ref([
|
||||
{ label: "日", num: 0 },
|
||||
{ label: "月", num: 1 },
|
||||
{ label: "季", num: 2 },
|
||||
{ label: "年", num: 3 }
|
||||
]);
|
||||
const listQuery = ref({})
|
||||
const obj = reactive({
|
||||
xsfzList: [],// 线索采集统计
|
||||
xslyList: [],// 线索专题统计
|
||||
ypList: {
|
||||
list: [],//线索来源统计
|
||||
topColor: '#1bd6c2',
|
||||
colors: ["#28EEBF", "#0DBAC5"],
|
||||
},
|
||||
yjdjList: {
|
||||
list: [],//线索来源统计
|
||||
topColor: '#1bd6c2',
|
||||
colors: ["#28EEBF", "#0DBAC5"],
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
init() //初始化数据
|
||||
});
|
||||
|
||||
|
||||
const init = () => {
|
||||
console.log(dateRange.value);
|
||||
let data = {
|
||||
ssbmdm: listQuery.value.ssbmdm,
|
||||
startTime: dateRange.value[0]+' 00:00:00',
|
||||
endTime: dateRange.value[1] + ' 23:59:59',
|
||||
}
|
||||
// 线索分组统计
|
||||
qcckGet(data, '/mosty-gsxt/xxcj/xxfztj').then(res => {
|
||||
let arr = res || [];
|
||||
obj.xsfzList = arr.map(v => {
|
||||
return { label: v.label, value: v.sl }
|
||||
})
|
||||
})
|
||||
//研判统计
|
||||
qcckGet(data, '/mosty-gsxt/ypbg/sjzl/yptj').then(res => {
|
||||
let arr = res || [];
|
||||
obj.ypList.list = arr.map(v => {
|
||||
return { label: v.label, value: v.sl }
|
||||
})
|
||||
})
|
||||
//线索来源统计
|
||||
qcckPost(data, '/mosty-gsxt/qbcj/getXscjTjByQbly').then(res => {
|
||||
let arr = res || [];
|
||||
obj.xslyList = arr.map(v => {
|
||||
return { label: v.zdmc, value: v.count }
|
||||
})
|
||||
})
|
||||
//预警等级
|
||||
const yjdjList = [
|
||||
{ label: '红色预警', key: '01', count: 0 },
|
||||
{ label: '橙色预警', key: '02', count: 0 },
|
||||
{ label: '黄色预警', key: '03', count: 0 },
|
||||
{ label: '蓝色预警', key: '04', count: 0 },
|
||||
]
|
||||
// 预警等级统计
|
||||
qcckGet({ssbmdm:data.ssbmdm,kssj:data.startTime,jssj:data.endTime}, '/mosty-gsxt/tbYjxx/getYjxxTj').then(res => {
|
||||
let arr = res || [];
|
||||
obj.yjdjList.list = yjdjList.map(items => {
|
||||
const index= arr.findIndex(item=>item.yj_jb===items.key);
|
||||
return {
|
||||
label: items.label,
|
||||
value:index==-1?0:arr[index].count
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const changeRadio = (val) => {
|
||||
switch (val) {
|
||||
case 0: //日
|
||||
dateRange.value = timeSlotChange('日')
|
||||
// [timeValidate(), timeValidate()]
|
||||
break;
|
||||
case 1: //月
|
||||
dateRange.value = timeSlotChange('本月')
|
||||
break;
|
||||
case 2: //季度
|
||||
dateRange.value = timeSlotChange('本季度')
|
||||
break;
|
||||
case 3: //年
|
||||
dateRange.value = timeSlotChange('本年')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const handleDateChange = (val) => {
|
||||
radio.value = '';
|
||||
if (val[0] == timeSlotChange('天')[0] && val[1] == timeSlotChange('天')[1]) radio.value = 0;
|
||||
if (val[0] == timeSlotChange('本月')[0] && val[1] == timeSlotChange('本月')[1]) radio.value = 1;
|
||||
if (val[0] == timeSlotChange('本季度')[0] && val[1] == timeSlotChange('本季度')[1]) radio.value = 2;
|
||||
if (val[0] == timeSlotChange('本年')[0] && val[1] == timeSlotChange('本年')[1]) radio.value = 3;
|
||||
};
|
||||
|
||||
// 重置
|
||||
const handleRest = () => {
|
||||
radio.value = 0;
|
||||
dateRange.value = [timeValidate(), timeValidate()];
|
||||
init();
|
||||
};
|
||||
|
||||
// 计算百分比
|
||||
const calculatePercentage = (value) => {
|
||||
if (!obj.xslyList || obj.xslyList.length === 0) return 0;
|
||||
const total = obj.xslyList.reduce((sum, item) => sum + (item.value || 0), 0);
|
||||
if (total === 0) return 0;
|
||||
return Math.round((value / total) * 100);
|
||||
};
|
||||
|
||||
import PageTitle from "@/components/aboutTable/PageTitle.vue";
|
||||
import QbtjCount from './qbtjCount.vue'
|
||||
import YjCount from './yjCount.vue'
|
||||
import XxhjCount from './xxhjCount.vue'
|
||||
import QygktjCount from './qygktjCount.vue'
|
||||
import { ref } from "vue";
|
||||
const butList=ref(["情报统计分析","预警统计",'全域管控统计','信息汇聚统计'])
|
||||
const qh = ref('情报统计分析')
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.statistical-analysis {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
||||
.left-menu {
|
||||
width: 350px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.right-content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
|
||||
.filter-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.charts-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
height: calc(100% - 50px);
|
||||
|
||||
.chart-item {
|
||||
width: 49.5%;
|
||||
height: calc(50% - 5px);
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
.chart-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: calc(100% - 40px);
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-radio {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
::v-deep .el-radio__inner {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::v-deep .el-progress-bar__innerText {
|
||||
color: #333;
|
||||
margin: 0 -40px;
|
||||
}
|
||||
|
||||
::v-deep .el-progress.is-exception .el-progress-bar__inner {
|
||||
background: linear-gradient(90deg, #fe5d00 0%, #face35 100%);
|
||||
}
|
||||
.countBox {
|
||||
height: calc(100vh - 180px);
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -0,0 +1,280 @@
|
||||
<template>
|
||||
<div class="statistical-analysis">
|
||||
<!-- 左侧树形菜单 -->
|
||||
<div class="left-menu">
|
||||
<!-- 这个部分用的是组件-后期替换 -->
|
||||
<MOSTY.DepartmentTree width="310px" @change="init" placeholder="管理部门" clearable filterable :isBmId="false"
|
||||
v-model="listQuery.ssbmdm" />
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容区 -->
|
||||
<div class="right-content">
|
||||
<!-- 顶部筛选 -->
|
||||
<div class="filter-section">
|
||||
<el-radio-group v-model="radio" @change="changeRadio">
|
||||
<el-radio v-for="(it, idx) in timeList" :key="idx" :label="it.num">{{ it.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
<el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期"
|
||||
end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" @change="handleDateChange" />
|
||||
<el-button type="primary" @click="init">查询</el-button>
|
||||
<el-button type="primary" @click="handleRest">重置</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 统计图表区域 -->
|
||||
<div class="charts-container">
|
||||
<div class="chart-item">
|
||||
<div class="chart-title">
|
||||
<span>信息分组统计</span>
|
||||
<!-- <el-button type="primary">导出统计表</el-button> -->
|
||||
</div>
|
||||
<div class="chart">
|
||||
<PieEcharts echartsId="pieChart" color="#333" :data="obj.xsfzList" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-item">
|
||||
<div class="chart-title">
|
||||
<span>预警等级统计</span>
|
||||
<!-- <el-button type="primary">导出统计表</el-button> -->
|
||||
</div>
|
||||
<div class="chart">
|
||||
<!-- {{ obj.yjdjList }} -->
|
||||
<DbarEcharts echartsId="bar3DChart" :data="obj.yjdjList" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-item">
|
||||
<div class="chart-title">
|
||||
<span>线索来源统计</span>
|
||||
<!-- <el-button type="primary">导出统计表</el-button> -->
|
||||
</div>
|
||||
<ul class="chart mt8">
|
||||
<li v-for="(it, idx) in obj.xslyList" :key="idx" class="mb6">
|
||||
<div style="color: #333">{{ it.label }}</div>
|
||||
<el-progress :text-inside="true" :stroke-width="20" :percentage="calculatePercentage(it.value)"
|
||||
status="exception">
|
||||
<span><span style="color: #e37233">{{ it.value }}</span> 个</span>
|
||||
</el-progress>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="chart-item">
|
||||
<div class="chart-title">
|
||||
<span>研判统计</span>
|
||||
<!-- <el-button type="primary">导出统计表</el-button> -->
|
||||
</div>
|
||||
<DbarEcharts echartsId="bar3DCharts" :data="obj.ypList" />
|
||||
<!-- <lineEcharts color="#333" echartsId="areaChart" :data="obj.ypList" /> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { timeValidate, timeSlotChange } from "@/utils/tools.js";
|
||||
import * as MOSTY from "@/components/MyComponents/index";
|
||||
import lineEcharts from "@/views/home/echarts/lineEcharts.vue";
|
||||
import PieEcharts from "@/views/home/echarts/pieEcharts.vue";
|
||||
import DbarEcharts from "@/views/home/echarts/3DbarEcharts.vue";
|
||||
import { qcckPost, qcckGet } from "@/api/qcckApi.js";
|
||||
import { reactive, ref, onMounted } from "vue";
|
||||
|
||||
const dateRange = ref([timeValidate(new Date(), 'ymd'), timeValidate(new Date(), 'ymd')]);// 日期范围
|
||||
const radio = ref(0); //当天
|
||||
const timeList = ref([
|
||||
{ label: "日", num: 0 },
|
||||
{ label: "月", num: 1 },
|
||||
{ label: "季", num: 2 },
|
||||
{ label: "年", num: 3 }
|
||||
]);
|
||||
const listQuery = ref({})
|
||||
const obj = reactive({
|
||||
xsfzList: [],// 线索采集统计
|
||||
xslyList: [],// 线索专题统计
|
||||
ypList: {
|
||||
list: [],//线索来源统计
|
||||
topColor: '#1bd6c2',
|
||||
colors: ["#28EEBF", "#0DBAC5"],
|
||||
},
|
||||
yjdjList: {
|
||||
list: [],//线索来源统计
|
||||
topColor: '#1bd6c2',
|
||||
colors: ["#28EEBF", "#0DBAC5"],
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
init() //初始化数据
|
||||
});
|
||||
|
||||
|
||||
const init = () => {
|
||||
console.log(dateRange.value);
|
||||
let data = {
|
||||
ssbmdm: listQuery.value.ssbmdm,
|
||||
startTime: dateRange.value[0]+' 00:00:00',
|
||||
endTime: dateRange.value[1] + ' 23:59:59',
|
||||
}
|
||||
// 线索分组统计
|
||||
qcckGet(data, '/mosty-gsxt/xxcj/xxfztj').then(res => {
|
||||
let arr = res || [];
|
||||
obj.xsfzList = arr.map(v => {
|
||||
return { label: v.label, value: v.sl }
|
||||
})
|
||||
})
|
||||
//研判统计
|
||||
qcckGet(data, '/mosty-gsxt/ypbg/sjzl/yptj').then(res => {
|
||||
let arr = res || [];
|
||||
obj.ypList.list = arr.map(v => {
|
||||
return { label: v.label, value: v.sl }
|
||||
})
|
||||
})
|
||||
//线索来源统计
|
||||
qcckPost(data, '/mosty-gsxt/qbcj/getXscjTjByQbly').then(res => {
|
||||
let arr = res || [];
|
||||
obj.xslyList = arr.map(v => {
|
||||
return { label: v.zdmc, value: v.count }
|
||||
})
|
||||
})
|
||||
//预警等级
|
||||
const yjdjList = [
|
||||
{ label: '红色预警', key: '01', count: 0 },
|
||||
{ label: '橙色预警', key: '02', count: 0 },
|
||||
{ label: '黄色预警', key: '03', count: 0 },
|
||||
{ label: '蓝色预警', key: '04', count: 0 },
|
||||
]
|
||||
// 预警等级统计
|
||||
qcckGet({ssbmdm:data.ssbmdm,kssj:data.startTime,jssj:data.endTime}, '/mosty-gsxt/tbYjxx/getYjxxTj').then(res => {
|
||||
let arr = res || [];
|
||||
obj.yjdjList.list = yjdjList.map(items => {
|
||||
const index= arr.findIndex(item=>item.yj_jb===items.key);
|
||||
return {
|
||||
label: items.label,
|
||||
value:index==-1?0:arr[index].count
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const changeRadio = (val) => {
|
||||
switch (val) {
|
||||
case 0: //日
|
||||
dateRange.value = timeSlotChange('日')
|
||||
// [timeValidate(), timeValidate()]
|
||||
break;
|
||||
case 1: //月
|
||||
dateRange.value = timeSlotChange('本月')
|
||||
break;
|
||||
case 2: //季度
|
||||
dateRange.value = timeSlotChange('本季度')
|
||||
break;
|
||||
case 3: //年
|
||||
dateRange.value = timeSlotChange('本年')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const handleDateChange = (val) => {
|
||||
radio.value = '';
|
||||
if (val[0] == timeSlotChange('天')[0] && val[1] == timeSlotChange('天')[1]) radio.value = 0;
|
||||
if (val[0] == timeSlotChange('本月')[0] && val[1] == timeSlotChange('本月')[1]) radio.value = 1;
|
||||
if (val[0] == timeSlotChange('本季度')[0] && val[1] == timeSlotChange('本季度')[1]) radio.value = 2;
|
||||
if (val[0] == timeSlotChange('本年')[0] && val[1] == timeSlotChange('本年')[1]) radio.value = 3;
|
||||
};
|
||||
|
||||
// 重置
|
||||
const handleRest = () => {
|
||||
radio.value = 0;
|
||||
dateRange.value = [timeValidate(), timeValidate()];
|
||||
init();
|
||||
};
|
||||
|
||||
// 计算百分比
|
||||
const calculatePercentage = (value) => {
|
||||
if (!obj.xslyList || obj.xslyList.length === 0) return 0;
|
||||
const total = obj.xslyList.reduce((sum, item) => sum + (item.value || 0), 0);
|
||||
if (total === 0) return 0;
|
||||
return Math.round((value / total) * 100);
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.statistical-analysis {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
.left-menu {
|
||||
width: 350px;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.right-content {
|
||||
flex: 1;
|
||||
padding: 0 20px 20px;
|
||||
height: 100%;
|
||||
.filter-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.charts-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
height: calc(100% - 50px);
|
||||
|
||||
.chart-item {
|
||||
width: 49.5%;
|
||||
height: calc(50% - 5px);
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
|
||||
.chart-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chart {
|
||||
height: calc(100% - 40px);
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-radio {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
::v-deep .el-radio__inner {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::v-deep .el-progress-bar__innerText {
|
||||
color: #333;
|
||||
margin: 0 -40px;
|
||||
}
|
||||
|
||||
::v-deep .el-progress.is-exception .el-progress-bar__inner {
|
||||
background: linear-gradient(90deg, #fe5d00 0%, #face35 100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div class="countBox">
|
||||
<div ref="searchRef">
|
||||
<HeadLayout @change="handleChange" />
|
||||
</div>
|
||||
<ul class="cntBox" :style="{ height: boxHeight + 'px'}">
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">人员来源统计</div>
|
||||
</div>
|
||||
<div class="echratsBox">
|
||||
<DbarEcharts echartsId="bar35DChart" :key="ketcount" :data="obj.rylyList" />
|
||||
</div>
|
||||
</li>
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">管控状态统计</div>
|
||||
</div>
|
||||
<div class="echratsBox">
|
||||
<StatusCount :data="obj.gkztList" id="gkztCount" />
|
||||
</div>
|
||||
</li>
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">各类标签管控数量统计</div>
|
||||
</div>
|
||||
<div class="echratsBox"><BqyjslCount :data="obj.bqgkslList"/></div>
|
||||
</li>
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">各部门管控数量统计</div>
|
||||
</div>
|
||||
<div class="echratsBox">
|
||||
<DbarEcharts echartsId="bardepDChart" :key="ketcount" :data="obj.gbmgkList" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import * as echarts from 'echarts'
|
||||
import HeadLayout from './components/headLayout.vue'
|
||||
import BqyjslCount from './components/bqyjslCount.vue'
|
||||
import StatusCount from './components/statusCount.vue'
|
||||
import DbarEcharts from "@/views/home/echarts/3DbarEcharts.vue";
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
const searchRef = ref() //筛选组件实例
|
||||
const boxHeight = ref() //盒子高度
|
||||
const formSearch = ref({}) //查询条件
|
||||
const ketcount = ref(0)
|
||||
const obj = reactive({
|
||||
gkztList: [
|
||||
{ name: '失控', value: 85 ,color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [{ offset: 0, color: '#FF8DA7' },{ offset: 1, color: '#D66A8D' }])},
|
||||
{ name: '在控', value: 55 ,color: '#17C0AE'},
|
||||
],//管控状态统计
|
||||
bqgkslList: [
|
||||
{ label: '涉兼爆炸等恐怖袭击人员', value: 100 },
|
||||
{ label: '全国涉稳重点人', value: 80 },
|
||||
{ label: '国保涉稳人员', value: 70 },
|
||||
{ label: '涉政涉恐涉稳重点人员', value: 60 },
|
||||
{ label: '区涉稳人员', value: 50 },
|
||||
],//各类标签预警数量统计
|
||||
sevenList: [
|
||||
{ label:'涉恐',value:30 },
|
||||
{ label:'涉暴',value:20 },
|
||||
{ label:'涉恐暴',value:10 },
|
||||
{ label:'涉睡',value:30 },
|
||||
{ label:'涉睡暴',value:20 },
|
||||
{ label:'涉睡恐',value:10 },
|
||||
{ label:'涉睡恐暴',value:5 },
|
||||
],//7类重点人员预警统计
|
||||
rylyList: {
|
||||
list: [
|
||||
{ label:'基础库',value:50 },
|
||||
{ label:'重点库',value:40 },
|
||||
{ label:'关注库',value:10 },
|
||||
{ label:'重点群体',value:50 },
|
||||
{ label:'重点车辆',value:40 },
|
||||
],
|
||||
topColor:'#17c8c3',
|
||||
colors: ["#28EEBF","#0DBAC5"],
|
||||
},//人员来源统计
|
||||
gbmgkList: {
|
||||
list: [
|
||||
{ label:'国家保',value:100 },
|
||||
{ label:'省保',value:80 },
|
||||
{ label:'市保',value:70 },
|
||||
{ label:'区保',value:60 },
|
||||
{ label:'其他保',value:50 },
|
||||
],
|
||||
topColor:'#17c8c3',
|
||||
colors: ["#28EEBF","#0DBAC5"],
|
||||
},//各部门管控数量统计
|
||||
})
|
||||
|
||||
onMounted(()=>{
|
||||
tabHeightFn()
|
||||
})
|
||||
|
||||
// 筛选
|
||||
const handleChange = (val) => {
|
||||
formSearch.value = val
|
||||
}
|
||||
|
||||
// 表格高度计算
|
||||
const tabHeightFn = () => {
|
||||
boxHeight.value = window.innerHeight - searchRef.value.offsetHeight - 200;
|
||||
window.onresize = function () {
|
||||
tabHeightFn();
|
||||
};
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.countBox {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
.cntBox{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-content: space-between;
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
.cnt-item{
|
||||
width: 49.8%;
|
||||
height: 49.5%;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
.common-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 5px 20px;
|
||||
box-sizing: border-box;
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -1px;
|
||||
width: 80px;
|
||||
height: 4px;
|
||||
background: linear-gradient(to right, #409EFF, #ffffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
.echratsBox{
|
||||
height: calc(100% - 40px);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="countBox">
|
||||
<div ref="searchRef">
|
||||
<HeadLayout @change="handleChange" />
|
||||
</div>
|
||||
<ul class="cntBox" :style="{ height: boxHeight + 'px'}">
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">采用情况统计</div>
|
||||
</div>
|
||||
<div class="echratsBox">
|
||||
<StatusCount :data="obj.cyqkList" id="cyqkCount" />
|
||||
</div>
|
||||
</li>
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">分组统计</div>
|
||||
</div>
|
||||
<div class="echratsBox">
|
||||
<StatusCount :data="obj.fzList" id="fzCount" />
|
||||
</div>
|
||||
</li>
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">状态统计</div>
|
||||
</div>
|
||||
<div class="echratsBox">
|
||||
<StatusCount :data="obj.ztList" id="ztCount" />
|
||||
</div>
|
||||
</li>
|
||||
<li class="cnt-item cnt-item-last">
|
||||
<div class="common-title">
|
||||
<div class="title">各部门管控数量统计</div>
|
||||
</div>
|
||||
<div class="echratsBox">
|
||||
<DbarEcharts echartsId="bardepDChart" :key="ketcount" :data="obj.bqList" />
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import * as echarts from 'echarts'
|
||||
import HeadLayout from './components/headLayout.vue'
|
||||
import StatusCount from './components/statusCount.vue'
|
||||
import DbarEcharts from "@/views/home/echarts/3DbarEcharts.vue";
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
const searchRef = ref() //筛选组件实例
|
||||
const boxHeight = ref() //盒子高度
|
||||
const formSearch = ref({}) //查询条件
|
||||
const ketcount = ref(0)
|
||||
const obj = reactive({
|
||||
cyqkList: [
|
||||
{ name:'采纳',value:30,color:'#0dbac5' },
|
||||
{ name:'退回',value:20,color:'#ff7700' },
|
||||
],
|
||||
fzList: [
|
||||
{ name:'内部',value:30,color:'#fdbc3a' },
|
||||
{ name:'共享',value:20,color:'#ff7700' },
|
||||
],
|
||||
ztList: [
|
||||
{ name: '送审', value: 85 ,color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [{ offset: 0, color: '#FF8DA7' },{ offset: 1, color: '#D66A8D' }])},
|
||||
{ name: '转线索', value: 55 ,color: '#17C0AE'},
|
||||
],
|
||||
bqList: {
|
||||
list: [
|
||||
{ label:'肇事肇祸',value:100 },
|
||||
{ label:'涉稳人员',value:80 },
|
||||
{ label:'前科人员',value:70 },
|
||||
{ label:'肇事肇祸',value:100 },
|
||||
{ label:'涉稳人员',value:80 },
|
||||
{ label:'前科人员',value:70 },
|
||||
{ label:'肇事肇祸',value:100 },
|
||||
{ label:'涉稳人员',value:80 },
|
||||
{ label:'前科人员',value:70 },
|
||||
{ label:'肇事肇祸',value:100 },
|
||||
{ label:'涉稳人员',value:80 },
|
||||
{ label:'前科人员',value:70 },
|
||||
],
|
||||
topColor:'#17c8c3',
|
||||
colors: ["#28EEBF","#0DBAC5"],
|
||||
},//各部门管控数量统计
|
||||
})
|
||||
|
||||
onMounted(()=>{
|
||||
tabHeightFn()
|
||||
})
|
||||
|
||||
// 筛选
|
||||
const handleChange = (val) => {
|
||||
formSearch.value = val
|
||||
}
|
||||
|
||||
// 表格高度计算
|
||||
const tabHeightFn = () => {
|
||||
boxHeight.value = window.innerHeight - searchRef.value.offsetHeight - 200;
|
||||
window.onresize = function () {
|
||||
tabHeightFn();
|
||||
};
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.countBox {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
.cntBox{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-content: space-between;
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
.cnt-item{
|
||||
width: 33%;
|
||||
height: 49.5%;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
.common-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 5px 20px;
|
||||
box-sizing: border-box;
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -1px;
|
||||
width: 80px;
|
||||
height: 4px;
|
||||
background: linear-gradient(to right, #409EFF, #ffffff);
|
||||
}
|
||||
}
|
||||
}
|
||||
.echratsBox{
|
||||
height: calc(100% - 40px);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.cnt-item-last{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,298 @@
|
||||
<template>
|
||||
<div class="countBox">
|
||||
<div ref="searchRef">
|
||||
<HeadLayout @change="handleChange" />
|
||||
</div>
|
||||
<ul class="countItem">
|
||||
<li v-for="(item, index) in countList" :key="index" class="item">
|
||||
<div class="left">
|
||||
<img :src="item.icon" alt="" class="icon-img">
|
||||
</div>
|
||||
<div class="center">
|
||||
<div class="label">{{ item.label }}</div>
|
||||
<div class="value">{{ item.value }}</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="right">
|
||||
<div class="stat-row">
|
||||
<span class="stat-label">环比</span>
|
||||
<span class="stat-val up">{{ item.hb }} <el-icon><CaretTop /></el-icon></span>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<span class="stat-label">同比</span>
|
||||
<span class="stat-val down">{{ item.tb }} <el-icon><CaretBottom /></el-icon></span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="cntBox" :style="{ height: boxHeight + 'px'}">
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">各部门预警数量统计</div>
|
||||
<div class="btn-group">
|
||||
<div class="btn" :class="{ active: activeTab === '县局' }" @click="activeTab = '县局'">县局</div>
|
||||
<div class="btn" :class="{ active: activeTab === '派出所' }" @click="activeTab = '派出所'">派出所</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="echratsBox">
|
||||
<DbarEcharts echartsId="bar3DChart" :key="ketcount" :data="obj.cnList" />
|
||||
</div>
|
||||
</li>
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">各类标签预警数量统计</div>
|
||||
</div>
|
||||
<div class="echratsBox"><BqyjslCount :data="obj.bqyjslList" /></div>
|
||||
</li>
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">7类重点人员预警统计</div>
|
||||
</div>
|
||||
<div class="echratsBox">
|
||||
<LineEcharts echartsId="seventTypeChart" color="#333333" :data="obj.sevenList" />
|
||||
</div>
|
||||
</li>
|
||||
<li class="cnt-item">
|
||||
<div class="common-title">
|
||||
<div class="title">处置状态统计</div>
|
||||
</div>
|
||||
<div class="echratsBox"><CzztCount /></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import HeadLayout from './components/headLayout.vue'
|
||||
import BqyjslCount from './components/bqyjslCount.vue'
|
||||
import CzztCount from './components/czztCount.vue'
|
||||
import DbarEcharts from "@/views/home/echarts/3DbarEcharts.vue";
|
||||
import LineEcharts from "@/views/home/echarts/lineEcharts.vue";
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { CaretTop, CaretBottom } from '@element-plus/icons-vue'
|
||||
const searchRef = ref() //筛选组件实例
|
||||
const boxHeight = ref() //盒子高度
|
||||
const formSearch = ref({}) //查询条件
|
||||
const activeTab = ref('县局')
|
||||
const ketcount = ref(0)
|
||||
const countList = ref([
|
||||
{ label: '红色预警', value: '123,4', hb: '106,3', tb: '106,3', icon: require('@/assets/images/icon-red.png') },
|
||||
{ label: '橙色预警', value: '123,4', hb: '106,3', tb: '106,3', icon: require('@/assets/images/icon-orange.png') },
|
||||
{ label: '黄色预警', value: '123,4', hb: '106,3', tb: '106,3', icon: require('@/assets/images/icon-yellow.png') },
|
||||
{ label: '蓝色预警', value: '123,4', hb: '106,3', tb: '106,3', icon: require('@/assets/images/icon-blue.png') },
|
||||
])
|
||||
const obj = reactive({
|
||||
bqyjslList: [
|
||||
{ label: '涉毒人员', value: 100 },
|
||||
{ label: '前科人员', value: 80 },
|
||||
{ label: '肇事肇祸', value: 70 },
|
||||
{ label: '涉稳人员', value: 60 },
|
||||
{ label: '涉稳人员', value: 50 },
|
||||
],//各类标签预警数量统计
|
||||
sevenList: [
|
||||
{ label:'涉恐',value:30 },
|
||||
{ label:'涉暴',value:20 },
|
||||
{ label:'涉恐暴',value:10 },
|
||||
{ label:'涉睡',value:30 },
|
||||
{ label:'涉睡暴',value:20 },
|
||||
{ label:'涉睡恐',value:10 },
|
||||
{ label:'涉睡恐暴',value:5 },
|
||||
],//7类重点人员预警统计
|
||||
cnList: {
|
||||
list: [
|
||||
{ label:'工布江达县',value:50 },
|
||||
{ label:'米林市',value:40 },
|
||||
{ label:'墨脱县',value:10 },
|
||||
{ label:'波密县',value:50 },
|
||||
{ label:'察隅县',value:40 },
|
||||
{ label:'郎县',value:10 },
|
||||
{ label:'雅安分局',value:40 },
|
||||
],
|
||||
topColor:'#17c8c3',
|
||||
colors: ["#28EEBF","#0DBAC5"],
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(()=>{
|
||||
tabHeightFn()
|
||||
})
|
||||
|
||||
// 筛选
|
||||
const handleChange = (val) => {
|
||||
formSearch.value = val
|
||||
}
|
||||
|
||||
// 表格高度计算
|
||||
const tabHeightFn = () => {
|
||||
boxHeight.value = window.innerHeight - searchRef.value.offsetHeight - 300;
|
||||
window.onresize = function () {
|
||||
tabHeightFn();
|
||||
};
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.countBox {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
.countItem{
|
||||
height: 100px;
|
||||
background: transparent;
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.item {
|
||||
width: 24.5%;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.05);
|
||||
|
||||
.left {
|
||||
margin-right: 15px;
|
||||
.icon-img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
flex: 1;
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 40px;
|
||||
background: #eee;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
|
||||
.stat-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #999;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.stat-val {
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.up {
|
||||
color: #F56C6C;
|
||||
}
|
||||
&.down {
|
||||
color: #67C23A; // Green for down in Chinese stocks usually, but commonly Red is up/bad, Green is down/good or vice versa. Image shows Red Up, Green Down.
|
||||
// Image: Top one (red arrow) is Red. Bottom one (green arrow) is Green.
|
||||
// 106,3 Red Arrow Up.
|
||||
// 106,3 Green Arrow Down.
|
||||
color: #00CC99; // Using a teal/green color often used in designs. Or #67C23A (Element Success).
|
||||
// Let's use #00C853 or similar.
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cntBox{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-content: space-between;
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
.cnt-item{
|
||||
width: 49.8%;
|
||||
height: 49.5%;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
.common-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 5px 20px;
|
||||
box-sizing: border-box;
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
position: relative;
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -1px;
|
||||
width: 80px;
|
||||
height: 4px;
|
||||
background: linear-gradient(to right, #409EFF, #ffffff);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
.btn {
|
||||
padding: 3px 16px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
transition: all 0.3s;
|
||||
user-select: none;
|
||||
|
||||
&.active {
|
||||
background: #409EFF;
|
||||
color: #fff;
|
||||
border-color: #409EFF;
|
||||
}
|
||||
|
||||
&:hover:not(.active) {
|
||||
color: #409EFF;
|
||||
border-color: #c6e2ff;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.echratsBox{
|
||||
height: calc(100% - 40px);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -101,7 +101,6 @@ function chartFn() {
|
||||
|
||||
// 安全获取标签数据
|
||||
const labels = props.data.list && Array.isArray(props.data.list) ? props.data.list.map(v => v.label) : [];
|
||||
console.log(labels);
|
||||
|
||||
const option = {
|
||||
grid: {
|
||||
@ -198,7 +197,7 @@ function chartFn() {
|
||||
symbolSize: [barWidth - 4, 10],
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: colorArr[2]
|
||||
color: props.data.topColor || colorArr[2]
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
|
||||
@ -41,8 +41,8 @@ function chartFn() {
|
||||
grid: {
|
||||
top: "8%",
|
||||
right: "2%",
|
||||
left: "10%",
|
||||
bottom: "12%",
|
||||
left: "2%",
|
||||
bottom: "8%",
|
||||
containLabel:true
|
||||
},
|
||||
tooltip: {
|
||||
|
||||
Reference in New Issue
Block a user