风险管理
This commit is contained in:
@@ -2,12 +2,17 @@
|
|||||||
<div class="dashboard-container">
|
<div class="dashboard-container">
|
||||||
<!-- 顶部标题栏 -->
|
<!-- 顶部标题栏 -->
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<div class="header-left">
|
<HeaderSelector
|
||||||
<div class="back-button" @click="openRegionSelector">
|
back-button-type="none"
|
||||||
{{ currentView }}
|
:show-back-button="false"
|
||||||
<span>···</span>
|
:display-text="currentView"
|
||||||
</div>
|
:clickable="true"
|
||||||
</div>
|
:show-selector-indicator="true"
|
||||||
|
selector-type="region"
|
||||||
|
:selected-value="selectedRegion"
|
||||||
|
theme="light"
|
||||||
|
@selector-change="onRegionChange"
|
||||||
|
/>
|
||||||
<!-- <h1 class="header-title">集团视角数据看板</h1> -->
|
<!-- <h1 class="header-title">集团视角数据看板</h1> -->
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="date-range-wrapper">
|
<div class="date-range-wrapper">
|
||||||
@@ -293,9 +298,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 区域选择弹窗 -->
|
|
||||||
<RegionSelector v-model="regionSelectorVisible" :modelSelected="selectedRegion" :regions="regionOption"
|
|
||||||
@change="onRegionChange" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -304,7 +306,7 @@ 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 } 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 HeaderSelector from '@/views/screen/components/HeaderSelector.vue'
|
||||||
import { getTableList } from '@/api/design/report'
|
import { getTableList } from '@/api/design/report'
|
||||||
import type { EChartsOption } from 'echarts'
|
import type { EChartsOption } from 'echarts'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@@ -349,9 +351,7 @@ const route = useRoute()
|
|||||||
|
|
||||||
// 区域选择相关
|
// 区域选择相关
|
||||||
const currentView = ref('集团')
|
const currentView = ref('集团')
|
||||||
const regionSelectorVisible = ref(false)
|
|
||||||
const selectedRegion = ref('')
|
const selectedRegion = ref('')
|
||||||
const regionOption = ref<RegionItem[]>([])
|
|
||||||
|
|
||||||
// 时间选择相关 - 默认当前月起止,如果路由中有日期范围参数则使用
|
// 时间选择相关 - 默认当前月起止,如果路由中有日期范围参数则使用
|
||||||
const getCurrentMonthRange = () => {
|
const getCurrentMonthRange = () => {
|
||||||
@@ -523,6 +523,21 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||||
},
|
},
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${outsourcingTotal.value}\n外协人员总数`,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '外协人员',
|
name: '外协人员',
|
||||||
@@ -536,14 +551,7 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'center',
|
|
||||||
formatter: () => {
|
|
||||||
return `${outsourcingTotal.value}\n外协人员总数`
|
|
||||||
},
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
data: [
|
data: [
|
||||||
{ value: 1, name: '暂无数据', itemStyle: { color: '#e5e7eb' } }
|
{ value: 1, name: '暂无数据', itemStyle: { color: '#e5e7eb' } }
|
||||||
@@ -558,6 +566,21 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||||
},
|
},
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${outsourcingTotal.value}\n外协人员总数`,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '外协人员',
|
name: '外协人员',
|
||||||
@@ -571,19 +594,12 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
borderWidth: 0
|
borderWidth: 0
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'center',
|
|
||||||
formatter: () => {
|
|
||||||
return `${outsourcingTotal.value}\n外协人员总数`
|
|
||||||
},
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
emphasis: {
|
emphasis: {
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
fontSize: 18,
|
fontSize: 14,
|
||||||
fontWeight: 'bold'
|
fontWeight: 'bold'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -595,18 +611,29 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
|
|
||||||
// 风险管理环形图配置
|
// 风险管理环形图配置
|
||||||
const riskChartOption = computed<EChartsOption>(() => {
|
const riskChartOption = computed<EChartsOption>(() => {
|
||||||
const chartData = riskDistribution.value
|
const chartData = riskDistribution.value.map(item => ({
|
||||||
.filter(item => item.count > 0) // 只显示有数据的风险等级
|
|
||||||
.map(item => ({
|
|
||||||
value: item.count,
|
value: item.count,
|
||||||
name: item.level,
|
name: item.level,
|
||||||
itemStyle: { color: item.color }
|
itemStyle: { color: item.color }
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 如果没有数据,显示空状态
|
|
||||||
if (chartData.length === 0 || riskTotal.value === 0) {
|
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${riskTotal.value}\n风险总数`,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '风险',
|
name: '风险',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -616,35 +643,51 @@ const riskChartOption = computed<EChartsOption>(() => {
|
|||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'center',
|
alignTo: 'edge',
|
||||||
formatter: () => `${riskTotal.value}\n风险总数`,
|
formatter: (params: any) => {
|
||||||
fontSize: 16,
|
const value = Number(params.value) || 0
|
||||||
fontWeight: 'bold',
|
const total = Number(riskTotal.value) || 0
|
||||||
|
if (total === 0) {
|
||||||
|
return '0%'
|
||||||
|
}
|
||||||
|
const percent = ((value / total) * 100).toFixed(2) + '%'
|
||||||
|
return percent
|
||||||
|
},
|
||||||
|
minMargin: 5,
|
||||||
|
edgeDistance: 10,
|
||||||
|
lineHeight: 15,
|
||||||
|
fontSize: 12,
|
||||||
color: '#333'
|
color: '#333'
|
||||||
},
|
},
|
||||||
data: [{ value: 1, name: '暂无数据', itemStyle: { color: '#e5e7eb' } }]
|
labelLine: {
|
||||||
}]
|
show: true,
|
||||||
|
length: 15,
|
||||||
|
length2: 10,
|
||||||
|
maxSurfaceAngle: 80
|
||||||
|
},
|
||||||
|
labelLayout: function (params: any) {
|
||||||
|
const isLeft = params.labelRect.x < params.labelRect.width
|
||||||
|
const points = params.labelLinePoints
|
||||||
|
if (points && points.length >= 3) {
|
||||||
|
// 调整线条的终点位置
|
||||||
|
if (isLeft) {
|
||||||
|
points[2][0] = params.labelRect.x
|
||||||
|
} else {
|
||||||
|
points[2][0] = params.labelRect.x + params.labelRect.width
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
labelLinePoints: points
|
||||||
series: [{
|
}
|
||||||
name: '风险',
|
}
|
||||||
type: 'pie',
|
return {}
|
||||||
radius: ['55%', '75%'],
|
},
|
||||||
center: ['50%', '50%'],
|
emphasis: {
|
||||||
avoidLabelOverlap: false,
|
|
||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'center',
|
fontSize: 14,
|
||||||
formatter: () => `${riskTotal.value}\n风险总数`,
|
fontWeight: 'bold'
|
||||||
fontSize: 16,
|
}
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
emphasis: { label: { show: true, fontSize: 18, fontWeight: 'bold' } },
|
|
||||||
data: chartData
|
data: chartData
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@@ -783,6 +826,21 @@ const highRiskChartOption = computed<EChartsOption>(() => {
|
|||||||
if (chartData.length === 0 || highRiskTotal.value === 0) {
|
if (chartData.length === 0 || highRiskTotal.value === 0) {
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${highRiskTotal.value}\n累计作业`,
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '高危作业',
|
name: '高危作业',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -791,12 +849,7 @@ const highRiskChartOption = computed<EChartsOption>(() => {
|
|||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'center',
|
|
||||||
formatter: () => `${highRiskTotal.value}\n累计作业`,
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
data: [{ value: 1, name: '暂无数据', itemStyle: { color: '#e5e7eb' } }]
|
data: [{ value: 1, name: '暂无数据', itemStyle: { color: '#e5e7eb' } }]
|
||||||
}]
|
}]
|
||||||
@@ -805,6 +858,21 @@ const highRiskChartOption = computed<EChartsOption>(() => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${highRiskTotal.value}\n累计作业`,
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '高危作业',
|
name: '高危作业',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -813,14 +881,9 @@ const highRiskChartOption = computed<EChartsOption>(() => {
|
|||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'center',
|
|
||||||
formatter: () => `${highRiskTotal.value}\n累计作业`,
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
emphasis: { label: { show: true, fontSize: 20, fontWeight: 'bold' } },
|
emphasis: { label: { show: true, fontSize: 16, fontWeight: 'bold' } },
|
||||||
data: chartData
|
data: chartData
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@@ -1002,10 +1065,6 @@ const navigateToModule = (path: string): void => {
|
|||||||
router.push(path)
|
router.push(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
const openRegionSelector = (): void => {
|
|
||||||
regionSelectorVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const onRegionChange = (item: RegionItem): void => {
|
const onRegionChange = (item: RegionItem): void => {
|
||||||
selectedRegion.value = item.name
|
selectedRegion.value = item.name
|
||||||
router.push({
|
router.push({
|
||||||
@@ -1030,28 +1089,6 @@ const refreshData = async () => {
|
|||||||
initData()
|
initData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化区域数据
|
|
||||||
const initRegionData = async () => {
|
|
||||||
try {
|
|
||||||
const { records } = await getTableList('park_info_list')
|
|
||||||
if (records && records.length > 0) {
|
|
||||||
// 去重region字段,使用Map来确保唯一性
|
|
||||||
const regionMap = new Map<string, RegionItem>()
|
|
||||||
records.forEach((el: any) => {
|
|
||||||
if (!regionMap.has(el.region)) {
|
|
||||||
regionMap.set(el.region, {
|
|
||||||
name: el.region,
|
|
||||||
code: el.region_id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// 转换为数组
|
|
||||||
regionOption.value = Array.from(regionMap.values())
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('初始化区域数据失败:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化风险管理数据
|
// 初始化风险管理数据
|
||||||
const initRiskData = async () => {
|
const initRiskData = async () => {
|
||||||
@@ -1108,10 +1145,10 @@ const initRiskData = async () => {
|
|||||||
|
|
||||||
// 处理风险等级分布数据(用于环形图)
|
// 处理风险等级分布数据(用于环形图)
|
||||||
const allLevels = [
|
const allLevels = [
|
||||||
{ key: '低', level: '低风险', count: 0, percent: '0%', color: '#10b981' },
|
{ key: '低', level: '低风险', count: 0, percent: '0%', color: '#117cee' },
|
||||||
{ key: '一般', level: '一般风险', count: 0, percent: '0%', color: '#f59e0b' },
|
{ key: '一般', level: '一般风险', count: 0, percent: '0%', color: '#fbde28' },
|
||||||
{ key: '较大', level: '较大风险', count: 0, percent: '0%', color: '#ef4444' },
|
{ key: '较大', level: '较大风险', count: 0, percent: '0%', color: '#ed740c' },
|
||||||
{ key: '重大', level: '重大风险', count: 0, percent: '0%', color: '#dc2626' }
|
{ key: '重大', level: '重大风险', count: 0, percent: '0%', color: '#df2a3f' }
|
||||||
]
|
]
|
||||||
|
|
||||||
riskDistribution.value = allLevels.map(defaultItem => {
|
riskDistribution.value = allLevels.map(defaultItem => {
|
||||||
@@ -1150,10 +1187,10 @@ const initRiskData = async () => {
|
|||||||
// 如果没有数据,设置为默认值
|
// 如果没有数据,设置为默认值
|
||||||
riskTotal.value = 0
|
riskTotal.value = 0
|
||||||
riskDistribution.value = [
|
riskDistribution.value = [
|
||||||
{ level: '低风险', count: 0, percent: '0%', color: '#10b981' },
|
{ level: '低风险', count: 0, percent: '0%', color: '#117cee' },
|
||||||
{ level: '一般风险', count: 0, percent: '0%', color: '#f59e0b' },
|
{ level: '一般风险', count: 0, percent: '0%', color: '#fbde28' },
|
||||||
{ level: '较大风险', count: 0, percent: '0%', color: '#ef4444' },
|
{ level: '较大风险', count: 0, percent: '0%', color: '#ed740c' },
|
||||||
{ level: '重大风险', count: 0, percent: '0%', color: '#dc2626' }
|
{ level: '重大风险', count: 0, percent: '0%', color: '#df2a3f' }
|
||||||
]
|
]
|
||||||
areaRiskDistribution.value = []
|
areaRiskDistribution.value = []
|
||||||
console.log('风险管理无数据')
|
console.log('风险管理无数据')
|
||||||
@@ -1163,10 +1200,10 @@ const initRiskData = async () => {
|
|||||||
// 如果接口失败,设置为默认值
|
// 如果接口失败,设置为默认值
|
||||||
riskTotal.value = 0
|
riskTotal.value = 0
|
||||||
riskDistribution.value = [
|
riskDistribution.value = [
|
||||||
{ level: '低风险', count: 0, percent: '0%', color: '#10b981' },
|
{ level: '低风险', count: 0, percent: '0%', color: '#117cee' },
|
||||||
{ level: '一般风险', count: 0, percent: '0%', color: '#f59e0b' },
|
{ level: '一般风险', count: 0, percent: '0%', color: '#fbde28' },
|
||||||
{ level: '较大风险', count: 0, percent: '0%', color: '#ef4444' },
|
{ level: '较大风险', count: 0, percent: '0%', color: '#ed740c' },
|
||||||
{ level: '重大风险', count: 0, percent: '0%', color: '#dc2626' }
|
{ level: '重大风险', count: 0, percent: '0%', color: '#df2a3f' }
|
||||||
]
|
]
|
||||||
areaRiskDistribution.value = []
|
areaRiskDistribution.value = []
|
||||||
}
|
}
|
||||||
@@ -1558,7 +1595,6 @@ const initTrainingData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const initData = async () => {
|
const initData = async () => {
|
||||||
await initRegionData()
|
|
||||||
initOutsourcingData()
|
initOutsourcingData()
|
||||||
|
|
||||||
initRiskData()
|
initRiskData()
|
||||||
|
|||||||
@@ -2,15 +2,19 @@
|
|||||||
<div class="dashboard-container">
|
<div class="dashboard-container">
|
||||||
<!-- 顶部标题栏 -->
|
<!-- 顶部标题栏 -->
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<div class="header-left">
|
<HeaderSelector
|
||||||
<el-icon class="back-arrow" @click="returnToHeadquarters">
|
back-button-type="image"
|
||||||
<ArrowLeft />
|
:show-back-button="true"
|
||||||
</el-icon>
|
:on-back="returnToHeadquarters"
|
||||||
<div class="region-name-clickable" @click="openParkSelector">
|
:display-text="selectedRegion"
|
||||||
{{ selectedRegion }}
|
:clickable="true"
|
||||||
<span>···</span>
|
:show-selector-indicator="true"
|
||||||
</div>
|
selector-type="park"
|
||||||
</div>
|
:selected-value="selectedPark"
|
||||||
|
:region-code="route.query.regionCode as string"
|
||||||
|
theme="light"
|
||||||
|
@selector-change="onParkChange"
|
||||||
|
/>
|
||||||
<!-- <h1 class="header-title">区域视角数据看板</h1> -->
|
<!-- <h1 class="header-title">区域视角数据看板</h1> -->
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="date-range-wrapper">
|
<div class="date-range-wrapper">
|
||||||
@@ -293,16 +297,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 园区选择弹窗 -->
|
|
||||||
<RegionSelector v-model="parkSelectorVisible" :modelSelected="selectedPark" :regions="parkOption"
|
|
||||||
@change="onParkChange" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<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, ArrowLeft } from '@element-plus/icons-vue'
|
import { Refresh } from '@element-plus/icons-vue'
|
||||||
import Echart from '@/components/Echart/src/Echart.vue'
|
import Echart from '@/components/Echart/src/Echart.vue'
|
||||||
import type { EChartsOption } from 'echarts'
|
import type { EChartsOption } from 'echarts'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@@ -316,8 +317,7 @@ import {
|
|||||||
getHiddenDangerManagementDataRegionWeek,
|
getHiddenDangerManagementDataRegionWeek,
|
||||||
getHiddenDangerManagementDataRegionMonth
|
getHiddenDangerManagementDataRegionMonth
|
||||||
} from '@/api'
|
} from '@/api'
|
||||||
import RegionSelector from '@/views/screen/components/RegionSelector.vue'
|
import HeaderSelector from '@/views/screen/components/HeaderSelector.vue'
|
||||||
import { getTableList } from '@/api/design/report'
|
|
||||||
|
|
||||||
defineOptions({ name: 'Home12' })
|
defineOptions({ name: 'Home12' })
|
||||||
|
|
||||||
@@ -358,8 +358,6 @@ const route = useRoute()
|
|||||||
// 区域和园区选择相关
|
// 区域和园区选择相关
|
||||||
const selectedRegion = ref<string>('')
|
const selectedRegion = ref<string>('')
|
||||||
const selectedPark = ref<string>('')
|
const selectedPark = ref<string>('')
|
||||||
const parkSelectorVisible = ref<boolean>(false)
|
|
||||||
const parkOption = ref<ParkItem[]>([])
|
|
||||||
|
|
||||||
// 外协管理:/person/table/view/1959187451673116674
|
// 外协管理:/person/table/view/1959187451673116674
|
||||||
// 风险管理:/fx/table/view/1978723750599790594
|
// 风险管理:/fx/table/view/1978723750599790594
|
||||||
@@ -447,36 +445,11 @@ const operationTypeColors: Record<string, string> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化区域数据 - 加载园区列表
|
// 初始化区域数据 - 加载园区列表
|
||||||
|
// 初始化区域数据 - 只设置选中区域,园区列表由 HeaderSelector 组件内部管理
|
||||||
const initRegionData = async () => {
|
const initRegionData = async () => {
|
||||||
try {
|
|
||||||
if (typeof route.query.region === 'string') {
|
if (typeof route.query.region === 'string') {
|
||||||
selectedRegion.value = route.query.region
|
selectedRegion.value = route.query.region
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载园区列表
|
|
||||||
const { records } = await getTableList('park_info_list')
|
|
||||||
|
|
||||||
if (records && records.length > 0) {
|
|
||||||
// 根据regionCode过滤园区
|
|
||||||
const regionCode = route.query.regionCode as string
|
|
||||||
const parkMap = new Map<string, ParkItem>()
|
|
||||||
|
|
||||||
records
|
|
||||||
.filter((el: any) => el.region_id == regionCode)
|
|
||||||
.forEach((el: any) => {
|
|
||||||
if (!parkMap.has(el.park_name)) {
|
|
||||||
parkMap.set(el.park_name, {
|
|
||||||
name: el.park_name,
|
|
||||||
code: el.park_code
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
parkOption.value = Array.from(parkMap.values())
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('初始化区域数据失败:', error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回总部页面
|
// 返回总部页面
|
||||||
@@ -490,15 +463,9 @@ const returnToHeadquarters = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开园区选择器
|
|
||||||
const openParkSelector = (): void => {
|
|
||||||
parkSelectorVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 园区选择变化 - 跳转到园区页面
|
// 园区选择变化 - 跳转到园区页面
|
||||||
const onParkChange = (item: ParkItem): void => {
|
const onParkChange = (item: ParkItem): void => {
|
||||||
selectedPark.value = item.name
|
selectedPark.value = item.name
|
||||||
parkSelectorVisible.value = false
|
|
||||||
router.push({
|
router.push({
|
||||||
path: '/park',
|
path: '/park',
|
||||||
query: {
|
query: {
|
||||||
@@ -630,10 +597,10 @@ const initRiskData = async () => {
|
|||||||
|
|
||||||
// 处理风险等级分布数据(用于环形图)
|
// 处理风险等级分布数据(用于环形图)
|
||||||
const allLevels = [
|
const allLevels = [
|
||||||
{ key: '低', level: '低风险', count: 0, percent: '0%', color: '#10b981' },
|
{ key: '低', level: '低风险', count: 0, percent: '0%', color: '#117cee' },
|
||||||
{ key: '一般', level: '一般风险', count: 0, percent: '0%', color: '#f59e0b' },
|
{ key: '一般', level: '一般风险', count: 0, percent: '0%', color: '#fbde28' },
|
||||||
{ key: '较大', level: '较大风险', count: 0, percent: '0%', color: '#ef4444' },
|
{ key: '较大', level: '较大风险', count: 0, percent: '0%', color: '#ed740c' },
|
||||||
{ key: '重大', level: '重大风险', count: 0, percent: '0%', color: '#dc2626' }
|
{ key: '重大', level: '重大风险', count: 0, percent: '0%', color: '#df2a3f' }
|
||||||
]
|
]
|
||||||
|
|
||||||
riskDistribution.value = allLevels.map(defaultItem => {
|
riskDistribution.value = allLevels.map(defaultItem => {
|
||||||
@@ -671,10 +638,10 @@ const initRiskData = async () => {
|
|||||||
} else {
|
} else {
|
||||||
riskTotal.value = 0
|
riskTotal.value = 0
|
||||||
riskDistribution.value = [
|
riskDistribution.value = [
|
||||||
{ level: '低风险', count: 0, percent: '0%', color: '#10b981' },
|
{ level: '低风险', count: 0, percent: '0%', color: '#117cee' },
|
||||||
{ level: '一般风险', count: 0, percent: '0%', color: '#f59e0b' },
|
{ level: '一般风险', count: 0, percent: '0%', color: '#fbde28' },
|
||||||
{ level: '较大风险', count: 0, percent: '0%', color: '#ef4444' },
|
{ level: '较大风险', count: 0, percent: '0%', color: '#ed740c' },
|
||||||
{ level: '重大风险', count: 0, percent: '0%', color: '#dc2626' }
|
{ level: '重大风险', count: 0, percent: '0%', color: '#df2a3f' }
|
||||||
]
|
]
|
||||||
parkRiskDistribution.value = []
|
parkRiskDistribution.value = []
|
||||||
console.log('区域风险管理无数据')
|
console.log('区域风险管理无数据')
|
||||||
@@ -683,10 +650,10 @@ const initRiskData = async () => {
|
|||||||
console.error('获取区域风险管理数据失败:', error)
|
console.error('获取区域风险管理数据失败:', error)
|
||||||
riskTotal.value = 0
|
riskTotal.value = 0
|
||||||
riskDistribution.value = [
|
riskDistribution.value = [
|
||||||
{ level: '低风险', count: 0, percent: '0%', color: '#10b981' },
|
{ level: '低风险', count: 0, percent: '0%', color: '#117cee' },
|
||||||
{ level: '一般风险', count: 0, percent: '0%', color: '#f59e0b' },
|
{ level: '一般风险', count: 0, percent: '0%', color: '#fbde28' },
|
||||||
{ level: '较大风险', count: 0, percent: '0%', color: '#ef4444' },
|
{ level: '较大风险', count: 0, percent: '0%', color: '#ed740c' },
|
||||||
{ level: '重大风险', count: 0, percent: '0%', color: '#dc2626' }
|
{ level: '重大风险', count: 0, percent: '0%', color: '#df2a3f' }
|
||||||
]
|
]
|
||||||
parkRiskDistribution.value = []
|
parkRiskDistribution.value = []
|
||||||
}
|
}
|
||||||
@@ -1092,6 +1059,21 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
if (chartData.length === 0 || outsourcingTotal.value === 0) {
|
if (chartData.length === 0 || outsourcingTotal.value === 0) {
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${outsourcingTotal.value}\n外协人员总数`,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '外协人员',
|
name: '外协人员',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -1100,12 +1082,7 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'center',
|
|
||||||
formatter: () => `${outsourcingTotal.value}\n外协人员总数`,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
data: [{ value: 1, name: '暂无数据', itemStyle: { color: '#e5e7eb' } }]
|
data: [{ value: 1, name: '暂无数据', itemStyle: { color: '#e5e7eb' } }]
|
||||||
}]
|
}]
|
||||||
@@ -1114,6 +1091,21 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${outsourcingTotal.value}\n外协人员总数`,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '外协人员',
|
name: '外协人员',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -1122,14 +1114,9 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'center',
|
|
||||||
formatter: () => `${outsourcingTotal.value}\n外协人员总数`,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
emphasis: { label: { show: true, fontSize: 18, fontWeight: 'bold' } },
|
emphasis: { label: { show: true, fontSize: 14, fontWeight: 'bold' } },
|
||||||
data: chartData
|
data: chartData
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@@ -1145,6 +1132,21 @@ const riskChartOption = computed<EChartsOption>(() => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${riskTotal.value}\n风险总数`,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '风险',
|
name: '风险',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -1154,13 +1156,51 @@ const riskChartOption = computed<EChartsOption>(() => {
|
|||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'center',
|
alignTo: 'edge',
|
||||||
formatter: () => `${riskTotal.value}\n风险总数`,
|
formatter: (params: any) => {
|
||||||
fontSize: 16,
|
const value = Number(params.value) || 0
|
||||||
fontWeight: 'bold',
|
const total = Number(riskTotal.value) || 0
|
||||||
|
if (total === 0) {
|
||||||
|
return '0%'
|
||||||
|
}
|
||||||
|
const percent = ((value / total) * 100).toFixed(2) + '%'
|
||||||
|
return percent
|
||||||
|
},
|
||||||
|
minMargin: 5,
|
||||||
|
edgeDistance: 10,
|
||||||
|
lineHeight: 15,
|
||||||
|
fontSize: 12,
|
||||||
color: '#333'
|
color: '#333'
|
||||||
},
|
},
|
||||||
emphasis: { label: { show: true, fontSize: 18, fontWeight: 'bold' } },
|
labelLine: {
|
||||||
|
show: true,
|
||||||
|
length: 15,
|
||||||
|
length2: 10,
|
||||||
|
maxSurfaceAngle: 80
|
||||||
|
},
|
||||||
|
labelLayout: function (params: any) {
|
||||||
|
const isLeft = params.labelRect.x < params.labelRect.width
|
||||||
|
const points = params.labelLinePoints
|
||||||
|
if (points && points.length >= 3) {
|
||||||
|
// 调整线条的终点位置
|
||||||
|
if (isLeft) {
|
||||||
|
points[2][0] = params.labelRect.x
|
||||||
|
} else {
|
||||||
|
points[2][0] = params.labelRect.x + params.labelRect.width
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
labelLinePoints: points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
data: chartData
|
data: chartData
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@@ -1237,6 +1277,21 @@ const hiddenDangerChartOption = computed<EChartsOption>(() => {
|
|||||||
const highRiskChartOption = computed<EChartsOption>(() => {
|
const highRiskChartOption = computed<EChartsOption>(() => {
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${highRiskTotal.value}\n累计作业`,
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '高危作业',
|
name: '高危作业',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -1245,14 +1300,9 @@ const highRiskChartOption = computed<EChartsOption>(() => {
|
|||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'center',
|
|
||||||
formatter: () => `${highRiskTotal.value}\n累计作业`,
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
emphasis: { label: { show: true, fontSize: 20, fontWeight: 'bold' } },
|
emphasis: { label: { show: true, fontSize: 16, fontWeight: 'bold' } },
|
||||||
data: operationTypeDistribution.value.map(item => ({
|
data: operationTypeDistribution.value.map(item => ({
|
||||||
value: item.count,
|
value: item.count,
|
||||||
name: item.type,
|
name: item.type,
|
||||||
|
|||||||
@@ -2,12 +2,16 @@
|
|||||||
<div class="dashboard-container">
|
<div class="dashboard-container">
|
||||||
<!-- 顶部标题栏 -->
|
<!-- 顶部标题栏 -->
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<div class="header-left">
|
<HeaderSelector
|
||||||
<el-icon class="back-arrow" @click="returnToRegion">
|
back-button-type="image"
|
||||||
<ArrowLeft />
|
:show-back-button="true"
|
||||||
</el-icon>
|
:on-back="returnToRegion"
|
||||||
<div class="park-name">{{ selectedPark }}</div>
|
:display-text="selectedPark"
|
||||||
</div>
|
:clickable="false"
|
||||||
|
:show-selector-indicator="false"
|
||||||
|
selector-type="none"
|
||||||
|
theme="light"
|
||||||
|
/>
|
||||||
<!-- <h1 class="header-title">园区视角数据看板</h1> -->
|
<!-- <h1 class="header-title">园区视角数据看板</h1> -->
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<div class="date-range-wrapper">
|
<div class="date-range-wrapper">
|
||||||
@@ -297,7 +301,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, ArrowLeft } from '@element-plus/icons-vue'
|
import { Refresh } from '@element-plus/icons-vue'
|
||||||
import Echart from '@/components/Echart/src/Echart.vue'
|
import Echart from '@/components/Echart/src/Echart.vue'
|
||||||
import type { EChartsOption } from 'echarts'
|
import type { EChartsOption } from 'echarts'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@@ -311,6 +315,7 @@ import {
|
|||||||
getHiddenDangerManagementDataParkWeek,
|
getHiddenDangerManagementDataParkWeek,
|
||||||
getHiddenDangerManagementDataParkMonth
|
getHiddenDangerManagementDataParkMonth
|
||||||
} from '@/api'
|
} from '@/api'
|
||||||
|
import HeaderSelector from '@/views/screen/components/HeaderSelector.vue'
|
||||||
|
|
||||||
defineOptions({ name: 'Home13' })
|
defineOptions({ name: 'Home13' })
|
||||||
|
|
||||||
@@ -580,10 +585,10 @@ const initRiskData = async () => {
|
|||||||
|
|
||||||
// 处理风险等级分布数据(用于环形图)
|
// 处理风险等级分布数据(用于环形图)
|
||||||
const allLevels = [
|
const allLevels = [
|
||||||
{ key: '低', level: '低风险', count: 0, percent: '0%', color: '#10b981' },
|
{ key: '低', level: '低风险', count: 0, percent: '0%', color: '#117cee' },
|
||||||
{ key: '一般', level: '一般风险', count: 0, percent: '0%', color: '#f59e0b' },
|
{ key: '一般', level: '一般风险', count: 0, percent: '0%', color: '#fbde28' },
|
||||||
{ key: '较大', level: '较大风险', count: 0, percent: '0%', color: '#ef4444' },
|
{ key: '较大', level: '较大风险', count: 0, percent: '0%', color: '#ed740c' },
|
||||||
{ key: '重大', level: '重大风险', count: 0, percent: '0%', color: '#dc2626' }
|
{ key: '重大', level: '重大风险', count: 0, percent: '0%', color: '#df2a3f' }
|
||||||
]
|
]
|
||||||
|
|
||||||
riskDistribution.value = allLevels.map(defaultItem => {
|
riskDistribution.value = allLevels.map(defaultItem => {
|
||||||
@@ -621,10 +626,10 @@ const initRiskData = async () => {
|
|||||||
} else {
|
} else {
|
||||||
riskTotal.value = 0
|
riskTotal.value = 0
|
||||||
riskDistribution.value = [
|
riskDistribution.value = [
|
||||||
{ level: '低风险', count: 0, percent: '0%', color: '#10b981' },
|
{ level: '低风险', count: 0, percent: '0%', color: '#117cee' },
|
||||||
{ level: '一般风险', count: 0, percent: '0%', color: '#f59e0b' },
|
{ level: '一般风险', count: 0, percent: '0%', color: '#fbde28' },
|
||||||
{ level: '较大风险', count: 0, percent: '0%', color: '#ef4444' },
|
{ level: '较大风险', count: 0, percent: '0%', color: '#ed740c' },
|
||||||
{ level: '重大风险', count: 0, percent: '0%', color: '#dc2626' }
|
{ level: '重大风险', count: 0, percent: '0%', color: '#df2a3f' }
|
||||||
]
|
]
|
||||||
locationRiskDistribution.value = []
|
locationRiskDistribution.value = []
|
||||||
console.log('园区风险管理无数据')
|
console.log('园区风险管理无数据')
|
||||||
@@ -633,10 +638,10 @@ const initRiskData = async () => {
|
|||||||
console.error('获取园区风险管理数据失败:', error)
|
console.error('获取园区风险管理数据失败:', error)
|
||||||
riskTotal.value = 0
|
riskTotal.value = 0
|
||||||
riskDistribution.value = [
|
riskDistribution.value = [
|
||||||
{ level: '低风险', count: 0, percent: '0%', color: '#10b981' },
|
{ level: '低风险', count: 0, percent: '0%', color: '#117cee' },
|
||||||
{ level: '一般风险', count: 0, percent: '0%', color: '#f59e0b' },
|
{ level: '一般风险', count: 0, percent: '0%', color: '#fbde28' },
|
||||||
{ level: '较大风险', count: 0, percent: '0%', color: '#ef4444' },
|
{ level: '较大风险', count: 0, percent: '0%', color: '#ed740c' },
|
||||||
{ level: '重大风险', count: 0, percent: '0%', color: '#dc2626' }
|
{ level: '重大风险', count: 0, percent: '0%', color: '#df2a3f' }
|
||||||
]
|
]
|
||||||
locationRiskDistribution.value = []
|
locationRiskDistribution.value = []
|
||||||
}
|
}
|
||||||
@@ -1013,6 +1018,21 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
if (chartData.length === 0 || outsourcingTotal.value === 0) {
|
if (chartData.length === 0 || outsourcingTotal.value === 0) {
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${outsourcingTotal.value}\n外协人员总数`,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '外协人员',
|
name: '外协人员',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -1021,12 +1041,7 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'center',
|
|
||||||
formatter: () => `${outsourcingTotal.value}\n外协人员总数`,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
data: [{ value: 1, name: '暂无数据', itemStyle: { color: '#e5e7eb' } }]
|
data: [{ value: 1, name: '暂无数据', itemStyle: { color: '#e5e7eb' } }]
|
||||||
}]
|
}]
|
||||||
@@ -1035,6 +1050,21 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${outsourcingTotal.value}\n外协人员总数`,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '外协人员',
|
name: '外协人员',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -1043,14 +1073,9 @@ const outsourcingChartOption = computed<EChartsOption>(() => {
|
|||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'center',
|
|
||||||
formatter: () => `${outsourcingTotal.value}\n外协人员总数`,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
emphasis: { label: { show: true, fontSize: 18, fontWeight: 'bold' } },
|
emphasis: { label: { show: true, fontSize: 14, fontWeight: 'bold' } },
|
||||||
data: chartData
|
data: chartData
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@@ -1066,6 +1091,21 @@ const riskChartOption = computed<EChartsOption>(() => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${riskTotal.value}\n风险总数`,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '风险',
|
name: '风险',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -1075,13 +1115,51 @@ const riskChartOption = computed<EChartsOption>(() => {
|
|||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: true,
|
||||||
position: 'center',
|
alignTo: 'edge',
|
||||||
formatter: () => `${riskTotal.value}\n风险总数`,
|
formatter: (params: any) => {
|
||||||
fontSize: 16,
|
const value = Number(params.value) || 0
|
||||||
fontWeight: 'bold',
|
const total = Number(riskTotal.value) || 0
|
||||||
|
if (total === 0) {
|
||||||
|
return '0%'
|
||||||
|
}
|
||||||
|
const percent = ((value / total) * 100).toFixed(2) + '%'
|
||||||
|
return percent
|
||||||
|
},
|
||||||
|
minMargin: 5,
|
||||||
|
edgeDistance: 10,
|
||||||
|
lineHeight: 15,
|
||||||
|
fontSize: 12,
|
||||||
color: '#333'
|
color: '#333'
|
||||||
},
|
},
|
||||||
emphasis: { label: { show: true, fontSize: 18, fontWeight: 'bold' } },
|
labelLine: {
|
||||||
|
show: true,
|
||||||
|
length: 15,
|
||||||
|
length2: 10,
|
||||||
|
maxSurfaceAngle: 80
|
||||||
|
},
|
||||||
|
labelLayout: function (params: any) {
|
||||||
|
const isLeft = params.labelRect.x < params.labelRect.width
|
||||||
|
const points = params.labelLinePoints
|
||||||
|
if (points && points.length >= 3) {
|
||||||
|
// 调整线条的终点位置
|
||||||
|
if (isLeft) {
|
||||||
|
points[2][0] = params.labelRect.x
|
||||||
|
} else {
|
||||||
|
points[2][0] = params.labelRect.x + params.labelRect.width
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
labelLinePoints: points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
data: chartData
|
data: chartData
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@@ -1159,6 +1237,21 @@ const hiddenDangerChartOption = computed<EChartsOption>(() => {
|
|||||||
const highRiskChartOption = computed<EChartsOption>(() => {
|
const highRiskChartOption = computed<EChartsOption>(() => {
|
||||||
return {
|
return {
|
||||||
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
||||||
|
graphic: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
left: 'center',
|
||||||
|
top: 'center',
|
||||||
|
style: {
|
||||||
|
text: `${highRiskTotal.value}\n累计作业`,
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
fill: '#333',
|
||||||
|
textAlign: 'center',
|
||||||
|
textVerticalAlign: 'middle'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
series: [{
|
series: [{
|
||||||
name: '高危作业',
|
name: '高危作业',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
@@ -1167,14 +1260,9 @@ const highRiskChartOption = computed<EChartsOption>(() => {
|
|||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
itemStyle: { borderRadius: 0, borderColor: 'transparent', borderWidth: 0 },
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false
|
||||||
position: 'center',
|
|
||||||
formatter: () => `${highRiskTotal.value}\n累计作业`,
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
color: '#333'
|
|
||||||
},
|
},
|
||||||
emphasis: { label: { show: true, fontSize: 20, fontWeight: 'bold' } },
|
emphasis: { label: { show: true, fontSize: 16, fontWeight: 'bold' } },
|
||||||
data: operationTypeDistribution.value.map(item => ({
|
data: operationTypeDistribution.value.map(item => ({
|
||||||
value: item.count,
|
value: item.count,
|
||||||
name: item.type,
|
name: item.type,
|
||||||
|
|||||||
@@ -2,10 +2,15 @@
|
|||||||
<div class="dashboard-container">
|
<div class="dashboard-container">
|
||||||
<!-- 顶部标题栏 -->
|
<!-- 顶部标题栏 -->
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<div class="header-left">
|
<HeaderSelector
|
||||||
<img class="back-img" @click="returnToHeadquarters" src="@/assets/images/screen/back_image.png" />
|
back-button-type="image"
|
||||||
<div class="back-button"> {{ selectedPark }} </div>
|
:show-back-button="true"
|
||||||
</div>
|
:on-back="returnToHeadquarters"
|
||||||
|
:display-text="selectedPark"
|
||||||
|
:clickable="false"
|
||||||
|
:show-selector-indicator="false"
|
||||||
|
selector-type="none"
|
||||||
|
/>
|
||||||
<h1 class="header-title">{{ selectedPark }}综合监控大屏</h1>
|
<h1 class="header-title">{{ selectedPark }}综合监控大屏</h1>
|
||||||
<div class="date-wrapper">
|
<div class="date-wrapper">
|
||||||
<span style="margin-top: 6%;font-size: 0.9rem;">{{ currentDate }}</span>
|
<span style="margin-top: 6%;font-size: 0.9rem;">{{ currentDate }}</span>
|
||||||
@@ -257,6 +262,7 @@ import WeatherWarning from './components/WeatherWarning.vue'
|
|||||||
import AlertList from './components/AlertList.vue'
|
import AlertList from './components/AlertList.vue'
|
||||||
import { getTableList, getTableData, getDangerDetail, getDangerCount, getExamDetail, getDrillDetail, getWorkOrderStatistics } from './report'
|
import { getTableList, getTableData, getDangerDetail, getDangerCount, getExamDetail, getDrillDetail, getWorkOrderStatistics } from './report'
|
||||||
import RiskStatisticsPanel from './components/RiskStatisticsPanel.vue'
|
import RiskStatisticsPanel from './components/RiskStatisticsPanel.vue'
|
||||||
|
import HeaderSelector from './components/HeaderSelector.vue'
|
||||||
|
|
||||||
interface PointPosition {
|
interface PointPosition {
|
||||||
label: string
|
label: string
|
||||||
|
|||||||
398
src/views/screen/components/HeaderSelector.vue
Normal file
398
src/views/screen/components/HeaderSelector.vue
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="['header-left', `theme-${theme}`]">
|
||||||
|
<!-- 返回按钮 - PNG图片类型(暗色系主题使用,保持原样) -->
|
||||||
|
<img
|
||||||
|
v-if="backButtonType === 'image' && showBackButton && theme === 'dark'"
|
||||||
|
class="back-img"
|
||||||
|
@click="handleBack"
|
||||||
|
src="@/assets/images/screen/back_image.png"
|
||||||
|
/>
|
||||||
|
<!-- 返回按钮 - SVG图片类型(亮色系主题使用,支持主题颜色) -->
|
||||||
|
<svg
|
||||||
|
v-if="backButtonType === 'image' && showBackButton && theme === 'light'"
|
||||||
|
class="back-svg"
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
@click="handleBack"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M620.8 348.16H276.48l102.4-102.4c10.24-10.24 10.24-25.6 0-35.84-10.24-10.24-25.6-10.24-35.84 0L197.12 354.56c-5.12 5.12-7.68 11.52-7.68 17.92 0 6.4 2.56 12.8 7.68 17.92l144.64 144.64c10.24 10.24 25.6 10.24 35.84 0 10.24-10.24 10.24-25.6 0-35.84L277.76 399.36h343.04C716.8 399.36 793.6 476.16 793.6 572.16S716.8 744.96 620.8 744.96H358.4c-14.08 0-25.6 11.52-25.6 25.6s11.52 25.6 25.6 25.6h262.4a223.4368 223.4368 0 0 0 224-224A223.4368 223.4368 0 0 0 620.8 348.16z"
|
||||||
|
:fill="svgFillColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<!-- 返回按钮 - 图标类型 -->
|
||||||
|
<el-icon
|
||||||
|
v-if="backButtonType === 'icon' && showBackButton"
|
||||||
|
class="back-arrow"
|
||||||
|
@click="handleBack"
|
||||||
|
>
|
||||||
|
<ArrowLeft />
|
||||||
|
</el-icon>
|
||||||
|
<!-- 文本按钮/显示 -->
|
||||||
|
<div
|
||||||
|
v-if="displayText"
|
||||||
|
:class="[
|
||||||
|
'back-button',
|
||||||
|
{
|
||||||
|
'clickable': clickable,
|
||||||
|
'non-clickable': !clickable
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
@click="handleTextClick"
|
||||||
|
>
|
||||||
|
{{ displayText }}
|
||||||
|
<span v-if="showSelectorIndicator">···</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 区域/园区选择弹窗 -->
|
||||||
|
<RegionSelector
|
||||||
|
v-model="selectorVisible"
|
||||||
|
:modelSelected="selectedValue"
|
||||||
|
:regions="options"
|
||||||
|
@change="handleSelectorChange"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, watch, computed } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { ArrowLeft } from '@element-plus/icons-vue'
|
||||||
|
import RegionSelector from './RegionSelector.vue'
|
||||||
|
import { getTableList } from '@/api/design/report'
|
||||||
|
|
||||||
|
interface OptionItem {
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
// 返回按钮类型:'image' | 'icon' | 'none'
|
||||||
|
backButtonType?: 'image' | 'icon' | 'none'
|
||||||
|
// 是否显示返回按钮
|
||||||
|
showBackButton?: boolean
|
||||||
|
// 返回按钮点击事件
|
||||||
|
onBack?: () => void
|
||||||
|
// 显示的文本
|
||||||
|
displayText?: string
|
||||||
|
// 文本是否可点击
|
||||||
|
clickable?: boolean
|
||||||
|
// 是否显示选择器指示器(···)
|
||||||
|
showSelectorIndicator?: boolean
|
||||||
|
// 选择器类型:'region' | 'park' | 'none'
|
||||||
|
selectorType?: 'region' | 'park' | 'none'
|
||||||
|
// 选择器选项(如果不提供,会根据 selectorType 自动获取)
|
||||||
|
options?: OptionItem[]
|
||||||
|
// 当前选中的值
|
||||||
|
selectedValue?: string
|
||||||
|
// 选择器变化事件
|
||||||
|
onSelectorChange?: (item: OptionItem) => void
|
||||||
|
// 区域代码(用于获取园区列表)
|
||||||
|
regionCode?: string
|
||||||
|
// 主题:'light' | 'dark'
|
||||||
|
theme?: 'light' | 'dark'
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
backButtonType: 'none',
|
||||||
|
showBackButton: false,
|
||||||
|
clickable: true,
|
||||||
|
showSelectorIndicator: false,
|
||||||
|
selectorType: 'none',
|
||||||
|
options: () => [],
|
||||||
|
selectedValue: '',
|
||||||
|
regionCode: '',
|
||||||
|
theme: 'dark'
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
selectorChange: [item: OptionItem]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const selectorVisible = ref(false)
|
||||||
|
const options = ref<OptionItem[]>(props.options || [])
|
||||||
|
|
||||||
|
// 根据主题计算 SVG 填充颜色
|
||||||
|
const svgFillColor = computed(() => {
|
||||||
|
return props.theme === 'light' ? '#409eff' : '#ffffff'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听 props.options 的变化
|
||||||
|
watch(() => props.options, (newOptions) => {
|
||||||
|
if (newOptions && newOptions.length > 0) {
|
||||||
|
options.value = newOptions
|
||||||
|
}
|
||||||
|
}, { immediate: true, deep: true })
|
||||||
|
|
||||||
|
// 缓存工具函数
|
||||||
|
const CACHE_KEY = 'shared_regionOption_cache'
|
||||||
|
|
||||||
|
interface CacheData {
|
||||||
|
records: any[]
|
||||||
|
timestamp: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCachedRegionOption = (): any[] | null => {
|
||||||
|
try {
|
||||||
|
const cached = sessionStorage.getItem(CACHE_KEY)
|
||||||
|
if (cached) {
|
||||||
|
const cacheData: CacheData = JSON.parse(cached)
|
||||||
|
return cacheData.records
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('读取缓存失败:', error)
|
||||||
|
sessionStorage.removeItem(CACHE_KEY)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const setCachedRegionOption = (records: any[]) => {
|
||||||
|
try {
|
||||||
|
const cacheData: CacheData = {
|
||||||
|
records,
|
||||||
|
timestamp: Date.now()
|
||||||
|
}
|
||||||
|
sessionStorage.setItem(CACHE_KEY, JSON.stringify(cacheData))
|
||||||
|
console.log('regionOption 数据已缓存')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存缓存失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化选项数据 - 统一从内部获取,不再依赖外部传入
|
||||||
|
const initOptions = async () => {
|
||||||
|
// 如果外部传入了 options,优先使用(保留兼容性)
|
||||||
|
if (props.options && props.options.length > 0) {
|
||||||
|
options.value = props.options
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.selectorType === 'none') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 先检查缓存
|
||||||
|
const cachedRecords = getCachedRegionOption()
|
||||||
|
let records = cachedRecords
|
||||||
|
|
||||||
|
if (!records || records.length === 0) {
|
||||||
|
// 缓存不存在或已过期,调用接口
|
||||||
|
const result = await getTableList('park_info_list')
|
||||||
|
records = result.records || []
|
||||||
|
|
||||||
|
if (records && records.length > 0) {
|
||||||
|
// 保存到缓存
|
||||||
|
setCachedRegionOption(records)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (records && records.length > 0) {
|
||||||
|
if (props.selectorType === 'region') {
|
||||||
|
// 区域选择:去重region字段
|
||||||
|
const regionMap = new Map()
|
||||||
|
records.forEach((el: any) => {
|
||||||
|
if (!regionMap.has(el.region)) {
|
||||||
|
regionMap.set(el.region, {
|
||||||
|
name: el.region,
|
||||||
|
code: el.region_id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
options.value = Array.from(regionMap.values())
|
||||||
|
} else if (props.selectorType === 'park') {
|
||||||
|
// 园区选择:根据regionCode过滤,去重park_name字段
|
||||||
|
if (!props.regionCode) {
|
||||||
|
// 如果 regionCode 还没有值,等待它被设置
|
||||||
|
console.log('等待 regionCode 设置...')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const parkMap = new Map()
|
||||||
|
const filteredRecords = records.filter((el: any) => el.region_id == props.regionCode)
|
||||||
|
if (filteredRecords.length === 0) {
|
||||||
|
console.warn(`未找到 regionCode 为 ${props.regionCode} 的园区数据`)
|
||||||
|
}
|
||||||
|
filteredRecords.forEach((el: any) => {
|
||||||
|
if (!parkMap.has(el.park_name)) {
|
||||||
|
parkMap.set(el.park_name, {
|
||||||
|
name: el.park_name,
|
||||||
|
code: el.park_code
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
options.value = Array.from(parkMap.values())
|
||||||
|
console.log('园区选项已更新:', options.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化选项数据失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理返回按钮点击
|
||||||
|
const handleBack = () => {
|
||||||
|
if (props.onBack) {
|
||||||
|
props.onBack()
|
||||||
|
} else {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理文本点击
|
||||||
|
const handleTextClick = () => {
|
||||||
|
if (!props.clickable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.selectorType !== 'none') {
|
||||||
|
selectorVisible.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理选择器变化
|
||||||
|
const handleSelectorChange = (item: OptionItem) => {
|
||||||
|
if (props.onSelectorChange) {
|
||||||
|
props.onSelectorChange(item)
|
||||||
|
}
|
||||||
|
// 同时发出事件,供父组件使用 @selector-change 监听
|
||||||
|
emit('selectorChange', item)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initOptions()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听 regionCode 的变化,当它变化时重新初始化选项(用于园区选择)
|
||||||
|
watch(() => props.regionCode, (newRegionCode) => {
|
||||||
|
if (props.selectorType === 'park' && newRegionCode) {
|
||||||
|
initOptions()
|
||||||
|
}
|
||||||
|
}, { immediate: false })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.header-left {
|
||||||
|
display: flex;
|
||||||
|
padding-left: 1vw;
|
||||||
|
line-height: 80px;
|
||||||
|
flex: 1;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.back-img {
|
||||||
|
height: 3vh;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-svg {
|
||||||
|
height: 3vh;
|
||||||
|
width: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-arrow {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #409eff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #79bbff;
|
||||||
|
transform: translateX(-2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-button {
|
||||||
|
display: inline-flex;
|
||||||
|
height: 2vh;
|
||||||
|
min-width: 6vw;
|
||||||
|
padding: 4px 16px;
|
||||||
|
margin-left: 0.5vw;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.non-clickable {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暗色系主题(默认)
|
||||||
|
&.theme-dark {
|
||||||
|
.back-button {
|
||||||
|
background: rgb(13 24 84 / 80%);
|
||||||
|
border: 1px solid rgb(59 130 246 / 40%);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgb(59 130 246 / 30%);
|
||||||
|
border-color: rgb(59 130 246 / 60%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 亮色系主题 - 只改颜色,保持UI结构
|
||||||
|
&.theme-light {
|
||||||
|
.back-button {
|
||||||
|
background: #409eff;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #79bbff;
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (width <= 1024px) {
|
||||||
|
.header-left .back-button {
|
||||||
|
min-width: 8vw;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 768px) {
|
||||||
|
.header-left {
|
||||||
|
line-height: 70px;
|
||||||
|
|
||||||
|
.back-button {
|
||||||
|
min-width: 12vw;
|
||||||
|
padding: 3px 12px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width <= 480px) {
|
||||||
|
.header-left {
|
||||||
|
line-height: 60px;
|
||||||
|
|
||||||
|
.back-button {
|
||||||
|
min-width: 15vw;
|
||||||
|
padding: 2px 10px;
|
||||||
|
font-size: 0.65rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -2,11 +2,16 @@
|
|||||||
<div class="dashboard-container">
|
<div class="dashboard-container">
|
||||||
<!-- 顶部标题栏 -->
|
<!-- 顶部标题栏 -->
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<div class="header-left">
|
<HeaderSelector
|
||||||
<div class="back-button" @click="openRegionSelector"> 集团
|
back-button-type="none"
|
||||||
<span>···</span>
|
:show-back-button="false"
|
||||||
</div>
|
display-text="集团"
|
||||||
</div>
|
:clickable="true"
|
||||||
|
:show-selector-indicator="true"
|
||||||
|
selector-type="region"
|
||||||
|
:selected-value="selectedRegion"
|
||||||
|
@selector-change="onRegionChange"
|
||||||
|
/>
|
||||||
<h1 class="header-title">总部综合监控大屏</h1>
|
<h1 class="header-title">总部综合监控大屏</h1>
|
||||||
<div class="date-wrapper">
|
<div class="date-wrapper">
|
||||||
<span style="margin-top: 6%;font-size: 0.9rem;">{{ currentDate }}</span>
|
<span style="margin-top: 6%;font-size: 0.9rem;">{{ currentDate }}</span>
|
||||||
@@ -41,17 +46,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 区域选择弹窗 -->
|
|
||||||
<RegionSelector v-model="regionSelectorVisible" :modelSelected="selectedRegion"
|
|
||||||
:regions="regionOption"
|
|
||||||
@change="onRegionChange"/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {getTableList, getTableData, getWorkOrderStatistics} from './report'
|
import {getTableList, getTableData, getWorkOrderStatistics} from './report'
|
||||||
import {ref, onMounted, watch, onUnmounted} from 'vue'
|
import {ref, onMounted, watch, onUnmounted} from 'vue'
|
||||||
import {useRouter} from 'vue-router'
|
import {useRouter} from 'vue-router'
|
||||||
import RegionSelector from './components/RegionSelector.vue'
|
import HeaderSelector from './components/HeaderSelector.vue'
|
||||||
import WeatherWarning from './components/WeatherWarning.vue'
|
import WeatherWarning from './components/WeatherWarning.vue'
|
||||||
import {getDashboardData, getAlertDetails, type DashboardData} from '@/api/dashboard'
|
import {getDashboardData, getAlertDetails, type DashboardData} from '@/api/dashboard'
|
||||||
|
|
||||||
@@ -80,7 +81,6 @@ const currentDateTime = ref<string>('')
|
|||||||
const currentDate = ref<string>('')
|
const currentDate = ref<string>('')
|
||||||
const currentWeek = ref<string>('')
|
const currentWeek = ref<string>('')
|
||||||
const currentTime = ref<string>('')
|
const currentTime = ref<string>('')
|
||||||
const regionSelectorVisible = ref<boolean>(false)
|
|
||||||
const selectedRegion = ref<string>('')
|
const selectedRegion = ref<string>('')
|
||||||
const sourceIndex = ref<number>(1)
|
const sourceIndex = ref<number>(1)
|
||||||
|
|
||||||
@@ -201,7 +201,6 @@ const updateAllCounts = (counts: {
|
|||||||
// 路由
|
// 路由
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const regionOption = ref<RegionItem[]>([])
|
|
||||||
// 定时器ID
|
// 定时器ID
|
||||||
const dashboardTimerId = ref<ReturnType<typeof setInterval> | null>(null)
|
const dashboardTimerId = ref<ReturnType<typeof setInterval> | null>(null)
|
||||||
const timeUpdateTimerId = ref<ReturnType<typeof setInterval> | null>(null)
|
const timeUpdateTimerId = ref<ReturnType<typeof setInterval> | null>(null)
|
||||||
@@ -280,21 +279,6 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (records && records.length > 0) {
|
if (records && records.length > 0) {
|
||||||
// 去重region字段,使用Map来确保唯一性
|
|
||||||
const regionMap = new Map()
|
|
||||||
records.forEach(el => {
|
|
||||||
if (!regionMap.has(el.region)) {
|
|
||||||
regionMap.set(el.region, {
|
|
||||||
name: el.region,
|
|
||||||
code: el.region_id // 使用region_id作为code
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// 转换为数组
|
|
||||||
regionOption.value = Array.from(regionMap.values())
|
|
||||||
|
|
||||||
console.log('regionOption.value>>>>', regionOption.value);
|
|
||||||
|
|
||||||
// 将园区信息去重
|
// 将园区信息去重
|
||||||
const parkMap = new Map();
|
const parkMap = new Map();
|
||||||
records.forEach(el => {
|
records.forEach(el => {
|
||||||
@@ -717,11 +701,6 @@ const onRegionChange = (item: RegionItem): void => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开区域选择器
|
|
||||||
const openRegionSelector = (): void => {
|
|
||||||
regionSelectorVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新时间
|
// 更新时间
|
||||||
const updateTime = (): void => {
|
const updateTime = (): void => {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
|
|||||||
@@ -2,13 +2,18 @@
|
|||||||
<div class="dashboard-container">
|
<div class="dashboard-container">
|
||||||
<!-- 顶部标题栏 -->
|
<!-- 顶部标题栏 -->
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<div class="header-left">
|
<HeaderSelector
|
||||||
<img class="back-img" @click="returnToHeadquarters"
|
back-button-type="image"
|
||||||
src="@/assets/images/screen/back_image.png"/>
|
:show-back-button="true"
|
||||||
<div class="back-button" @click="openRegionSelector"> {{ selectedRegion }}
|
:on-back="returnToHeadquarters"
|
||||||
<span>···</span>
|
:display-text="selectedRegion"
|
||||||
</div>
|
:clickable="true"
|
||||||
</div>
|
:show-selector-indicator="true"
|
||||||
|
selector-type="park"
|
||||||
|
:selected-value="selectedPark"
|
||||||
|
:region-code="query.regionCode"
|
||||||
|
@selector-change="onRegionChange"
|
||||||
|
/>
|
||||||
<h1 class="header-title">{{ selectedRegion }}综合监控大屏</h1>
|
<h1 class="header-title">{{ selectedRegion }}综合监控大屏</h1>
|
||||||
<div class="date-wrapper">
|
<div class="date-wrapper">
|
||||||
<span style="margin-top: 6%;font-size: 0.9rem;">{{ currentDate }}</span>
|
<span style="margin-top: 6%;font-size: 0.9rem;">{{ currentDate }}</span>
|
||||||
@@ -44,17 +49,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 区域选择弹窗 -->
|
|
||||||
<RegionSelector v-model="regionSelectorVisible" :modelSelected="selectedPark"
|
|
||||||
:regions="regionOption"
|
|
||||||
@change="onRegionChange"/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {getTableList, getTableData, getWorkOrderStatistics} from './report'
|
import {getTableList, getTableData, getWorkOrderStatistics} from './report'
|
||||||
import {ref, onMounted, watch, onUnmounted} from 'vue'
|
import {ref, onMounted, watch, onUnmounted} from 'vue'
|
||||||
import {useRoute, useRouter} from 'vue-router'
|
import {useRoute, useRouter} from 'vue-router'
|
||||||
import RegionSelector from './components/RegionSelector.vue'
|
import HeaderSelector from './components/HeaderSelector.vue'
|
||||||
import WeatherWarning from './components/WeatherWarning.vue'
|
import WeatherWarning from './components/WeatherWarning.vue'
|
||||||
import {getDashboardData, getAlertDetails, type DashboardData} from '@/api/dashboard'
|
import {getDashboardData, getAlertDetails, type DashboardData} from '@/api/dashboard'
|
||||||
|
|
||||||
@@ -82,7 +83,6 @@ const currentDateTime = ref<string>('')
|
|||||||
const currentDate = ref<string>('')
|
const currentDate = ref<string>('')
|
||||||
const currentWeek = ref<string>('')
|
const currentWeek = ref<string>('')
|
||||||
const currentTime = ref<string>('')
|
const currentTime = ref<string>('')
|
||||||
const regionSelectorVisible = ref<boolean>(false)
|
|
||||||
const selectedRegion = ref<string>('')
|
const selectedRegion = ref<string>('')
|
||||||
const sourceIndex = ref<number>(1)
|
const sourceIndex = ref<number>(1)
|
||||||
|
|
||||||
@@ -202,7 +202,6 @@ const updateAllCounts = (counts: {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const regionOption = ref<RegionItem[]>([])
|
|
||||||
const selectedPark = ref<string>('')
|
const selectedPark = ref<string>('')
|
||||||
const parkValue = ref<string>('')
|
const parkValue = ref<string>('')
|
||||||
// 定时器ID
|
// 定时器ID
|
||||||
@@ -304,20 +303,20 @@ onMounted(async () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// 转换为数组
|
// 转换为数组并设置 campus_id
|
||||||
regionOption.value = Array.from(regionMap.values())
|
const regionArray = Array.from(regionMap.values())
|
||||||
console.log('regionOption.value>>>>', regionOption.value);
|
query.campus_id = regionArray.map(el => el.code).join()
|
||||||
|
|
||||||
query.campus_id = regionOption.value.map(el => el.code).join()
|
|
||||||
}
|
}
|
||||||
// 暂时先放在这里
|
// 初始化数据 - 先加载数据,再设置初始值
|
||||||
|
await loadDashboardData()
|
||||||
|
// 暂时先放在这里 - 确保 dashboardData.value 已初始化后再访问
|
||||||
|
if (dashboardData.value && dashboardData.value.hiddenDangerData) {
|
||||||
dashboardData.value.hiddenDangerData.general = 0
|
dashboardData.value.hiddenDangerData.general = 0
|
||||||
dashboardData.value.hiddenDangerData.major = 0
|
dashboardData.value.hiddenDangerData.major = 0
|
||||||
dashboardData.value.hiddenDangerData.progress.overdue = 0
|
dashboardData.value.hiddenDangerData.progress.overdue = 0
|
||||||
dashboardData.value.hiddenDangerData.progress.processed = 0
|
dashboardData.value.hiddenDangerData.progress.processed = 0
|
||||||
dashboardData.value.hiddenDangerData.progress.processing = 0
|
dashboardData.value.hiddenDangerData.progress.processing = 0
|
||||||
// 初始化数据
|
}
|
||||||
await loadDashboardData()
|
|
||||||
|
|
||||||
// 启动定时器
|
// 启动定时器
|
||||||
timeOut1()
|
timeOut1()
|
||||||
@@ -740,11 +739,6 @@ const onRegionChange = (item: RegionItem): void => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开区域选择器
|
|
||||||
const openRegionSelector = (): void => {
|
|
||||||
regionSelectorVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新时间
|
// 更新时间
|
||||||
const updateTime = (): void => {
|
const updateTime = (): void => {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
|
|||||||
Reference in New Issue
Block a user