数据看板优化

This commit is contained in:
chenlin
2025-11-27 18:12:53 +08:00
parent 9d2ae9b0b4
commit 26e1f9a181
3 changed files with 1163 additions and 1751 deletions

View File

@@ -7,7 +7,8 @@ import {
MapChart, MapChart,
PictorialBarChart, PictorialBarChart,
RadarChart, RadarChart,
GaugeChart GaugeChart,
CandlestickChart
} from 'echarts/charts' } from 'echarts/charts'
import { import {
@@ -41,7 +42,8 @@ echarts.use([
CanvasRenderer, CanvasRenderer,
PictorialBarChart, PictorialBarChart,
RadarChart, RadarChart,
GaugeChart GaugeChart,
CandlestickChart
]) ])
export default echarts export default echarts

View File

@@ -3,6 +3,9 @@
<!-- 顶部标题栏 --> <!-- 顶部标题栏 -->
<div class="header-container"> <div class="header-container">
<div class="header-left"> <div class="header-left">
<el-icon class="back-arrow" @click="returnToHeadquarters">
<ArrowLeft />
</el-icon>
<div class="back-button" @click="openRegionSelector"> <div class="back-button" @click="openRegionSelector">
{{ selectedPark || selectedRegion }} {{ selectedPark || selectedRegion }}
<span>···</span> <span>···</span>
@@ -138,9 +141,9 @@
<el-button type="text" class="manage-btn">管理</el-button> <el-button type="text" class="manage-btn">管理</el-button>
</div> </div>
<div class="card-content"> <div class="card-content">
<div class="high-risk-content"> <div class="high-risk-top">
<div class="donut-chart-wrapper-small"> <div class="donut-chart-wrapper-small">
<Echart :options="highRiskChartOption" width="100%" height="180px" /> <Echart :options="highRiskChartOption" width="100%" height="250px" />
</div> </div>
<div class="operation-type-list"> <div class="operation-type-list">
<div class="operation-type-item" v-for="item in operationTypeDistribution" :key="item.type"> <div class="operation-type-item" v-for="item in operationTypeDistribution" :key="item.type">
@@ -174,9 +177,9 @@
<el-button type="text" class="manage-btn">管理</el-button> <el-button type="text" class="manage-btn">管理</el-button>
</div> </div>
<div class="card-content"> <div class="card-content">
<div class="emergency-plan-content"> <div class="emergency-plan-top">
<div class="progress-chart-wrapper"> <div class="progress-chart-wrapper">
<Echart :options="emergencyPlanChartOption" width="100%" height="180px" /> <Echart :options="emergencyPlanChartOption" width="100%" height="250px" />
</div> </div>
<div class="drill-info"> <div class="drill-info">
<div class="drill-item"> <div class="drill-item">
@@ -243,7 +246,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { Refresh } from '@element-plus/icons-vue' import { Refresh, ArrowLeft } from '@element-plus/icons-vue'
import Echart from '@/components/Echart/src/Echart.vue' import Echart from '@/components/Echart/src/Echart.vue'
import RegionSelector from '@/views/screen/components/RegionSelector.vue' import RegionSelector from '@/views/screen/components/RegionSelector.vue'
import { getTableList } from '@/api/design/report' import { getTableList } from '@/api/design/report'
@@ -262,6 +265,8 @@ interface RegionItem {
interface DistributionItem { interface DistributionItem {
region?: string region?: string
park?: string park?: string
level?: string
type?: string
count: number count: number
percent?: string percent?: string
color: string color: string
@@ -323,7 +328,11 @@ const emergencyPlanCompleted = ref<number>(0)
const parkDrillProgress = ref<DistributionItem[]>([]) const parkDrillProgress = ref<DistributionItem[]>([])
// 安全培训数据 // 安全培训数据
const trainingChartData = ref<any>({}) const trainingBarData = ref<{ regions: string[]; trainingCount: number[]; participants: number[] }>({
regions: [],
trainingCount: [],
participants: []
})
const parkTrainingProgress = ref<DistributionItem[]>([]) const parkTrainingProgress = ref<DistributionItem[]>([])
// 区域颜色配置 // 区域颜色配置
@@ -366,6 +375,13 @@ const initRegionData = async () => {
} }
} }
// 返回总部页面
const returnToHeadquarters = () => {
router.push({
path: '/index'
})
}
// 打开区域选择器 // 打开区域选择器
const openRegionSelector = (): void => { const openRegionSelector = (): void => {
regionSelectorVisible.value = true regionSelectorVisible.value = true
@@ -375,7 +391,7 @@ const openRegionSelector = (): void => {
const onRegionChange = (item: RegionItem): void => { const onRegionChange = (item: RegionItem): void => {
selectedPark.value = item.name selectedPark.value = item.name
router.push({ router.push({
path: '/home/park', path: '/park',
query: { region: selectedRegion.value, regionCode: route.query.regionCode, park: item.name, parkCode: item.code } query: { region: selectedRegion.value, regionCode: route.query.regionCode, park: item.name, parkCode: item.code }
}) })
} }
@@ -487,9 +503,9 @@ const initHighRiskData = async () => {
] ]
parkOperationDistribution.value = [ parkOperationDistribution.value = [
{ park: '雄安园区', count: 42 }, { park: '雄安园区', count: 42, color: '#3b82f6' },
{ park: '重庆园区', count: 31 }, { park: '重庆园区', count: 31, color: '#8b5cf6' },
{ park: '北京园区', count: 21 } { park: '北京园区', count: 21, color: '#06b6d4' }
] ]
} }
@@ -499,25 +515,25 @@ const initEmergencyPlanData = async () => {
emergencyPlanTotal.value = 36 emergencyPlanTotal.value = 36
emergencyPlanCompleted.value = 28 emergencyPlanCompleted.value = 28
parkDrillProgress.value = [ parkDrillProgress.value = [
{ park: '雄安园区', percent: '85%', color: '#10b981' }, { park: '雄安园区', percent: '85%', color: '#10b981', count: 0 },
{ park: '重庆园区', percent: '78%', color: '#10b981' }, { park: '重庆园区', percent: '78%', color: '#10b981', count: 0 },
{ park: '北京园区', percent: '70%', color: '#10b981' } { park: '北京园区', percent: '70%', color: '#10b981', count: 0 }
] ]
} }
// 初始化安全培训数据 // 初始化安全培训数据
const initSafetyTrainingData = async () => { const initSafetyTrainingData = async () => {
// TODO: 调用安全培训API // TODO: 调用安全培训API
trainingChartData.value = { trainingBarData.value = {
parks: ['雄安', '重庆', '北京'], regions: ['雄安园区', '重庆园区', '北京园区'],
trainingCount: [12, 10, 8], trainingCount: [25, 15, 12],
participants: [25, 15, 12] participants: [12, 10, 8]
} }
parkTrainingProgress.value = [ parkTrainingProgress.value = [
{ park: '雄安园区', percent: '92%', color: '#8b5cf6' }, { park: '雄安园区', percent: '92%', color: '#8b5cf6', count: 0 },
{ park: '重庆园区', percent: '88%', color: '#8b5cf6' }, { park: '重庆园区', percent: '88%', color: '#8b5cf6', count: 0 },
{ park: '北京园区', percent: '85%', color: '#8b5cf6' } { park: '北京园区', percent: '85%', color: '#8b5cf6', count: 0 }
] ]
} }
@@ -651,19 +667,19 @@ const highRiskChartOption = computed<EChartsOption>(() => {
series: [{ series: [{
name: '高危作业', name: '高危作业',
type: 'pie', type: 'pie',
radius: ['55%', '75%'], radius: ['60%', '75%'],
center: ['50%', '50%'], center: ['50%', '45%'],
avoidLabelOverlap: false, avoidLabelOverlap: false,
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 }, itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
label: { label: {
show: true, show: true,
position: 'center', position: 'center',
formatter: () => `${highRiskTotal.value}\n本月作业`, formatter: () => `${highRiskTotal.value}\n本月作业`,
fontSize: 16, fontSize: 18,
fontWeight: 'bold', fontWeight: 'bold',
color: '#333' color: '#333'
}, },
emphasis: { label: { show: true, fontSize: 18, fontWeight: 'bold' } }, emphasis: { label: { show: true, fontSize: 20, fontWeight: 'bold' } },
data: operationTypeDistribution.value.map(item => ({ data: operationTypeDistribution.value.map(item => ({
value: item.count, value: item.count,
name: item.type, name: item.type,
@@ -684,15 +700,15 @@ const emergencyPlanChartOption = computed<EChartsOption>(() => {
series: [{ series: [{
name: '演练完成率', name: '演练完成率',
type: 'pie', type: 'pie',
radius: ['55%', '75%'], radius: ['60%', '75%'],
center: ['50%', '50%'], center: ['50%', '45%'],
avoidLabelOverlap: false, avoidLabelOverlap: false,
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0, color: '#10b981' }, itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0, color: '#10b981' },
label: { label: {
show: true, show: true,
position: 'center', position: 'center',
formatter: () => `${percent}%\n演练完成率`, formatter: () => `${percent}%\n演练完成率`,
fontSize: 16, fontSize: 18,
fontWeight: 'bold', fontWeight: 'bold',
color: '#333' color: '#333'
}, },
@@ -712,24 +728,33 @@ const safetyTrainingChartOption = computed<EChartsOption>(() => {
data: ['培训次数', '参与人次'], data: ['培训次数', '参与人次'],
top: 10 top: 10
}, },
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, grid: { left: '6%', right: '4%', bottom: '8%', containLabel: true },
xAxis: { xAxis: {
type: 'category', type: 'category',
data: trainingChartData.value.parks || [] data: trainingBarData.value.regions,
axisLine: { lineStyle: { color: '#d1d5db' } }
},
yAxis: {
type: 'value',
axisLine: { lineStyle: { color: '#d1d5db' } },
splitLine: { lineStyle: { color: '#f3f4f6' } }
}, },
yAxis: { type: 'value' },
series: [ series: [
{ {
name: '培训次数', name: '培训次数',
type: 'bar', type: 'bar',
data: trainingChartData.value.trainingCount || [], data: trainingBarData.value.trainingCount,
itemStyle: { color: '#8b5cf6' } barWidth: 24,
itemStyle: { color: '#8b5cf6' },
label: { show: true, position: 'insideBottom', color: '#fff' }
}, },
{ {
name: '参与人次', name: '参与人次',
type: 'bar', type: 'bar',
data: trainingChartData.value.participants || [], data: trainingBarData.value.participants,
itemStyle: { color: '#3b82f6' } barWidth: 24,
itemStyle: { color: '#c4b5fd' },
label: { show: true, position: 'top', color: '#7c3aed' }
} }
] ]
} }
@@ -774,7 +799,19 @@ onMounted(() => {
.header-left { .header-left {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 15px; gap: 10px;
}
.back-arrow {
font-size: 20px;
color: #3b82f6;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
color: #2563eb;
transform: translateX(-2px);
}
} }
.back-button { .back-button {
@@ -981,17 +1018,27 @@ onMounted(() => {
} }
} }
.high-risk-content { .high-risk-top {
display: flex; display: flex;
align-items: center;
gap: 15px; gap: 15px;
margin-bottom: 15px; margin-bottom: 15px;
} }
.donut-chart-wrapper-small {
flex: 1.5;
min-width: 0;
display: flex;
align-items: center;
}
.operation-type-list { .operation-type-list {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 8px;
min-width: 0;
justify-content: center;
} }
.operation-type-item { .operation-type-item {
@@ -1001,8 +1048,8 @@ onMounted(() => {
} }
.operation-name { .operation-name {
width: 80px; width: 70px;
font-size: 13px; font-size: 12px;
color: #666; color: #666;
flex-shrink: 0; flex-shrink: 0;
} }
@@ -1020,27 +1067,37 @@ onMounted(() => {
border-radius: 4px; border-radius: 4px;
} }
.emergency-plan-content { .emergency-plan-top {
display: flex; display: flex;
align-items: center;
gap: 15px; gap: 15px;
margin-bottom: 15px; margin-bottom: 15px;
} }
.progress-chart-wrapper {
flex: 1.5;
min-width: 0;
display: flex;
align-items: center;
}
.drill-info { .drill-info {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 8px;
flex: 1; flex: 1;
min-width: 0;
justify-content: center;
} }
.drill-item { .drill-item {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 10px; padding: 8px 10px;
background-color: #f0fdf4; background-color: #f0fdf4;
border-radius: 4px; border-radius: 4px;
font-size: 13px; font-size: 12px;
color: #666; color: #666;
} }

File diff suppressed because it is too large Load Diff