- 在 companyScreen.vue 中隐藏“隐患排查治理”标题 - 在 HiddenDangerPanel.vue 中将“分类风险”更改为“隐患等级” - 在 mainScreenV1.vue 中同步修改标题和子标题显示逻辑 - 在 parkScreen.vue 中隐藏“隐患排查治理”标题
1874 lines
58 KiB
Vue
1874 lines
58 KiB
Vue
<template>
|
||
<div class="dashboard-container">
|
||
<!-- 顶部标题栏 -->
|
||
<div class="header-container">
|
||
<div class="header-left">
|
||
<img class="back-img" @click="returnToHeadquarters" src="@/assets/images/screen/back_image.png" />
|
||
<div class="back-button"> {{ selectedPark }} </div>
|
||
</div>
|
||
<h1 class="header-title">{{ selectedPark }}综合监控大屏</h1>
|
||
<div class="date-wrapper">
|
||
<span style="margin-top: 6%;font-size: 0.9rem;">{{ currentDate }}</span>
|
||
<span style="margin-top: 6%;font-size: 0.9rem;">{{ currentWeek }}</span>
|
||
<span style="margin-top: 6%;font-size: 0.9rem;">{{ currentTime }}</span>
|
||
</div>
|
||
</div>
|
||
<!-- 天气预报 -->
|
||
<WeatherWarning />
|
||
<!-- 主内容区 -->
|
||
<div class="content-container">
|
||
<div class="left-wrapper">
|
||
<!-- 左上区域 -->
|
||
<div class="left-top">
|
||
<div class="panel-title">人员管理</div>
|
||
<img style="margin: 8px 0" src="@/assets/images/title_border_line_1.png" />
|
||
<div class="top-card">
|
||
<div class="top-card-left">
|
||
<div>
|
||
<img width="33px" src="@/assets/images/1_224520_821.png" />
|
||
</div>
|
||
<span>总计</span>
|
||
<div class="number-wrapper">
|
||
<span class="total-number" v-for="(digit, index) in totalCountDigits" :key="index">
|
||
{{ digit }}
|
||
</span>
|
||
</div>
|
||
<span>人</span>
|
||
</div>
|
||
<div class="top-card-right">
|
||
<div class="top-card-right-item">
|
||
<img width="18px" src="@/assets/images/v2_rel0n8.png" />
|
||
<span>正式员工</span>
|
||
<div class="type-number-wrapper" style="margin-left: 2vw">
|
||
<span class="type-number" v-for="(digit, index) in formalEmployeeDigits" :key="index">
|
||
{{ digit }}
|
||
</span>
|
||
</div>
|
||
<span>人</span>
|
||
</div>
|
||
<div class="top-card-right-item">
|
||
<img width="18px" src="@/assets/images/v2_rel0n23.png" />
|
||
<span>外协人员</span>
|
||
<div class="type-number-wrapper" style="margin-left: 1vw">
|
||
<span class="type-number" v-for="(digit, index) in externalStaffDigits" :key="index">
|
||
{{ digit }}
|
||
</span>
|
||
</div>
|
||
<span>人</span>
|
||
</div>
|
||
<div class="top-card-right-item">
|
||
<img width="18px" src="@/assets/images/24508_654.png" />
|
||
<span>访客</span>
|
||
<div class="type-number-wrapper">
|
||
<span class="type-number" v-for="(digit, index) in visitorDigits" :key="index">
|
||
{{ digit }}
|
||
</span>
|
||
</div>
|
||
<span>人</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="bottom-card">
|
||
<div class="bottom-card-title">
|
||
<!-- 隐患排查治理 标题需要隐藏 2025-10-31 -->
|
||
<span></span>
|
||
<img width="50%" style="margin: 8px 0" src="@/assets/images/line_1.png" />
|
||
</div>
|
||
<div class="type-wrapper">
|
||
<div class="type-item">
|
||
<span class="type-btn yellow">重大</span>
|
||
<span class="type-num">{{ mockData.hiddenDangerData.severityCount }}</span>
|
||
</div>
|
||
<div class="type-item">
|
||
<span class="type-btn green">一般</span>
|
||
<span class="type-num">{{ mockData.hiddenDangerData.generalCount }}</span>
|
||
</div>
|
||
<!-- <div class="type-item">
|
||
<span class="type-btn blue">较大</span>
|
||
<span class="type-num">{{ mockData.hiddenDangerData.majorCount }}</span>
|
||
</div> -->
|
||
|
||
</div>
|
||
<div class="hazard-wrapper">
|
||
<div ref="progressChartRef" class="progress-chart"></div>
|
||
<div class="progress-legend">
|
||
<div class="legend-item"><span class="dot red"></span>已逾期</div>
|
||
<div class="legend-item"><span class="dot green"></span>已处理</div>
|
||
</div>
|
||
<div class="progress-legend">
|
||
<!-- <div class="legend-item"><span class="dot yellow"></span>待排查</div>-->
|
||
<div class="legend-item"><span class="dot blue"></span>处理中</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 左下区域 -->
|
||
<div class="left-bottom">
|
||
<div class="panel-title">
|
||
<div class="tabs">
|
||
<span class="tab" :class="{ active: activeTab === '高危作业' }" @click="handleTabClick('高危作业')">高危作业</span>
|
||
<span class="divider">|</span>
|
||
<span
|
||
class="tab" :class="{ active: activeTab === '安全培训考试' }"
|
||
@click="handleTabClick('安全培训考试')">安全培训考试</span>
|
||
<span class="divider">|</span>
|
||
<span class="tab" :class="{ active: activeTab === '应急预案及演练' }" @click="handleTabClick('应急预案及演练')">应急预案及演练</span>
|
||
</div>
|
||
</div>
|
||
<img style="margin: 8px 0" src="@/assets/images/title_border_line.png" />
|
||
<div style="padding-top: 10px;display: flex; flex-direction: column; justify-content: start; height: 91%;">
|
||
<template v-if="activeTab === '高危作业'">
|
||
<div class="bottom-card">
|
||
<div class="bottom-card-title" style="margin-top: 0;">
|
||
<span>各园区统计</span>
|
||
<img width="50%" style="margin: 8px 0" src="@/assets/images/line_1.png" />
|
||
</div>
|
||
</div>
|
||
<div class="row-wrapper">
|
||
<div class="row-item">
|
||
<img src="@/assets/images/screen/unfinished.png" width="20" />
|
||
<span class="row-item-title">未开始数量</span>
|
||
<span class="row-item-number">{{unfinishedCount}}</span>
|
||
</div>
|
||
<div class="row-item">
|
||
<img src="@/assets/images/screen/inProcess.png" width="20" />
|
||
<span class="row-item-title">进行中数量</span>
|
||
<span class="row-item-number">{{inProgressCount}}</span>
|
||
</div>
|
||
<div class="row-item">
|
||
<img src="@/assets/images/screen/finished.png" width="20" />
|
||
<span class="row-item-title">已完成数量</span>
|
||
<span class="row-item-number">{{finishedCount}}</span>
|
||
</div>
|
||
</div>
|
||
<div style="display: flex; width: 100%;">
|
||
<AlertList maxHeight="25vh" style="margin-left: 1vw;" :list-data="dangerList" />
|
||
</div>
|
||
</template>
|
||
<template v-if="activeTab === '安全培训考试'">
|
||
<div class="bottom-card" style="margin-top: 10px;">
|
||
<div class="bottom-card-title" style="margin-top: 0;">
|
||
<span>安全培训考试</span>
|
||
<img width="50%" style="margin: 8px 0" src="@/assets/images/line_1.png" />
|
||
</div>
|
||
</div>
|
||
<AlertList maxHeight="40vh" style="margin-left: 1vw;" :table-title="tableTitle" :list-data="examList" />
|
||
</template>
|
||
<template v-if="activeTab === '应急预案及演练'">
|
||
<div class="bottom-card">
|
||
<div class="bottom-card-title" style="margin-top: 0;">
|
||
<span>应急预案及演练</span>
|
||
<img width="50%" style="margin: 8px 0" src="@/assets/images/line_1.png" />
|
||
</div>
|
||
</div>
|
||
<AlertList maxHeight="40vh" style="margin-left: 1vw;" :table-title="trainingTableTitle" :list-data="drillList" />
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="right-wrapper">
|
||
<!-- 右上区域 -->
|
||
<div class="right-top" style="text-align: right">
|
||
<div class="panel-title">高风险告警</div>
|
||
<div>
|
||
<img style="margin: 8px 0" src="@/assets/images/title_border_line_1.png" />
|
||
</div>
|
||
<div class="tip-container">
|
||
<div class="tip-image">
|
||
<img src="@/assets/images/screen/circle_image.png" width="80" height="80" />
|
||
<span class="number">{{ mockData.alertData.total }}</span>
|
||
</div>
|
||
<img src="@/assets/images/screen/tip_bg_image.png" width="100%" height="70" />
|
||
<div class="tip-content">
|
||
<div class="col-item">
|
||
<img src="@/assets/images/screen/warning_img.png" width="23" />
|
||
<span>告警总数</span>
|
||
<span style="font-size: 1.2rem; marker-start: 2vw; color: yellow;">{{ mockData.alertData.total }}</span>
|
||
</div>
|
||
<div class="col-item">
|
||
<span>已处理</span>
|
||
<span style="font-size: 1.2rem; marker-start: 2vw; color: greenyellow;">{{ mockData.alertData.processed
|
||
}}</span>
|
||
</div>
|
||
<div class="col-item" style=" display: flex;margin-left: 2vw; align-items: center;">
|
||
<span>待处理</span>
|
||
<span style="font-size: 1.2rem; marker-start: 2vw; color: yellow;">{{ mockData.alertData.pending }}</span>
|
||
</div>
|
||
<div class="col-item" style=" display: flex;margin-left: 2vw; align-items: center;">
|
||
<span>处理中</span>
|
||
<span style="font-size: 1.2rem; marker-start: 2vw; color: yellow;">{{ mockData.alertData.processing }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style=" display: flex; width: 100%;margin-top: 1vw; flex: 1; justify-content: flex-end;">
|
||
<AlertList style="margin-right: 1vw;" title="告警详情" :list-data="mockData.alertData.details" />
|
||
</div>
|
||
</div>
|
||
<!-- 右下区域 -->
|
||
<div class="right-bottom" style="text-align: right">
|
||
<div class="panel-title">超时工单</div>
|
||
<img style="margin: 8px 0" src="@/assets/images/title_border_line_1.png" />
|
||
<div class="tip-container">
|
||
<div class="tip-image">
|
||
<img src="@/assets/images/screen/circle_image.png" width="80" height="80" />
|
||
<span class="number">{{ mockData.timeoutWorkOrders.total }}</span>
|
||
</div>
|
||
<img src="@/assets/images/screen/tip_bg_image.png" width="100%" height="70" />
|
||
<div class="tip-content">
|
||
<div class="col-item">
|
||
<img src="@/assets/images/screen/warning_img.png" width="23" />
|
||
<span>超时工单数</span>
|
||
<span style="font-size: 1.2rem; marker-start: 2vw; color: red;">{{ mockData.timeoutWorkOrders.total }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div style=" display: flex; width: 100%;margin-top: 1vw; flex: 1; justify-content: flex-end;">
|
||
<AlertList style="margin-right: 1vw;" title="工单详情" :list-data="mockData.timeoutWorkOrders.details" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="center-container">
|
||
<!-- 中部区域 -->
|
||
<ParkCenter
|
||
:parkName="selectedPark" :backgroundImage="backgroundImage" @point-hover="handlePointHover"
|
||
@point-leave="handlePointLeave" />
|
||
</div>
|
||
|
||
<!-- 点位信息弹窗 -->
|
||
<PointInfoPopup
|
||
v-if="showPointPopup" :visible="showPointPopup" :point-info="currentPoint"
|
||
:park-name="selectedPark || '雄安园区'" />
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<script setup lang="ts">
|
||
import { ref, onMounted, onUnmounted, computed } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import * as echarts from 'echarts'
|
||
import ParkCenter from './components/ParkCenter.vue'
|
||
import PointInfoPopup from './components/PointInfoPopup.vue'
|
||
import WeatherWarning from './components/WeatherWarning.vue'
|
||
import AlertList from './components/AlertList.vue'
|
||
import { getTableList, getTableData, getDangerDetail, getDangerCount, getExamDetail, getDrillDetail } from './report'
|
||
|
||
interface PointPosition {
|
||
label: string
|
||
x: number
|
||
y: number
|
||
relativeX: number
|
||
relativeY: number
|
||
}
|
||
const totalCountDigits = computed(() => String(mockData.totalCount).split('').map(Number))
|
||
const formalEmployeeDigits = computed(() => String(mockData.formalEmployeeCount).split('').map(Number))
|
||
const externalStaffDigits = computed(() => String(mockData.externalStaffCount).split('').map(Number))
|
||
const visitorDigits = computed(() => String(mockData.visitorCount).split('').map(Number))
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
|
||
const selectedPark = ref('雄安园区')
|
||
const backgroundImage = ref('')
|
||
const currentDateTime = ref<string>('')
|
||
const currentDate = ref<string>('')
|
||
const currentWeek = ref<string>('')
|
||
const currentTime = ref<string>('')
|
||
const activeTab = ref<string>('高危作业')
|
||
const showPointPopup = ref(false)
|
||
const currentPoint = ref<PointPosition | null>(null)
|
||
const progressChart = ref<echarts.ECharts | null>(null)
|
||
const progressChartRef = ref<HTMLElement | null>(null)
|
||
const timeUpdateTimerId = ref<ReturnType<typeof setInterval> | null>(null)
|
||
|
||
const dangerList = ref<any[]>([])
|
||
const unfinishedCount = ref<number>(0)
|
||
const inProgressCount = ref<number>(0)
|
||
const finishedCount = ref<number>(0)
|
||
|
||
|
||
const tableTitle = [
|
||
{
|
||
name: '培训(考试)名称',
|
||
key: 'examname'
|
||
},
|
||
{
|
||
name: '参与人次',
|
||
key: 'exampeoplenum'
|
||
},
|
||
{
|
||
name: '培训时长(小时)',
|
||
key: 'examduration',
|
||
},
|
||
{
|
||
name: '考试通过率(%)',
|
||
key: 'exampassrate',
|
||
}
|
||
]
|
||
|
||
const trainingTableTitle = [
|
||
{
|
||
name: '演练名称',
|
||
key: 'drill_plan_name'
|
||
},
|
||
{
|
||
name: '完成时间',
|
||
key: 'drill_time'
|
||
},
|
||
{
|
||
name: '参与人数',
|
||
key: 'drill_count'
|
||
}
|
||
]
|
||
|
||
const examList = ref<any[]>([])
|
||
|
||
const drillList = ref<any[]>([])
|
||
|
||
|
||
const query = reactive({
|
||
pageNo: 1,
|
||
pageSize: 10000,
|
||
startDate: currentDateTime.value + " 00:00:00",
|
||
endDate: currentDateTime.value + " 23:59:59",
|
||
campus_id: "1825468527486140416" //园区id(如果是多个用逗号拼接)
|
||
})
|
||
|
||
// 模拟数据
|
||
const mockData = reactive({
|
||
totalCount: 0,
|
||
formalEmployeeCount: 0,
|
||
externalStaffCount: 0,
|
||
visitorCount: 0,
|
||
buildInfoList: [
|
||
{
|
||
highRiskCount: 2,
|
||
buildName: "A楼栋",
|
||
alarmsCount: 23
|
||
},
|
||
{
|
||
highRiskCount: 1,
|
||
buildName: "B楼栋",
|
||
alarmsCount: 3
|
||
},
|
||
{
|
||
highRiskCount: 11,
|
||
buildName: "C楼栋",
|
||
alarmsCount: 33
|
||
},
|
||
{
|
||
highRiskCount: 11,
|
||
buildName: "D楼栋",
|
||
alarmsCount: 33
|
||
}
|
||
],
|
||
alertData: {
|
||
total: 0,
|
||
processed: 0,
|
||
pending: 0,
|
||
processing: 0,
|
||
details: [
|
||
// {
|
||
// description: '2024-07-10 上午8:00 雄安园区隐患内容管理 状态 处理',
|
||
// alarm_level_code: 'major',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283935'
|
||
// },
|
||
// {
|
||
// description: '2025-07-09 上海XXX区域A1门禁告警 处理中 紧急',
|
||
// alarm_level_code: 'major',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283936'
|
||
// },
|
||
// {
|
||
// description: '2025-07-09 上海XXX区域A1门禁告警 处理中 紧急',
|
||
// alarm_level_code: 'major',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// }, {
|
||
// description: '2024-07-10 上午8:00 雄安园区隐患内容管理 状态 处理',
|
||
// alarm_level_code: 'major',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283935'
|
||
// },
|
||
// {
|
||
// description: '2025-07-09 上海XXX区域A1门禁告警 处理中 紧急',
|
||
// alarm_level_code: 'major',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283936'
|
||
// },
|
||
// {
|
||
// description: '2025-07-09 上海XXX区域A1门禁告警 处理中 紧急',
|
||
// alarm_level_code: 'major',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// }
|
||
]
|
||
},
|
||
timeoutWorkOrders: {
|
||
total: 0,
|
||
details: [
|
||
// {
|
||
// description: '2025-08-10 上午8:00 武汉园区隐患内容管理 状态 处理',
|
||
// alarm_level_code: 'general',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// },
|
||
// {
|
||
// description: '2025-08-10 上午8:00 武汉园区隐患内容管理 状态 处理',
|
||
// alarm_level_code: 'general',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// }, {
|
||
// description: '2025-08-10 上午8:00 武汉园区隐患内容管理 状态 处理',
|
||
// alarm_level_code: 'general',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// }, {
|
||
// description: '2025-08-10 上午8:00 武汉园区隐患内容管理 状态 处理',
|
||
// alarm_level_code: 'general',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// }, {
|
||
// description: '2025-08-10 上午8:00 武汉园区隐患内容管理 状态 处理',
|
||
// alarm_level_code: 'general',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// }, {
|
||
// description: '2025-08-10 上午8:00 武汉园区隐患内容管理 状态 处理',
|
||
// alarm_level_code: 'general',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// },
|
||
]
|
||
},
|
||
hiddenDangerData: {
|
||
generalCount: 0,
|
||
majorCount: 0,
|
||
severityCount: 0,
|
||
progress: {
|
||
participateCount: 0,
|
||
toParticipateCount: 0,
|
||
finishCount: 0
|
||
},
|
||
progressDetail: [
|
||
// {
|
||
// description: '2025-08-10 上午8:00 武汉园区参与内容管理 状态 处理',
|
||
// alarm_level_code: 'severity',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// },
|
||
// {
|
||
// description: '2025-08-10 上午8:00 武汉园区参与内容管理 状态 处理',
|
||
// alarm_level_code: 'general',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// }, {
|
||
// description: '2025-08-10 上午8:00 武汉园区参与内容管理 状态 处理',
|
||
// alarm_level_code: 'major',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// }, {
|
||
// description: '2025-08-10 上午8:00 武汉园区参与内容管理 状态 处理',
|
||
// alarm_level_code: 'general',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// }, {
|
||
// description: '2025-08-10 上午8:00 武汉园区参与内容管理 状态 处理',
|
||
// alarm_level_code: 'severity',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// }, {
|
||
// description: '2025-08-10 上午8:00 武汉园区参与内容管理 状态 处理',
|
||
// alarm_level_code: 'general',
|
||
// alarm_status: '已关闭',
|
||
// alarm_biz_id: '20250827164305283937'
|
||
// },
|
||
]
|
||
}
|
||
})
|
||
|
||
// 返回上一级
|
||
const returnToHeadquarters = () => {
|
||
router.back()
|
||
}
|
||
|
||
const handlePointHover = (position: PointPosition) => {
|
||
currentPoint.value = position
|
||
showPointPopup.value = true
|
||
}
|
||
|
||
const handlePointLeave = () => {
|
||
showPointPopup.value = false
|
||
currentPoint.value = null
|
||
}
|
||
|
||
// 初始化进度图表
|
||
const initProgressChart = () => {
|
||
const chartElement = progressChartRef.value
|
||
if (chartElement) {
|
||
progressChart.value = echarts.init(chartElement)
|
||
const width = progressChart.value.getWidth()
|
||
|
||
const progressOption = {
|
||
series: [
|
||
{
|
||
type: 'pie',
|
||
radius: '60%',
|
||
center: ['50%', '50%'],
|
||
left: 0,
|
||
top: 0,
|
||
bottom: 0,
|
||
data: [
|
||
{ value: 20, name: '已逾期', itemStyle: { color: '#ef4444' } },
|
||
{ value: 35, name: '已处理', itemStyle: { color: '#10b981' } },
|
||
// { value: 25, name: '待排查', itemStyle: { color: '#eab308' } },
|
||
{ value: 20, name: '处理中', itemStyle: { color: '#3b82f6' } },
|
||
],
|
||
label: {
|
||
alignTo: 'edge',
|
||
formatter: '{time|{c} %}\n',
|
||
minMargin: 5,
|
||
edgeDistance: 10,
|
||
lineHeight: 15,
|
||
rich: {
|
||
time: {
|
||
fontSize: 10,
|
||
color: '#fff'
|
||
}
|
||
}
|
||
},
|
||
labelLine: {
|
||
length: 5,
|
||
length2: 0,
|
||
maxSurfaceAngle: 10
|
||
},
|
||
labelLayout: function (params: any) {
|
||
const isLeft = params.labelRect.x < width / 2;
|
||
const points = params.labelLinePoints;
|
||
points[2][0] = isLeft
|
||
? params.labelRect.x
|
||
: params.labelRect.x + params.labelRect.width;
|
||
return {
|
||
labelLinePoints: points
|
||
};
|
||
},
|
||
}
|
||
]
|
||
}
|
||
|
||
progressChart.value.setOption(progressOption)
|
||
}
|
||
}
|
||
const isFirstLoad = ref<boolean>(true)
|
||
// 动画相关的状态
|
||
const isAnimating = ref<boolean>(false)
|
||
const animationDuration = 2000 // 动画持续时间(毫秒)
|
||
const dashboardTimerId = ref<ReturnType<typeof setInterval> | null>(null)
|
||
interface RegionItem {
|
||
name: string
|
||
code: string
|
||
pic_url: string
|
||
}
|
||
const regionOption = ref<RegionItem[]>([])
|
||
onMounted(async () => {
|
||
updateTime()
|
||
timeUpdateTimerId.value = setInterval(updateTime, 1000)
|
||
|
||
if (typeof route.query.parkName === 'string') {
|
||
selectedPark.value = route.query.parkName
|
||
query.campus_id = route.query.parkCode as string
|
||
}
|
||
|
||
try {
|
||
let { records } = await getTableList(
|
||
'park_info_list'
|
||
)
|
||
// records = [
|
||
// {
|
||
// "region_id": "130601",
|
||
// "park_code": "1825468527486140416",
|
||
// "region": "北京",
|
||
// "park_name": "雄安新区总部",
|
||
// "pic_url": "1"
|
||
// },
|
||
// {
|
||
// "region_id": "130601",
|
||
// "park_code": "1825468527486140417",
|
||
// "region": "北京",
|
||
// "park_name": "雄安地面站",
|
||
// "pic_url": "2"
|
||
// },
|
||
// {
|
||
// "region_id": "130603",
|
||
// "park_code": "1825468527486140426",
|
||
// "region": "武汉",
|
||
// "park_name": "花山新区总部",
|
||
// "pic_url": "3"
|
||
// }
|
||
// ]
|
||
if (records && records.length > 0) {
|
||
// 更新为新的数据格式
|
||
regionOption.value = records.map(el => ({
|
||
name: el.park_name,
|
||
code: el.park_code,
|
||
pic_url: el.pic_url
|
||
}))
|
||
|
||
for (let i = 0; i < regionOption.value.length; i++) {
|
||
const el = regionOption.value[i];
|
||
if (el.code == query.campus_id && el.pic_url) {
|
||
backgroundImage.value = el.pic_url
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('初始化园区数据失败:', error)
|
||
}
|
||
// 初始化数据
|
||
await loadDashboardData()
|
||
|
||
// 添加周期性动画演示
|
||
dashboardTimerId.value = setInterval(async () => {
|
||
await loadDashboardData()
|
||
}, 2 * 60 * 1000) // 每2分钟更新一次
|
||
})
|
||
|
||
const loadDashboardData = () => {
|
||
if (isFirstLoad.value) {
|
||
isFirstLoad.value = false
|
||
}
|
||
// 获取总体概览数据
|
||
getTableList('generalTotal', query).then(generalTotal => {
|
||
if (generalTotal.records && generalTotal.records.length > 0) {
|
||
mockData.totalCount = Number(generalTotal.records[0].totalCount)
|
||
mockData.formalEmployeeCount = Number(generalTotal.records[0].formalEmployeeCount)
|
||
mockData.externalStaffCount = Number(generalTotal.records[0].externalStaffCount)
|
||
mockData.visitorCount = Number(generalTotal.records[0].visitorCount)
|
||
}
|
||
}).catch(error => {
|
||
console.log("获取总体概览数据失败>>>>>>>", error);
|
||
})
|
||
// 获取总体概览-隐患排查治理数量
|
||
// getTableList('hidden_danger_investigation', query).then(res => {
|
||
// if (res.records && res.records.length > 0) {
|
||
// mockData.hiddenDangerData.generalCount = res.records[0].general
|
||
// mockData.hiddenDangerData.severityCount = res.records[0].major
|
||
// // 获取隐患排查治理数据
|
||
// getTableList('risk_level_count', { ...query }).then(res => {
|
||
// mockData.hiddenDangerData.generalCount = Number(mockData.hiddenDangerData.generalCount) + Number(res.records[0].general_count)
|
||
// mockData.hiddenDangerData.severityCount = Number(mockData.hiddenDangerData.severityCount) + Number(res.records[0].major_count)
|
||
// })
|
||
// }
|
||
// }).catch(error => {
|
||
// console.log("获取隐患排查治理数量失败>>>>>>>", error);
|
||
// })
|
||
|
||
// 获取总体概览-隐患排查治理统计
|
||
// getTableList('hidden_danger_process_progress', query).then(res => {
|
||
// if (res.records && res.records.length > 0) {
|
||
// const process = res.records[0]
|
||
// progressChart.value = echarts.init(progressChartRef.value)
|
||
// const width = progressChart.value.getWidth()
|
||
// const progressOption = {
|
||
// series: [
|
||
// {
|
||
// type: 'pie',
|
||
// radius: '60%',
|
||
// center: ['50%', '50%'],
|
||
// left: 0,
|
||
// top: 0,
|
||
// bottom: 0,
|
||
// data: [
|
||
// { value: process.overdue || 0, name: '已逾期', itemStyle: { color: '#ef4444' } },
|
||
// { value: process.processed || 0, name: '已处理', itemStyle: { color: '#10b981' } },
|
||
// { value: process.pending || 0, name: '待排查', itemStyle: { color: '#eab308' } },
|
||
// { value: process.processing || 0, name: '处理中', itemStyle: { color: '#3b82f6' } }
|
||
// ],
|
||
// label: {
|
||
// alignTo: 'edge',
|
||
// formatter: '{time|{c} %}\n',
|
||
// minMargin: 5,
|
||
// edgeDistance: 10,
|
||
// lineHeight: 15,
|
||
// rich: {
|
||
// time: {
|
||
// fontSize: 10,
|
||
// color: '#fff'
|
||
// }
|
||
// }
|
||
// },
|
||
// labelLine: {
|
||
// length: 5,
|
||
// length2: 0,
|
||
// maxSurfaceAngle: 10
|
||
// },
|
||
// labelLayout: function (params: any) {
|
||
// const isLeft = params.labelRect.x < width / 2;
|
||
// const points = params.labelLinePoints;
|
||
// points[2][0] = isLeft
|
||
// ? params.labelRect.x
|
||
// : params.labelRect.x + params.labelRect.width;
|
||
// return {
|
||
// labelLinePoints: points
|
||
// };
|
||
// },
|
||
// }
|
||
// ]
|
||
// }
|
||
// progressChart.value.setOption(progressOption)
|
||
// getTableData('1963446160885366786', { ...query, park_name: query.campus_id }).then(res => {
|
||
// let overdueCnt = 0
|
||
// let processedCnt = 0
|
||
// let processingCnt = 0
|
||
// res.records.forEach(el => {
|
||
// // 整改状态
|
||
// if (el.corrective_status == 2) { //已逾期
|
||
// overdueCnt++
|
||
// }
|
||
// if (el.corrective_status == 1) { //已处理
|
||
// processedCnt++
|
||
// }
|
||
// if (el.corrective_status == 0) { //处理中
|
||
// processingCnt++
|
||
// }
|
||
// });
|
||
// progressOption.series[0].data[0].value = Number(progressOption.series[0].data[0].value) + overdueCnt
|
||
// progressOption.series[0].data[1].value = Number(progressOption.series[0].data[1].value) + processedCnt
|
||
// progressOption.series[0].data[3].value = Number(progressOption.series[0].data[3].value) + processingCnt
|
||
// if (progressChart.value) {
|
||
// progressChart.value.setOption(progressOption)
|
||
// }
|
||
// })
|
||
// }
|
||
// }).catch(error => {
|
||
// console.log("获取隐患排查治理统计数据失败>>>>>>>", error);
|
||
// })
|
||
handleTabClick("高危作业")
|
||
|
||
// 获取风险预警数据
|
||
getTableList('risk_alert_data', query).then(risk_alert_data => {
|
||
if (risk_alert_data.records && risk_alert_data.records.length > 0) {
|
||
mockData.alertData.total = risk_alert_data.records[0].total
|
||
mockData.alertData.processed = risk_alert_data.records[0].processed
|
||
mockData.alertData.pending = risk_alert_data.records[0].pending
|
||
mockData.alertData.processing = risk_alert_data.records[0].processing
|
||
}
|
||
}).catch(error => {
|
||
console.error('获取风险预警数据失败:', error)
|
||
})
|
||
// 获取风险预警详情数据
|
||
getTableList('risk_alert_detail', query).then(risk_alert_detail => {
|
||
if (risk_alert_detail.records && risk_alert_detail.records.length > 0) {
|
||
mockData.alertData.details = risk_alert_detail.records
|
||
}
|
||
}).catch(error => {
|
||
console.error('获取风险预警详情数据失败:', error)
|
||
})
|
||
|
||
// 获取超期工单数据
|
||
getTableList('timeout_work_order', query).then(timeout_work_order => {
|
||
if (timeout_work_order.records && timeout_work_order.records.length > 0) {
|
||
mockData.timeoutWorkOrders.total = timeout_work_order.records.length
|
||
mockData.timeoutWorkOrders.details = timeout_work_order.records
|
||
}
|
||
}).catch(error => {
|
||
console.error('获取超期工单数据失败:', error)
|
||
})
|
||
|
||
// 获取超期工单数据
|
||
getTableList('park_build_info', query).then(res => {
|
||
if (res.records && res.records.length > 0) {
|
||
|
||
}
|
||
}).catch(error => {
|
||
console.error('获取超期工单数据失败:', error)
|
||
})
|
||
|
||
handleHiddenDangerPannelData(query)
|
||
}
|
||
|
||
|
||
const handleHiddenDangerPannelData = (query) => {
|
||
let _data = {
|
||
flag: false,
|
||
general: 0,
|
||
major: 0,
|
||
overdue: 0,
|
||
processed: 0,
|
||
processing: 0,
|
||
pending: 0
|
||
}
|
||
|
||
let _data2 = {
|
||
flag: false,
|
||
general: 0,
|
||
major: 0,
|
||
overdue: 0,
|
||
processed: 0,
|
||
processing: 0,
|
||
pending: 0
|
||
}
|
||
progressChart.value = echarts.init(progressChartRef.value)
|
||
const width = progressChart.value.getWidth()
|
||
const progressOption = {
|
||
series: [
|
||
{
|
||
type: 'pie',
|
||
radius: '60%',
|
||
center: ['50%', '50%'],
|
||
left: 0,
|
||
top: 0,
|
||
bottom: 0,
|
||
data: [
|
||
// { value: overdueCnt || 0, name: '已逾期', itemStyle: { color: '#ef4444' } },
|
||
// { value: processedCnt || 0, name: '已处理', itemStyle: { color: '#10b981' } },
|
||
// { value: pendingCnt || 0, name: '待排查', itemStyle: { color: '#eab308' } },
|
||
// { value: processingCnt || 0, name: '处理中', itemStyle: { color: '#3b82f6' } }
|
||
] as any,
|
||
label: {
|
||
alignTo: 'edge',
|
||
formatter: '{time|{c} %}\n',
|
||
minMargin: 5,
|
||
edgeDistance: 10,
|
||
lineHeight: 15,
|
||
rich: {
|
||
time: {
|
||
fontSize: 10,
|
||
color: '#fff'
|
||
}
|
||
}
|
||
},
|
||
labelLine: {
|
||
length: 5,
|
||
length2: 0,
|
||
maxSurfaceAngle: 10
|
||
},
|
||
labelLayout: function (params: any) {
|
||
const isLeft = params.labelRect.x < width / 2;
|
||
const points = params.labelLinePoints;
|
||
points[2][0] = isLeft
|
||
? params.labelRect.x
|
||
: params.labelRect.x + params.labelRect.width;
|
||
return {
|
||
labelLinePoints: points
|
||
};
|
||
},
|
||
}
|
||
]
|
||
}
|
||
try {
|
||
// 获取隐患排查治理数据
|
||
getTableList('risk_level_count', query).then(res => {
|
||
if (res.records && res.records.length > 0) {
|
||
_data.general = _data.general + Number(res.records[0].general_count)
|
||
_data.major = _data.major + Number(res.records[0].major_count)
|
||
// 获取隐患排查治理数据
|
||
getTableList('risk_status_count', query).then(res => {
|
||
if (res.records && res.records.length > 0) {
|
||
_data.overdue = _data.overdue + Number(res.records[0].overdueCnt)
|
||
_data.processed = _data.processed + Number(res.records[0].processedCnt)
|
||
_data.processing = _data.processing + Number(res.records[0].processingCnt)
|
||
_data.pending = _data.pending
|
||
_data.flag = true
|
||
|
||
if (_data2.flag) {
|
||
// 合并数据
|
||
console.log("请求系统和第三方成功,合并数据", _data, _data2);
|
||
let generalCnt = _data.general + _data2.general
|
||
let majorCnt = _data.major + _data2.major
|
||
mockData.hiddenDangerData.generalCount = generalCnt
|
||
mockData.hiddenDangerData.severityCount = majorCnt
|
||
|
||
let totalCnt = generalCnt + majorCnt
|
||
let overdueCnt = ((_data.overdue + _data2.overdue) / totalCnt * 100).toFixed(2)
|
||
let processedCnt = ((_data.processed + _data2.processed) / totalCnt * 100).toFixed(2)
|
||
let processingCnt = ((_data.processing + _data2.processing) / totalCnt * 100).toFixed(2)
|
||
let pendingCnt = ((_data.pending + _data2.pending) / totalCnt * 100).toFixed(2)
|
||
|
||
progressOption.series[0].data = [
|
||
{ value: overdueCnt || 0, name: '已逾期', itemStyle: { color: '#ef4444' } },
|
||
{ value: processedCnt || 0, name: '已处理', itemStyle: { color: '#10b981' } },
|
||
{ value: pendingCnt || 0, name: '待排查', itemStyle: { color: '#eab308' } },
|
||
{ value: processingCnt || 0, name: '处理中', itemStyle: { color: '#3b82f6' } }
|
||
]
|
||
if (progressChart.value != null) {
|
||
progressChart.value.setOption(progressOption)
|
||
}
|
||
} else {
|
||
console.log("请求系统成功,展示数据", _data, _data2);
|
||
mockData.hiddenDangerData.generalCount = _data.general
|
||
mockData.hiddenDangerData.severityCount = _data.major
|
||
|
||
let totalCnt = _data.general + _data.major
|
||
let overdueCnt = (_data.overdue / totalCnt * 100).toFixed(2)
|
||
let processedCnt = (_data.processed / totalCnt * 100).toFixed(2)
|
||
let processingCnt = (_data.processing / totalCnt * 100).toFixed(2)
|
||
let pendingCnt = (_data.pending / totalCnt * 100).toFixed(2)
|
||
progressOption.series[0].data = [
|
||
{ value: overdueCnt || 0, name: '已逾期', itemStyle: { color: '#ef4444' } },
|
||
{ value: processedCnt || 0, name: '已处理', itemStyle: { color: '#10b981' } },
|
||
{ value: pendingCnt || 0, name: '待排查', itemStyle: { color: '#eab308' } },
|
||
{ value: processingCnt || 0, name: '处理中', itemStyle: { color: '#3b82f6' } }
|
||
]
|
||
if (progressChart.value != null) {
|
||
progressChart.value.setOption(progressOption)
|
||
}
|
||
}
|
||
}
|
||
})
|
||
}
|
||
})
|
||
|
||
|
||
|
||
// 获取隐患排查治理数据
|
||
getTableList('hidden_danger_investigation', query).then(res => {
|
||
if (res.records && res.records.length > 0) {
|
||
_data2.general = Number(res.records[0].general)
|
||
_data2.major = Number(res.records[0].major)
|
||
|
||
// 获取隐患排查治理处理进度数据
|
||
getTableList('hidden_danger_process_progress', query).then(res => {
|
||
if (res.records && res.records.length > 0) {
|
||
_data2.flag = true
|
||
_data2.overdue = Number(res.records[0].overdue) / 100 * (_data2.general + _data2.major)
|
||
_data2.processed = Number(res.records[0].processed) / 100 * (_data2.general + _data2.major)
|
||
_data2.processing = Number(res.records[0].processing) / 100 * (_data2.general + _data2.major)
|
||
_data2.pending = Number(res.records[0].pending) / 100 * (_data2.general + _data2.major)
|
||
|
||
if (_data.flag) {
|
||
console.log("请求第三方和系统成功,合并数据", _data, _data2);
|
||
// 合并数据
|
||
let generalCnt = _data.general + _data2.general
|
||
let majorCnt = _data.major + _data2.major
|
||
mockData.hiddenDangerData.generalCount = generalCnt
|
||
mockData.hiddenDangerData.severityCount = majorCnt
|
||
|
||
let totalCnt = generalCnt + majorCnt
|
||
let overdueCnt = ((_data.overdue + _data2.overdue) / totalCnt * 100).toFixed(2)
|
||
let processedCnt = ((_data.processed + _data2.processed) / totalCnt * 100).toFixed(2)
|
||
let processingCnt = ((_data.processing + _data2.processing) / totalCnt * 100).toFixed(2)
|
||
let pendingCnt = ((_data.pending + _data2.pending) / totalCnt * 100).toFixed(2)
|
||
progressOption.series[0].data = [
|
||
{ value: overdueCnt || 0, name: '已逾期', itemStyle: { color: '#ef4444' } },
|
||
{ value: processedCnt || 0, name: '已处理', itemStyle: { color: '#10b981' } },
|
||
{ value: pendingCnt || 0, name: '待排查', itemStyle: { color: '#eab308' } },
|
||
{ value: processingCnt || 0, name: '处理中', itemStyle: { color: '#3b82f6' } }
|
||
]
|
||
if (progressChart.value != null) {
|
||
progressChart.value.setOption(progressOption)
|
||
}
|
||
} else {
|
||
//显示三方数据
|
||
console.log("请求第三方成功,展示数据", _data, _data2);
|
||
mockData.hiddenDangerData.generalCount = _data2.general
|
||
mockData.hiddenDangerData.severityCount = _data2.major
|
||
|
||
let overdueCnt = res.records[0].overdue
|
||
let processedCnt = res.records[0].processed
|
||
let processingCnt = res.records[0].processing
|
||
let pendingCnt = res.records[0].pending
|
||
progressOption.series[0].data = [
|
||
{ value: overdueCnt || 0, name: '已逾期', itemStyle: { color: '#ef4444' } },
|
||
{ value: processedCnt || 0, name: '已处理', itemStyle: { color: '#10b981' } },
|
||
{ value: pendingCnt || 0, name: '待排查', itemStyle: { color: '#eab308' } },
|
||
{ value: processingCnt || 0, name: '处理中', itemStyle: { color: '#3b82f6' } }
|
||
]
|
||
if (progressChart.value != null) {
|
||
progressChart.value.setOption(progressOption)
|
||
}
|
||
}
|
||
}
|
||
}).catch(error => {
|
||
console.error('获取隐患排查治理处理进度数据失败:', error)
|
||
})
|
||
}
|
||
}).catch(error => {
|
||
console.error('获取隐患排查治理数据失败:', error)
|
||
})
|
||
} catch (error) {
|
||
console.error('获取隐患排查治理数据失败:', error)
|
||
}
|
||
}
|
||
|
||
|
||
const trainingStatistics = ref<any>({})
|
||
const trainingList = ref<any>({})
|
||
const handleTabClick = async (tab: string) => {
|
||
activeTab.value = tab
|
||
let code = ''
|
||
let code1 = ''
|
||
switch (tab) {
|
||
case '高危作业':
|
||
code = "fire_drill"
|
||
code1 = "fire_drill_detail"
|
||
|
||
getDangerDetail(query.campus_id).then(res => {
|
||
const list = res.records.map((item: any) => ({description: item.contenttext}))
|
||
dangerList.value = list
|
||
})
|
||
|
||
getDangerCount(query.campus_id).then(res => {
|
||
unfinishedCount.value = res.records[0].wks
|
||
inProgressCount.value = res.records[0].jxz
|
||
finishedCount.value = res.records[0].ywc
|
||
})
|
||
|
||
break
|
||
case '安全培训考试':
|
||
code = 'security_training_count'
|
||
code1 = 'security_training_detail'
|
||
|
||
const res = await getExamDetail(query.campus_id)
|
||
|
||
examList.value = res.records
|
||
|
||
break
|
||
case '应急预案及演练':
|
||
code = 'security_training_count'
|
||
code1 = 'security_training_detail'
|
||
|
||
const res1 = await getDrillDetail(query.campus_id)
|
||
|
||
drillList.value = res1.records
|
||
|
||
break
|
||
}
|
||
// 根据不同的tab请求不同的接口
|
||
getTableList(
|
||
code,
|
||
{ ...query, activeTab: tab }
|
||
).then(response => {
|
||
// 更新风险统计数据 - 传递完整的数组数据用于饼图显示
|
||
if (response.records && response.records.length > 0) {
|
||
trainingStatistics.value = response.records[0]
|
||
mockData.hiddenDangerData.progress = response.records[0]
|
||
}
|
||
}).catch(error => {
|
||
console.error('获取风险统计数据失败:', error)
|
||
})
|
||
getTableList(
|
||
code1,
|
||
{ ...query, activeTab: tab }
|
||
).then(response => {
|
||
// 更新风险统计数据 - 传递完整的数组数据用于饼图显示
|
||
if (response.records && response.records.length > 0) {
|
||
mockData.hiddenDangerData.progressDetail = response.records
|
||
}
|
||
}).catch(error => {
|
||
console.error('获取风险统计数据失败:', error)
|
||
})
|
||
}
|
||
|
||
// 批量更新所有数字的方法
|
||
const updateAllCounts = (counts: {
|
||
total?: number
|
||
formal?: number
|
||
external?: number
|
||
visitor?: number
|
||
}) => {
|
||
if (isAnimating.value) return
|
||
|
||
isAnimating.value = true
|
||
const promises: Promise<void>[] = []
|
||
|
||
if (counts.total !== undefined) {
|
||
promises.push(new Promise<void>((resolve) => {
|
||
const startValue = mockData.totalCount
|
||
animateNumber(startValue, counts.total || 0, animationDuration, (value) => {
|
||
mockData.totalCount = value
|
||
})
|
||
setTimeout(resolve, animationDuration)
|
||
}))
|
||
}
|
||
|
||
if (counts.formal !== undefined) {
|
||
promises.push(new Promise<void>((resolve) => {
|
||
const startValue = mockData.formalEmployeeCount
|
||
animateNumber(startValue, counts.formal || 0, animationDuration, (value) => {
|
||
mockData.formalEmployeeCount = value
|
||
})
|
||
setTimeout(resolve, animationDuration)
|
||
}))
|
||
}
|
||
|
||
if (counts.external !== undefined) {
|
||
promises.push(new Promise<void>((resolve) => {
|
||
const startValue = mockData.externalStaffCount
|
||
animateNumber(startValue, counts.external || 0, animationDuration, (value) => {
|
||
mockData.externalStaffCount = value
|
||
})
|
||
setTimeout(resolve, animationDuration)
|
||
}))
|
||
}
|
||
|
||
if (counts.visitor !== undefined) {
|
||
promises.push(new Promise<void>((resolve) => {
|
||
const startValue = mockData.visitorCount
|
||
animateNumber(startValue, counts.visitor || 0, animationDuration, (value) => {
|
||
mockData.visitorCount = value
|
||
})
|
||
setTimeout(resolve, animationDuration)
|
||
}))
|
||
}
|
||
|
||
Promise.all(promises).then(() => {
|
||
isAnimating.value = false
|
||
flashNumbers() // 更新完成后闪烁效果
|
||
})
|
||
}
|
||
|
||
// 数字滚动动画方法
|
||
const animateNumber = (startValue: number, endValue: number, duration: number, callback: (value: number) => void) => {
|
||
const startTime = Date.now()
|
||
const difference = endValue - startValue
|
||
|
||
const animate = () => {
|
||
const currentTime = Date.now()
|
||
const elapsed = currentTime - startTime
|
||
const progress = Math.min(elapsed / duration, 1)
|
||
|
||
// 使用缓动函数让动画更自然
|
||
const easeOutQuart = 1 - Math.pow(1 - progress, 4)
|
||
const currentValue = Math.round(startValue + difference * easeOutQuart)
|
||
|
||
callback(currentValue)
|
||
|
||
if (progress < 1) {
|
||
requestAnimationFrame(animate)
|
||
}
|
||
}
|
||
|
||
animate()
|
||
}
|
||
|
||
|
||
// 数字变化时的闪烁效果
|
||
const flashNumbers = () => {
|
||
const numberElements = document.querySelectorAll('.total-number, .type-number')
|
||
numberElements.forEach((el) => {
|
||
el.classList.add('flash')
|
||
setTimeout(() => {
|
||
el.classList.remove('flash')
|
||
}, 300)
|
||
})
|
||
}
|
||
|
||
// 更新时间
|
||
const updateTime = (): void => {
|
||
const now = new Date()
|
||
const year = now.getFullYear()
|
||
const month = String(now.getMonth() + 1).padStart(2, '0')
|
||
const day = String(now.getDate()).padStart(2, '0')
|
||
const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
||
const weekday = weekdays[now.getDay()]
|
||
const hours = String(now.getHours()).padStart(2, '0')
|
||
const minutes = String(now.getMinutes()).padStart(2, '0')
|
||
const seconds = String(now.getSeconds()).padStart(2, '0')
|
||
|
||
currentDate.value = `${year}年${month}月${day}日`
|
||
currentDateTime.value = `${year}-${month}-${day}`
|
||
currentWeek.value = `${weekday}`
|
||
currentTime.value = `${hours}:${minutes}:${seconds}`
|
||
|
||
query.startDate = currentDateTime.value + " 00:00:00"
|
||
query.endDate = currentDateTime.value + " 23:59:59"
|
||
}
|
||
|
||
onUnmounted(() => {
|
||
if (dashboardTimerId.value) {
|
||
clearInterval(dashboardTimerId.value)
|
||
dashboardTimerId.value = null
|
||
}
|
||
if (timeUpdateTimerId.value) {
|
||
clearInterval(timeUpdateTimerId.value)
|
||
timeUpdateTimerId.value = null
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
/* 响应式设计 - 自动适应不同屏幕尺寸 */
|
||
@media (width <=1200px) {
|
||
.dashboard-container {
|
||
.content-container {
|
||
column-gap: 4vw;
|
||
|
||
.center-container {
|
||
width: 60vh;
|
||
height: 60vh;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@media (width <=1024px) {
|
||
.dashboard-container {
|
||
.content-container {
|
||
column-gap: 3vw;
|
||
padding: 0 8px 15px;
|
||
|
||
.left-wrapper {
|
||
.left-top .hazard-wrapper {
|
||
.progress-chart {
|
||
min-height: 180px;
|
||
}
|
||
}
|
||
|
||
.left-bottom .donut-chart-with-labels {
|
||
width: 35vw;
|
||
height: 35vh;
|
||
}
|
||
}
|
||
|
||
.center-container {
|
||
width: 55vh;
|
||
height: 55vh;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@media (width <=768px) {
|
||
.dashboard-container {
|
||
.content-container {
|
||
column-gap: 2vw;
|
||
padding: 0 5px 10px;
|
||
|
||
.left-wrapper {
|
||
.left-top .hazard-wrapper {
|
||
.progress-chart {
|
||
min-height: 160px;
|
||
}
|
||
}
|
||
|
||
.left-bottom .donut-chart-with-labels {
|
||
width: 40vw;
|
||
height: 40vh;
|
||
margin-left: 1vw;
|
||
}
|
||
}
|
||
|
||
.center-container {
|
||
top: 60%;
|
||
width: 50vh;
|
||
height: 50vh;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@media (width <=480px) {
|
||
.dashboard-container {
|
||
.content-container {
|
||
column-gap: 1vw;
|
||
padding: 0 3px 8px;
|
||
|
||
.left-wrapper {
|
||
.left-top .hazard-wrapper {
|
||
.progress-chart {
|
||
min-height: 140px;
|
||
}
|
||
}
|
||
|
||
.left-bottom .donut-chart-with-labels {
|
||
width: 45vw;
|
||
height: 45vh;
|
||
margin-left: 0.5vw;
|
||
}
|
||
}
|
||
|
||
.center-container {
|
||
top: 65%;
|
||
width: 45vh;
|
||
height: 45vh;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.dot.red {
|
||
background-color: #ef4444;
|
||
}
|
||
|
||
.dot.green {
|
||
background-color: #10b981;
|
||
}
|
||
|
||
.dot.yellow {
|
||
background-color: #eab308;
|
||
}
|
||
|
||
.dot.blue {
|
||
background-color: #3b82f6;
|
||
}
|
||
|
||
.dot.cyan {
|
||
background-color: #06b6d4;
|
||
}
|
||
|
||
.dot.purple {
|
||
background-color: #8b5cf6;
|
||
}
|
||
|
||
.dot.orange {
|
||
background-color: #f59e0b;
|
||
}
|
||
|
||
.dashboard-container {
|
||
position: relative;
|
||
display: flex;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
font-size: 0.8rem;
|
||
color: #fff;
|
||
background: url('@/assets/images/v2_rel0n6.png');
|
||
background-color: #030e22;
|
||
background-size: cover;
|
||
flex-direction: column;
|
||
|
||
.header-container {
|
||
display: flex;
|
||
height: 80px;
|
||
background: url('@/assets/images/top_bg.png') no-repeat;
|
||
background-size: 100%;
|
||
|
||
.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-button {
|
||
display: inline-flex;
|
||
height: 2vh;
|
||
min-width: 6vw;
|
||
padding: 4px 16px;
|
||
margin-left: 0.5vw;
|
||
font-size: 0.9rem;
|
||
cursor: pointer;
|
||
background: rgb(13 24 84 / 80%);
|
||
border: 1px solid rgb(59 130 246 / 40%);
|
||
transition: all 0.3s ease;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.back-button:hover {
|
||
background: rgb(59 130 246 / 30%);
|
||
border-color: rgb(59 130 246 / 60%);
|
||
}
|
||
}
|
||
|
||
.date-wrapper {
|
||
display: flex;
|
||
margin-right: 20px;
|
||
flex: 1;
|
||
justify-content: flex-end;
|
||
column-gap: 2vw;
|
||
}
|
||
|
||
.header-right {
|
||
margin-right: 20px;
|
||
font-size: 0.9rem;
|
||
line-height: 80px;
|
||
text-align: right;
|
||
flex: 1;
|
||
}
|
||
|
||
.header-title {
|
||
font-size: 1.3rem;
|
||
font-weight: bold;
|
||
line-height: 40px;
|
||
color: #fff;
|
||
text-align: center;
|
||
flex: 1;
|
||
}
|
||
}
|
||
|
||
.content-container {
|
||
position: relative;
|
||
display: flex;
|
||
width: calc(100vw - 20px);
|
||
height: calc(100vh - 105px);
|
||
padding: 0 10px 20px;
|
||
column-gap: 7vw;
|
||
|
||
.panel-title {
|
||
margin: 4px 20px 0;
|
||
font-size: 0.8rem;
|
||
font-weight: bold;
|
||
color: #fff;
|
||
}
|
||
|
||
.bottom-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
width: 67%;
|
||
margin-bottom: 20px;
|
||
|
||
.bottom-card-title {
|
||
display: flex;
|
||
margin-top: 5px;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.type-wrapper {
|
||
display: flex;
|
||
justify-content: center;
|
||
column-gap: 2vw;
|
||
margin-top: 5px;
|
||
|
||
.type-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
column-gap: 4px;
|
||
|
||
.type-btn {
|
||
display: inline-block;
|
||
padding: 2px 30px;
|
||
font-size: 0.7rem;
|
||
cursor: pointer;
|
||
background-color: rgb(59 130 246 / 20%);
|
||
border-radius: 12px;
|
||
user-select: none;
|
||
|
||
&.blue {
|
||
background-color: rgb(32 148 235);
|
||
}
|
||
|
||
&.green {
|
||
background-color: rgb(80 139 79);
|
||
}
|
||
|
||
&.yellow {
|
||
background-color: rgb(204 96 26);
|
||
}
|
||
}
|
||
|
||
.type-num {
|
||
margin-top: 0.3vh;
|
||
font-size: 0.9rem;
|
||
}
|
||
}
|
||
}
|
||
|
||
.hazard-wrapper {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
column-gap: 1.8vw;
|
||
|
||
.progress-chart {
|
||
width: 55%;
|
||
min-height: 140px;
|
||
}
|
||
|
||
.progress-legend {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: 0.5vw;
|
||
|
||
.legend-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
font-size: 0.7rem;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.left-wrapper {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
row-gap: 1rem;
|
||
height: 100%;
|
||
|
||
.left-top {
|
||
padding: 0 5px;
|
||
background-image:
|
||
url('@/assets/images/screen/left_top_img.png'),
|
||
url('@/assets/images/screen/left_center_img.png'),
|
||
url('@/assets/images/screen/left_bottom_img.png');
|
||
background-position:
|
||
top center,
|
||
left center,
|
||
bottom center;
|
||
|
||
/* 设置大小,注意中间的背景图应该覆盖整个容器 */
|
||
background-repeat: no-repeat, no-repeat, no-repeat;
|
||
|
||
/* 设置位置 */
|
||
background-size:
|
||
100% 90px,
|
||
cover,
|
||
100% 68px;
|
||
flex: 1;
|
||
|
||
/* 设置重复方式 */
|
||
.top-card {
|
||
display: flex;
|
||
padding: 0 20px;
|
||
column-gap: 15px;
|
||
font-size: 0.8rem;
|
||
|
||
.top-card-left {
|
||
display: flex;
|
||
height: 12vh;
|
||
min-width: 15vw;
|
||
padding: 0 10px;
|
||
background-image: url('@/assets/imgs/total_count_card_bg.png');
|
||
background-size: cover;
|
||
column-gap: 6px;
|
||
align-items: center;
|
||
|
||
.number-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
font-size: 0.8rem;
|
||
color: #fff;
|
||
}
|
||
|
||
.total-number {
|
||
display: inline-block;
|
||
width: 26px;
|
||
height: 50px;
|
||
font-size: 0.8rem;
|
||
line-height: 50px;
|
||
color: #fff;
|
||
text-align: center;
|
||
background-color: rgb(177 74 201 / 100%);
|
||
border-radius: 4px;
|
||
}
|
||
}
|
||
|
||
.top-card-right {
|
||
display: flex;
|
||
height: 12vh;
|
||
min-width: 20vw;
|
||
background-image: url('@/assets/imgs/staff_types_bg.png');
|
||
background-position: top center;
|
||
background-size: cover;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
row-gap: 4px;
|
||
|
||
.top-card-right-item {
|
||
display: flex;
|
||
align-items: center;
|
||
column-gap: 5px;
|
||
padding: 0 10px;
|
||
font-size: 0.7rem;
|
||
color: #fff;
|
||
|
||
.type-number-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
font-size: 0.8rem;
|
||
color: #fff;
|
||
|
||
.type-number {
|
||
display: inline-block;
|
||
width: 14px;
|
||
height: 25px;
|
||
font-size: 0.9rem;
|
||
line-height: 25px;
|
||
color: #fff;
|
||
text-align: center;
|
||
background-color: #1afb8f;
|
||
border-radius: 2px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
.left-bottom {
|
||
background-image:
|
||
url('@/assets/images/screen/left_top_2_img.png'),
|
||
url('@/assets/images/screen/left_center_img.png'),
|
||
url('@/assets/images/screen/left_bottom_img.png');
|
||
background-position:
|
||
top center,
|
||
left center,
|
||
bottom center;
|
||
|
||
/* 设置大小,注意中间的背景图应该覆盖整个容器 */
|
||
background-repeat: no-repeat, no-repeat, no-repeat;
|
||
|
||
/* 设置位置 */
|
||
background-size:
|
||
100% 90px,
|
||
cover,
|
||
100% 68px;
|
||
flex: 1;
|
||
|
||
/* 设置重复方式 */
|
||
.tabs {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
column-gap: 8px;
|
||
}
|
||
|
||
.tab {
|
||
padding: 2px 10px;
|
||
color: #fff;
|
||
cursor: pointer;
|
||
border-radius: 4px;
|
||
transition: all 0.2s ease-in-out;
|
||
user-select: none;
|
||
}
|
||
|
||
.tab:hover {
|
||
color: #1afb8f;
|
||
}
|
||
|
||
.tab.active {
|
||
color: #1afb8f;
|
||
background: rgb(26 251 143 / 12%);
|
||
border: 1px solid rgb(26 251 143 / 35%);
|
||
}
|
||
|
||
.divider {
|
||
margin: 0 2px;
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.donut-chart-with-labels {
|
||
width: 30vw;
|
||
height: 30vh;
|
||
min-height: 200px;
|
||
margin-left: 2vw;
|
||
}
|
||
|
||
.row-wrapper {
|
||
display: flex;
|
||
width: 67%;
|
||
column-gap: 1vw;
|
||
margin-left: 1vw;
|
||
|
||
.row-item {
|
||
display: flex;
|
||
padding: 0.5vh 0;
|
||
font-size: 0.7rem;
|
||
background-color: rgb(4 35 125 / 50%);
|
||
flex-direction: column;
|
||
align-items: center;
|
||
column-gap: 10px;
|
||
flex: 1;
|
||
|
||
.row-item-title {}
|
||
|
||
.row-item-number {
|
||
font-size: 1rem;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.right-wrapper {
|
||
display: flex;
|
||
height: 100%;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
row-gap: 1rem;
|
||
|
||
.right-top {
|
||
/* 设置重复方式 */
|
||
display: flex;
|
||
background-image:
|
||
url('@/assets/images/screen/right_top_img.png'),
|
||
url('@/assets/images/screen/right_center_img.png'),
|
||
url('@/assets/images/screen/right_bottom_img.png');
|
||
background-position:
|
||
top center,
|
||
right center,
|
||
bottom center;
|
||
|
||
/* 设置大小,注意中间的背景图应该覆盖整个容器 */
|
||
background-repeat: no-repeat, no-repeat, no-repeat;
|
||
|
||
/* 设置位置 */
|
||
background-size:
|
||
100% 90px,
|
||
cover,
|
||
100% 68px;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
|
||
.tip-container {
|
||
position: relative;
|
||
display: flex;
|
||
width: 70%;
|
||
height: 80px;
|
||
padding-right: 20px;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
|
||
.tip-image {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 0;
|
||
transform: translateY(-50%) translateX(-50%);
|
||
|
||
.number {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
font-size: 1rem;
|
||
color: #fff;
|
||
transform: translate(-80%, -50%);
|
||
}
|
||
}
|
||
|
||
.tip-content {
|
||
position: absolute;
|
||
inset: 0% 0 0 6%;
|
||
display: flex;
|
||
padding-left: 20px;
|
||
align-items: center;
|
||
|
||
.col-item {
|
||
display: flex;
|
||
margin-left: 1vw;
|
||
align-items: center;
|
||
|
||
span {
|
||
margin-right: 10px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.right-bottom {
|
||
/* 设置重复方式 */
|
||
|
||
display: flex;
|
||
background-image:
|
||
url('@/assets/images/screen/right_top_img.png'),
|
||
url('@/assets/images/screen/right_center_img.png'),
|
||
url('@/assets/images/screen/right_bottom_img.png');
|
||
background-position:
|
||
top center,
|
||
right center,
|
||
bottom center;
|
||
|
||
/* 设置大小,注意中间的背景图应该覆盖整个容器 */
|
||
background-repeat: no-repeat, no-repeat, no-repeat;
|
||
|
||
/* 设置位置 */
|
||
background-size:
|
||
100% 90px,
|
||
cover,
|
||
100% 68px;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
|
||
.tip-container {
|
||
position: relative;
|
||
display: flex;
|
||
width: 50%;
|
||
height: 80px;
|
||
padding-right: 20px;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
|
||
.tip-image {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 0;
|
||
transform: translateY(-50%) translateX(-50%);
|
||
|
||
.number {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
font-size: 1rem;
|
||
color: #fff;
|
||
transform: translate(-80%, -50%);
|
||
}
|
||
}
|
||
|
||
.tip-content {
|
||
position: absolute;
|
||
inset: 0% 0 0 6%;
|
||
display: flex;
|
||
padding-left: 20px;
|
||
align-items: center;
|
||
|
||
.col-item {
|
||
display: flex;
|
||
margin-left: 1vw;
|
||
align-items: center;
|
||
|
||
span {
|
||
margin-right: 10px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.center-container {
|
||
position: fixed;
|
||
top: 55%;
|
||
left: 50%;
|
||
z-index: 1;
|
||
display: flex;
|
||
width: 65vh;
|
||
height: 65vh;
|
||
color: #fff;
|
||
background-image: url('@/assets/images/circle_bg.png');
|
||
background-size: cover;
|
||
transform: translate(-50%, -50%);
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.test-btn {
|
||
position: absolute;
|
||
top: 10px;
|
||
right: 10px;
|
||
z-index: 1000;
|
||
padding: 8px 16px;
|
||
font-size: 12px;
|
||
color: white;
|
||
cursor: pointer;
|
||
background: rgb(59 130 246 / 80%);
|
||
border: 1px solid rgb(59 130 246 / 40%);
|
||
border-radius: 4px;
|
||
|
||
&:hover {
|
||
background: rgb(59 130 246 / 60%);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|