2025-10-17 10:31:13 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="dashboard-container">
|
|
|
|
|
|
<!-- 顶部标题栏 -->
|
|
|
|
|
|
<div class="header-container">
|
|
|
|
|
|
<div class="header-left">
|
2025-12-13 18:14:32 +08:00
|
|
|
|
<div class="back-button" @click="openRegionSelector"> 集团
|
2025-10-17 10:31:13 +08:00
|
|
|
|
<span>···</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<h1 class="header-title">总部综合监控大屏</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">
|
|
|
|
|
|
<OverviewPanel
|
|
|
|
|
|
:totalCount="dashboardData?.totalCount || 0"
|
|
|
|
|
|
:formalEmployeeCount="dashboardData?.formalEmployeeCount || 0"
|
|
|
|
|
|
:externalStaffCount="dashboardData?.externalStaffCount || 0"
|
|
|
|
|
|
:visitorCount="dashboardData?.visitorCount || 0"
|
|
|
|
|
|
:parkStatistics="dashboardData?.parkStatistics"/>
|
|
|
|
|
|
<RiskStatisticsPanel
|
|
|
|
|
|
:riskStatistics="riskStatistics" :dangerDetail="dangerDetail"
|
|
|
|
|
|
@tab-change="handleRiskTabChange" :campus_id="query.campus_id"/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="right-wrapper">
|
|
|
|
|
|
<HighRiskAlertPanel
|
|
|
|
|
|
:alertData="dashboardData?.alertData" :alertDetails="dashboardData?.alertData.details"
|
|
|
|
|
|
:sourceIndex="sourceIndex"/>
|
|
|
|
|
|
<TimeoutWorkOrderPanel
|
|
|
|
|
|
:timeoutWorkOrders="dashboardData?.timeoutWorkOrders"
|
|
|
|
|
|
:alertDetails="dashboardData?.timeoutWorkOrders.details" :sourceIndex="sourceIndex"/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<HiddenDangerPanel :hiddenDangerData="dashboardData?.hiddenDangerData"/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 区域选择弹窗 -->
|
|
|
|
|
|
<RegionSelector
|
|
|
|
|
|
v-model="regionSelectorVisible" :modelSelected="selectedRegion" :regions="regionOption"
|
|
|
|
|
|
@change="onRegionChange"/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import {getTableList, getTableData} from './report'
|
|
|
|
|
|
import {ref, onMounted, watch, onUnmounted} from 'vue'
|
|
|
|
|
|
import {useRouter} from 'vue-router'
|
|
|
|
|
|
import RegionSelector from './components/RegionSelector.vue'
|
|
|
|
|
|
import WeatherWarning from './components/WeatherWarning.vue'
|
|
|
|
|
|
import {getDashboardData, getAlertDetails, type DashboardData} from '@/api/dashboard'
|
|
|
|
|
|
|
|
|
|
|
|
import OverviewPanel from './components/OverviewPanel.vue'
|
|
|
|
|
|
import RiskStatisticsPanel from './components/RiskStatisticsPanel.vue'
|
|
|
|
|
|
import HighRiskAlertPanel from './components/HighRiskAlertPanel.vue'
|
|
|
|
|
|
import TimeoutWorkOrderPanel from './components/TimeoutWorkOrderPanel.vue'
|
|
|
|
|
|
import HiddenDangerPanel from './components/HiddenDangerPanel.vue'
|
2025-11-06 11:56:15 +08:00
|
|
|
|
import {error} from "echarts/types/src/util/log";
|
2025-10-17 10:31:13 +08:00
|
|
|
|
|
|
|
|
|
|
// 类型定义
|
|
|
|
|
|
interface AlertItem {
|
|
|
|
|
|
describe: string
|
|
|
|
|
|
status: number
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface RegionItem {
|
|
|
|
|
|
name: string
|
|
|
|
|
|
code: string
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type TabType = '高危作业' | '安全培训考试' | '安全培训考试'
|
|
|
|
|
|
|
|
|
|
|
|
// 响应式数据
|
|
|
|
|
|
const currentDateTime = ref<string>('')
|
|
|
|
|
|
const currentDate = ref<string>('')
|
|
|
|
|
|
const currentWeek = ref<string>('')
|
|
|
|
|
|
const currentTime = ref<string>('')
|
|
|
|
|
|
const regionSelectorVisible = ref<boolean>(false)
|
|
|
|
|
|
const selectedRegion = ref<string>('')
|
|
|
|
|
|
const sourceIndex = ref<number>(1)
|
|
|
|
|
|
|
|
|
|
|
|
// 大屏数据
|
|
|
|
|
|
const dashboardData = ref<any>(null)
|
|
|
|
|
|
|
|
|
|
|
|
// 总体概览-总计数量数据
|
|
|
|
|
|
const totalCount = ref<number>(0)
|
|
|
|
|
|
const formalEmployeeCount = ref<number>(0)
|
|
|
|
|
|
const externalStaffCount = ref<number>(0)
|
|
|
|
|
|
const visitorCount = ref<number>(0)
|
|
|
|
|
|
// 总体概览-各园区统计
|
|
|
|
|
|
const parkStatistics = ref<any>()
|
|
|
|
|
|
// 高危作业/安全培训考试/安全培训考试
|
|
|
|
|
|
const riskStatistics = ref<any>()
|
|
|
|
|
|
const dangerDetail = ref<any>()
|
|
|
|
|
|
|
|
|
|
|
|
// 动画相关的状态
|
|
|
|
|
|
const isAnimating = ref<boolean>(false)
|
|
|
|
|
|
const animationDuration = 2000 // 动画持续时间(毫秒)
|
|
|
|
|
|
|
|
|
|
|
|
// 数字变化时的闪烁效果
|
|
|
|
|
|
const flashNumbers = () => {
|
|
|
|
|
|
const numberElements = document.querySelectorAll('.total-number, .type-number')
|
|
|
|
|
|
numberElements.forEach((el) => {
|
|
|
|
|
|
el.classList.add('flash')
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
el.classList.remove('flash')
|
|
|
|
|
|
}, 300)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 数字滚动动画方法
|
|
|
|
|
|
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 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 = totalCount.value
|
|
|
|
|
|
animateNumber(startValue, counts.total || 0, animationDuration, (value) => {
|
|
|
|
|
|
totalCount.value = value
|
|
|
|
|
|
})
|
|
|
|
|
|
setTimeout(resolve, animationDuration)
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (counts.formal !== undefined) {
|
|
|
|
|
|
promises.push(new Promise<void>((resolve) => {
|
|
|
|
|
|
const startValue = formalEmployeeCount.value
|
|
|
|
|
|
animateNumber(startValue, counts.formal || 0, animationDuration, (value) => {
|
|
|
|
|
|
formalEmployeeCount.value = value
|
|
|
|
|
|
})
|
|
|
|
|
|
setTimeout(resolve, animationDuration)
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (counts.external !== undefined) {
|
|
|
|
|
|
promises.push(new Promise<void>((resolve) => {
|
|
|
|
|
|
const startValue = externalStaffCount.value
|
|
|
|
|
|
animateNumber(startValue, counts.external || 0, animationDuration, (value) => {
|
|
|
|
|
|
externalStaffCount.value = value
|
|
|
|
|
|
})
|
|
|
|
|
|
setTimeout(resolve, animationDuration)
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (counts.visitor !== undefined) {
|
|
|
|
|
|
promises.push(new Promise<void>((resolve) => {
|
|
|
|
|
|
const startValue = visitorCount.value
|
|
|
|
|
|
animateNumber(startValue, counts.visitor || 0, animationDuration, (value) => {
|
|
|
|
|
|
visitorCount.value = value
|
|
|
|
|
|
})
|
|
|
|
|
|
setTimeout(resolve, animationDuration)
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Promise.all(promises).then(() => {
|
|
|
|
|
|
isAnimating.value = false
|
|
|
|
|
|
flashNumbers() // 更新完成后闪烁效果
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 路由
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
|
|
|
|
|
|
const regionOption = ref<RegionItem[]>([])
|
|
|
|
|
|
// 定时器ID
|
|
|
|
|
|
const dashboardTimerId = ref<ReturnType<typeof setInterval> | null>(null)
|
|
|
|
|
|
const timeUpdateTimerId = ref<ReturnType<typeof setInterval> | null>(null)
|
|
|
|
|
|
const sourceIndexTimerId = ref<ReturnType<typeof setInterval> | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
const query = reactive({
|
|
|
|
|
|
pageNo: 1,
|
|
|
|
|
|
pageSize: 10000,
|
|
|
|
|
|
parkCode: "",
|
|
|
|
|
|
startDate: currentDateTime.value + " 00:00:00",
|
|
|
|
|
|
endDate: currentDateTime.value + " 23:59:59",
|
|
|
|
|
|
regionCode: "",
|
|
|
|
|
|
campus_id: "",
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 生命周期
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
updateTime()
|
|
|
|
|
|
timeUpdateTimerId.value = setInterval(updateTime, 1000)
|
|
|
|
|
|
//
|
|
|
|
|
|
try {
|
|
|
|
|
|
let {records} = await getTableList(
|
|
|
|
|
|
'park_info_list'
|
|
|
|
|
|
)
|
|
|
|
|
|
// records = [
|
|
|
|
|
|
// {
|
|
|
|
|
|
// "region_id": "130601",
|
|
|
|
|
|
// "park_code": "1825468527486140416",
|
|
|
|
|
|
// "region": "北京",
|
|
|
|
|
|
// "park_name": "雄安新区总部"
|
|
|
|
|
|
// },
|
|
|
|
|
|
// {
|
|
|
|
|
|
// "region_id": "130601",
|
|
|
|
|
|
// "park_code": "1825468527486140417",
|
|
|
|
|
|
// "region": "北京",
|
|
|
|
|
|
// "park_name": "雄安地面站"
|
|
|
|
|
|
// },
|
|
|
|
|
|
// {
|
|
|
|
|
|
// "region_id": "130603",
|
|
|
|
|
|
// "park_code": "1825468527486140426",
|
|
|
|
|
|
// "region": "武汉",
|
|
|
|
|
|
// "park_name": "花山新区总部"
|
|
|
|
|
|
// }
|
|
|
|
|
|
// ]
|
|
|
|
|
|
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_code作为code
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
// 转换为数组
|
|
|
|
|
|
regionOption.value = Array.from(regionMap.values())
|
|
|
|
|
|
|
|
|
|
|
|
console.log('regionOption.value>>>>', regionOption.value);
|
|
|
|
|
|
|
|
|
|
|
|
// 将园区信息去重
|
|
|
|
|
|
const parkMap = new Map();
|
|
|
|
|
|
records.forEach(el => {
|
|
|
|
|
|
if (!parkMap.has(el.park_code)) {
|
|
|
|
|
|
parkMap.set(el.park_code, {
|
|
|
|
|
|
name: el.park_name,
|
|
|
|
|
|
code: el.park_code
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
// 将parkMap转换为数组
|
|
|
|
|
|
query.campus_id = Array.from(parkMap.values()).map(e1 => e1.code).join();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('初始化园区数据失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化数据
|
|
|
|
|
|
await loadDashboardData()
|
|
|
|
|
|
|
|
|
|
|
|
// 启动定时器
|
|
|
|
|
|
timeOut1()
|
|
|
|
|
|
|
|
|
|
|
|
// 添加周期性动画演示
|
|
|
|
|
|
dashboardTimerId.value = setInterval(async () => {
|
|
|
|
|
|
await loadDashboardData()
|
|
|
|
|
|
}, 2 * 60 * 1000) // 每2分钟更新一次
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 组件卸载时清理定时器
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
if (dashboardTimerId.value) {
|
|
|
|
|
|
clearInterval(dashboardTimerId.value)
|
|
|
|
|
|
dashboardTimerId.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
if (timeUpdateTimerId.value) {
|
|
|
|
|
|
clearInterval(timeUpdateTimerId.value)
|
|
|
|
|
|
timeUpdateTimerId.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
if (sourceIndexTimerId.value) {
|
|
|
|
|
|
clearInterval(sourceIndexTimerId.value)
|
|
|
|
|
|
sourceIndexTimerId.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
let isFirstLoad = ref<boolean>(true)
|
|
|
|
|
|
// 数据初始化方法
|
|
|
|
|
|
const loadDashboardData = async (): Promise<void> => {
|
|
|
|
|
|
const data = await getDashboardData()
|
|
|
|
|
|
if (isFirstLoad.value) {
|
|
|
|
|
|
console.log('第一次加载');
|
|
|
|
|
|
dashboardData.value = data
|
|
|
|
|
|
isFirstLoad.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
console.log('dashboardData.value>>>>>>>>>>', dashboardData.value);
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取总体概览数据
|
|
|
|
|
|
getTableList('generalTotal', query).then(generalTotal => {
|
|
|
|
|
|
if (generalTotal.records && generalTotal.records.length > 0) {
|
|
|
|
|
|
dashboardData.value.totalCount = Number(generalTotal.records[0].totalCount)
|
|
|
|
|
|
dashboardData.value.formalEmployeeCount = Number(generalTotal.records[0].formalEmployeeCount)
|
|
|
|
|
|
dashboardData.value.externalStaffCount = Number(generalTotal.records[0].externalStaffCount)
|
|
|
|
|
|
dashboardData.value.visitorCount = Number(generalTotal.records[0].visitorCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
updateAllCounts({// 使用动画更新数字
|
|
|
|
|
|
total: Number(dashboardData.value.totalCount),
|
|
|
|
|
|
formal: Number(dashboardData.value.formalEmployeeCount),
|
|
|
|
|
|
external: Number(dashboardData.value.externalStaffCount),
|
|
|
|
|
|
visitor: Number(dashboardData.value.visitorCount)
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取总体概览数据失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取各园区统计数据
|
|
|
|
|
|
getTableList('parkscreen_user_info', query).then(parkscreen_user_info => {
|
|
|
|
|
|
if (parkscreen_user_info.records && parkscreen_user_info.records.length > 0) {
|
|
|
|
|
|
dashboardData.value.parkStatistics = parkscreen_user_info.records.map(el => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
name: el.campus_name,
|
|
|
|
|
|
formal: el.formalEmployeeCount,
|
|
|
|
|
|
external: el.externalStaffCount,
|
|
|
|
|
|
visitor: el.visitorCount
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取各园区统计数据失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取风险预警数据
|
|
|
|
|
|
getTableList('risk_alert_data', query).then(risk_alert_data => {
|
|
|
|
|
|
if (risk_alert_data.records && risk_alert_data.records.length > 0) {
|
|
|
|
|
|
dashboardData.value.alertData.total = risk_alert_data.records[0].total
|
|
|
|
|
|
dashboardData.value.alertData.processed = risk_alert_data.records[0].processed
|
|
|
|
|
|
dashboardData.value.alertData.pending = risk_alert_data.records[0].pending
|
|
|
|
|
|
dashboardData.value.alertData.processing = risk_alert_data.records[0].processing
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
console.error('获取风险预警数据失败:', error)
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取风险预警数据失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取风险预警详情数据
|
|
|
|
|
|
getTableList('risk_alert_detail', query).then(risk_alert_detail => {
|
|
|
|
|
|
if (risk_alert_detail.records && risk_alert_detail.records.length > 0) {
|
|
|
|
|
|
dashboardData.value.alertData.details = risk_alert_detail.records
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
console.error('获取风险预警详情数据失败:', error)
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取风险预警详情数据失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取超期工单数据
|
|
|
|
|
|
getTableList('timeout_work_order', query).then(timeout_work_order => {
|
|
|
|
|
|
if (timeout_work_order.records && timeout_work_order.records.length > 0) {
|
|
|
|
|
|
dashboardData.value.timeoutWorkOrders.total = timeout_work_order.records.length
|
|
|
|
|
|
dashboardData.value.timeoutWorkOrders.details = timeout_work_order.records
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
console.error('获取超期工单数据失败:', error)
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取超期工单数据失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
handleRiskTabChange('高危作业')
|
|
|
|
|
|
handleHiddenDangerPannelData(query)
|
|
|
|
|
|
console.log('dashboardData.value>>>>>>>>>>', dashboardData.value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.general = 0
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.major = 0
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.progress.overdue = 0
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.progress.processed = 0
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.progress.processing = 0
|
|
|
|
|
|
// 获取隐患排查治理数据
|
|
|
|
|
|
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
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.general = generalCnt
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.major = 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)
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.progress = {
|
|
|
|
|
|
overdue: overdueCnt,
|
|
|
|
|
|
processed: processedCnt,
|
|
|
|
|
|
processing: processingCnt,
|
|
|
|
|
|
pending: pendingCnt,
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log("请求系统成功,展示数据", _data, _data2);
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.general = _data.general
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.major = _data.major
|
|
|
|
|
|
|
|
|
|
|
|
let totalCnt = _data.general + _data.major
|
|
|
|
|
|
// 显示系统数据
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.progress = {
|
|
|
|
|
|
overdue: (_data.overdue / totalCnt * 100).toFixed(2),
|
|
|
|
|
|
processed: (_data.processed / totalCnt * 100).toFixed(2),
|
|
|
|
|
|
processing: (_data.processing / totalCnt * 100).toFixed(2),
|
|
|
|
|
|
pending: (_data.pending / totalCnt * 100).toFixed(2),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取隐患排查治理数据
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
2025-11-06 11:56:15 +08:00
|
|
|
|
// 安全指数另算,再起一个报表
|
|
|
|
|
|
// dashboardData.value.hiddenDangerData.safetyIndex = res.records[0].safetyIndex
|
|
|
|
|
|
// 在这里添加获取安全指数的逻辑
|
|
|
|
|
|
getTableList('hidden_danger_safety_index', query).then(res => {
|
|
|
|
|
|
if (res.records && res.records.length > 0) {
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.safetyIndex = res.records[0].safetyIndex
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
console.error('获取隐患排查治理数据失败:', error)
|
|
|
|
|
|
})
|
2025-10-17 10:31:13 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取隐患排查治理处理进度数据
|
|
|
|
|
|
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
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.general = generalCnt
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.major = 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)
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.progress = {
|
|
|
|
|
|
overdue: overdueCnt,
|
|
|
|
|
|
processed: processedCnt,
|
|
|
|
|
|
processing: processingCnt,
|
|
|
|
|
|
pending: pendingCnt,
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
//显示三方数据
|
|
|
|
|
|
console.log("请求第三方成功,展示数据", _data, _data2);
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.general = _data2.general
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.major = _data2.major
|
|
|
|
|
|
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.progress = {
|
|
|
|
|
|
overdue: res.records[0].overdue,
|
|
|
|
|
|
processed: res.records[0].processed,
|
|
|
|
|
|
processing: res.records[0].processing,
|
|
|
|
|
|
pending: res.records[0].pending,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
console.error('获取隐患排查治理处理进度数据失败:', error)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
console.error('获取隐患排查治理数据失败:', error)
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取隐患排查治理数据失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取隐患排查治理TOP3类型数据
|
|
|
|
|
|
getTableList('hidden_danger_top', query).then(hidden_danger_top => {
|
|
|
|
|
|
if (hidden_danger_top.records && hidden_danger_top.records.length > 0) {
|
|
|
|
|
|
dashboardData.value.hiddenDangerData.top3Types = hidden_danger_top.records
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
console.error('获取隐患排查治理TOP3类型数据失败:', error)
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取隐患排查治理TOP3类型数据失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理风险统计tab切换
|
|
|
|
|
|
const handleRiskTabChange = async (tab: TabType) => {
|
|
|
|
|
|
console.log('Tab changed to:', tab)
|
|
|
|
|
|
try {
|
|
|
|
|
|
let code = ''
|
|
|
|
|
|
switch (tab) {
|
|
|
|
|
|
case '高危作业':
|
|
|
|
|
|
code = 'hazardous_operations'
|
|
|
|
|
|
break
|
|
|
|
|
|
case '安全培训考试':
|
|
|
|
|
|
code = 'safety_assessment'
|
|
|
|
|
|
break
|
|
|
|
|
|
case '安全培训考试':
|
|
|
|
|
|
code = 'security_training'
|
|
|
|
|
|
break
|
|
|
|
|
|
default:
|
|
|
|
|
|
code = 'hazardous_operations'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (code == 'hazardous_operations') {
|
|
|
|
|
|
// 根据不同的tab请求不同的接口
|
|
|
|
|
|
getTableList(
|
|
|
|
|
|
code,
|
|
|
|
|
|
{...query, activeTab: tab}
|
|
|
|
|
|
).then(response => {
|
|
|
|
|
|
// 更新风险统计数据 - 传递完整的数组数据用于饼图显示
|
|
|
|
|
|
if (response.records && response.records.length > 0) {
|
|
|
|
|
|
dangerDetail.value = response.records
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
console.error('获取风险统计数据失败:', error)
|
|
|
|
|
|
if (isFirstLoad.value) {
|
|
|
|
|
|
// dangerDetail.value = [
|
|
|
|
|
|
// {
|
|
|
|
|
|
// 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'
|
|
|
|
|
|
// }
|
|
|
|
|
|
// ]
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 根据不同的tab请求不同的接口
|
|
|
|
|
|
getTableList(
|
|
|
|
|
|
code,
|
|
|
|
|
|
{...query, activeTab: tab}
|
|
|
|
|
|
).then(response => {
|
|
|
|
|
|
// 更新风险统计数据 - 传递完整的数组数据用于饼图显示
|
|
|
|
|
|
if (response.records && response.records.length > 0) {
|
|
|
|
|
|
riskStatistics.value = response.records
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 如果没有数据,设置默认值
|
|
|
|
|
|
riskStatistics.value = [
|
|
|
|
|
|
{
|
|
|
|
|
|
csmpus_name: "雄安新区总部",
|
|
|
|
|
|
finishCount: "0",
|
|
|
|
|
|
participateCount: "0"
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch(error => {
|
|
|
|
|
|
console.error('获取风险统计数据失败:', error)
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取风险统计数据失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 方法定义
|
|
|
|
|
|
const onRegionChange = (item: RegionItem): void => {
|
|
|
|
|
|
selectedRegion.value = item.name
|
|
|
|
|
|
router.push({
|
|
|
|
|
|
path: '/screen/region',
|
|
|
|
|
|
query: {region: item.name, regionCode: item.code}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 打开区域选择器
|
|
|
|
|
|
const openRegionSelector = (): void => {
|
|
|
|
|
|
regionSelectorVisible.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新时间
|
|
|
|
|
|
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}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 定时器
|
|
|
|
|
|
const timeOut1 = (): void => {
|
|
|
|
|
|
sourceIndexTimerId.value = setInterval(() => {
|
|
|
|
|
|
if (sourceIndex.value === 2) {
|
|
|
|
|
|
sourceIndex.value--
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sourceIndex.value++
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 3000)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
</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 {
|
|
|
|
|
|
.header-container {
|
|
|
|
|
|
.header-title {
|
|
|
|
|
|
font-size: 1.1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header-left .back-button {
|
|
|
|
|
|
min-width: 8vw;
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content-container {
|
|
|
|
|
|
column-gap: 3vw;
|
|
|
|
|
|
padding: 0 8px 15px;
|
|
|
|
|
|
|
|
|
|
|
|
.left-wrapper {
|
|
|
|
|
|
.left-top .top-card {
|
|
|
|
|
|
.top-card-left {
|
|
|
|
|
|
min-width: 18vw;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.top-card-right {
|
|
|
|
|
|
min-width: 25vw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.left-bottom .donut-chart-with-labels {
|
|
|
|
|
|
width: 35vw;
|
|
|
|
|
|
height: 35vh;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.center-container {
|
|
|
|
|
|
width: 55vh;
|
|
|
|
|
|
height: 55vh;
|
|
|
|
|
|
|
|
|
|
|
|
.center-content {
|
|
|
|
|
|
.title {
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sub-title {
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.type-wrapper {
|
|
|
|
|
|
width: 85%;
|
|
|
|
|
|
|
|
|
|
|
|
.type-item .type-btn {
|
|
|
|
|
|
padding: 2px 25px;
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (width <=768px) {
|
|
|
|
|
|
.dashboard-container {
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
|
|
|
|
|
|
.header-container {
|
|
|
|
|
|
height: 80px;
|
|
|
|
|
|
|
|
|
|
|
|
.header-left {
|
|
|
|
|
|
line-height: 70px;
|
|
|
|
|
|
|
|
|
|
|
|
.back-button {
|
|
|
|
|
|
min-width: 12vw;
|
|
|
|
|
|
padding: 3px 12px;
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header-title {
|
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
|
line-height: 35px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.weather-warning {
|
|
|
|
|
|
margin-left: 5%;
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
line-height: 35px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content-container {
|
|
|
|
|
|
column-gap: 2vw;
|
|
|
|
|
|
padding: 0 5px 10px;
|
|
|
|
|
|
|
|
|
|
|
|
.panel-title {
|
|
|
|
|
|
margin: 3px 15px 0;
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.left-wrapper {
|
|
|
|
|
|
.left-top .top-card {
|
|
|
|
|
|
padding: 0 15px;
|
|
|
|
|
|
column-gap: 10px;
|
|
|
|
|
|
|
|
|
|
|
|
.top-card-left {
|
|
|
|
|
|
height: 10vh;
|
|
|
|
|
|
min-width: 20vw;
|
|
|
|
|
|
|
|
|
|
|
|
.total-number {
|
|
|
|
|
|
width: 22px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
line-height: 40px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.top-card-right {
|
|
|
|
|
|
height: 10vh;
|
|
|
|
|
|
min-width: 30vw;
|
|
|
|
|
|
|
|
|
|
|
|
.top-card-right-item {
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
|
|
|
|
|
|
.type-number-wrapper .type-number {
|
|
|
|
|
|
width: 12px;
|
|
|
|
|
|
height: 20px;
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
line-height: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.left-bottom {
|
|
|
|
|
|
.tabs .tab {
|
|
|
|
|
|
padding: 1px 8px;
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.donut-chart-with-labels {
|
|
|
|
|
|
width: 40vw;
|
|
|
|
|
|
height: 40vh;
|
|
|
|
|
|
margin-left: 1vw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.right-wrapper {
|
|
|
|
|
|
|
|
|
|
|
|
.right-top,
|
|
|
|
|
|
.right-bottom {
|
|
|
|
|
|
.tip-container {
|
|
|
|
|
|
width: 80%;
|
|
|
|
|
|
height: 70px;
|
|
|
|
|
|
|
|
|
|
|
|
.tip-image img {
|
|
|
|
|
|
width: 60px;
|
|
|
|
|
|
height: 60px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tip-content .col-item {
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.list-content {
|
|
|
|
|
|
width: 75%;
|
|
|
|
|
|
|
|
|
|
|
|
.list .list-item {
|
|
|
|
|
|
padding: 0.4vh 0.3vw;
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.center-container {
|
|
|
|
|
|
top: 60%;
|
|
|
|
|
|
width: 50vh;
|
|
|
|
|
|
height: 50vh;
|
|
|
|
|
|
|
|
|
|
|
|
.center-content {
|
|
|
|
|
|
.title {
|
|
|
|
|
|
margin-top: 1.5vh;
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sub-title {
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.type-wrapper {
|
|
|
|
|
|
width: 90%;
|
|
|
|
|
|
margin-top: 0.3vh;
|
|
|
|
|
|
|
|
|
|
|
|
.type-item {
|
|
|
|
|
|
.type-btn {
|
|
|
|
|
|
padding: 2px 20px;
|
|
|
|
|
|
font-size: 0.6rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.type-num {
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.clasic-wrapper {
|
|
|
|
|
|
width: 90%;
|
|
|
|
|
|
margin-top: 1vh;
|
|
|
|
|
|
|
|
|
|
|
|
.clasic-item {
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.echart-wrapper {
|
|
|
|
|
|
width: 90%;
|
|
|
|
|
|
|
|
|
|
|
|
.lf-rt .progress-legend .legend-item {
|
|
|
|
|
|
font-size: 0.6rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.safe-wrapper {
|
|
|
|
|
|
width: 50%;
|
|
|
|
|
|
|
|
|
|
|
|
.safe-title {
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pending-count {
|
|
|
|
|
|
font-size: 1.4rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (width <=480px) {
|
|
|
|
|
|
.dashboard-container {
|
|
|
|
|
|
.header-container {
|
|
|
|
|
|
height: 70px;
|
|
|
|
|
|
|
|
|
|
|
|
.header-left {
|
|
|
|
|
|
line-height: 60px;
|
|
|
|
|
|
|
|
|
|
|
|
.back-button {
|
|
|
|
|
|
min-width: 15vw;
|
|
|
|
|
|
padding: 2px 10px;
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header-title {
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
line-height: 30px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.weather-warning {
|
|
|
|
|
|
margin-left: 3%;
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
line-height: 30px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content-container {
|
|
|
|
|
|
column-gap: 1vw;
|
|
|
|
|
|
padding: 0 3px 8px;
|
|
|
|
|
|
|
|
|
|
|
|
.left-wrapper {
|
|
|
|
|
|
.left-top .top-card {
|
|
|
|
|
|
padding: 0 10px;
|
|
|
|
|
|
column-gap: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
.top-card-left {
|
|
|
|
|
|
height: 8vh;
|
|
|
|
|
|
min-width: 25vw;
|
|
|
|
|
|
|
|
|
|
|
|
.total-number {
|
|
|
|
|
|
width: 18px;
|
|
|
|
|
|
height: 35px;
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
line-height: 35px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.top-card-right {
|
|
|
|
|
|
height: 8vh;
|
|
|
|
|
|
min-width: 35vw;
|
|
|
|
|
|
|
|
|
|
|
|
.top-card-right-item {
|
|
|
|
|
|
font-size: 0.6rem;
|
|
|
|
|
|
|
|
|
|
|
|
.type-number-wrapper .type-number {
|
|
|
|
|
|
width: 10px;
|
|
|
|
|
|
height: 18px;
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
line-height: 18px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.left-bottom .donut-chart-with-labels {
|
|
|
|
|
|
width: 45vw;
|
|
|
|
|
|
height: 45vh;
|
|
|
|
|
|
margin-left: 0.5vw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.center-container {
|
|
|
|
|
|
top: 65%;
|
|
|
|
|
|
width: 45vh;
|
|
|
|
|
|
height: 45vh;
|
|
|
|
|
|
|
|
|
|
|
|
.center-content {
|
|
|
|
|
|
.title {
|
|
|
|
|
|
margin-top: 1vh;
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sub-title {
|
|
|
|
|
|
font-size: 0.6rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.type-wrapper {
|
|
|
|
|
|
width: 95%;
|
|
|
|
|
|
|
|
|
|
|
|
.type-item .type-btn {
|
|
|
|
|
|
padding: 1px 15px;
|
|
|
|
|
|
font-size: 0.55rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.echart-wrapper {
|
|
|
|
|
|
width: 95%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.safe-wrapper {
|
|
|
|
|
|
width: 60%;
|
|
|
|
|
|
|
|
|
|
|
|
.safe-title {
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pending-count {
|
|
|
|
|
|
font-size: 1.2rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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: end;
|
|
|
|
|
|
column-gap: 2vw;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header-title {
|
|
|
|
|
|
font-size: 1.3rem;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
line-height: 40px;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.weather-warning {
|
|
|
|
|
|
margin-left: 10%;
|
|
|
|
|
|
line-height: 45px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
|
|
|
|
|
|
.bottom-card-title {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
margin-top: 5px;
|
|
|
|
|
|
margin-left: -15%;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bar-chart {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
width: 80%;
|
|
|
|
|
|
min-height: 17.5vh;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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 {
|
|
|
|
|
|
@keyframes numberGlow {
|
|
|
|
|
|
0% {
|
|
|
|
|
|
box-shadow: 0 0 5px rgb(177 74 201 / 50%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
100% {
|
|
|
|
|
|
box-shadow: 0 0 20px rgb(177 74 201 / 90%);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes flashEffect {
|
|
|
|
|
|
0% {
|
|
|
|
|
|
background-color: rgb(177 74 201 / 100%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
50% {
|
|
|
|
|
|
background-color: rgb(255 255 255 / 100%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
100% {
|
|
|
|
|
|
background-color: rgb(177 74 201 / 100%);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
animation: numberGlow 2s ease-in-out infinite alternate;
|
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.total-number:hover {
|
|
|
|
|
|
transform: scale(1.1);
|
|
|
|
|
|
box-shadow: 0 0 15px rgb(177 74 201 / 80%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.flash {
|
|
|
|
|
|
animation: flashEffect 0.3s ease-in-out;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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 {
|
|
|
|
|
|
@keyframes typeNumberPulse {
|
|
|
|
|
|
0% {
|
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
50% {
|
|
|
|
|
|
transform: scale(1.05);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
100% {
|
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes typeFlashEffect {
|
|
|
|
|
|
0% {
|
2025-12-12 11:01:19 +08:00
|
|
|
|
background-color: #158e56;
|
2025-10-17 10:31:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
50% {
|
|
|
|
|
|
background-color: rgb(255 255 255 / 100%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
100% {
|
2025-12-12 11:01:19 +08:00
|
|
|
|
background-color: #158e56;
|
2025-10-17 10:31:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2025-12-12 11:01:19 +08:00
|
|
|
|
background-color: #158e56;
|
2025-10-17 10:31:13 +08:00
|
|
|
|
border-radius: 2px;
|
|
|
|
|
|
animation: typeNumberPulse 1.5s ease-in-out infinite;
|
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.type-number:hover {
|
2025-12-12 11:01:19 +08:00
|
|
|
|
background-color: #158e56;
|
2025-10-17 10:31:13 +08:00
|
|
|
|
transform: scale(1.2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.flash {
|
|
|
|
|
|
animation: typeFlashEffect 0.3s ease-in-out;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
margin-left: 2vw;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.right-wrapper {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
row-gap: 1rem;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
|
|
|
|
|
|
.center-content {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
// background: rgba(255, 22, 1, 0.3);
|
|
|
|
|
|
width: 77%;
|
|
|
|
|
|
height: 77%;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
|
|
|
|
.title {
|
|
|
|
|
|
margin-top: 2vh;
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bottom-border-line {
|
|
|
|
|
|
width: 20%;
|
|
|
|
|
|
margin: 1vh 0 1.2vh;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sub-title {
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.type-wrapper {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-around;
|
|
|
|
|
|
width: 80%;
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
|
|
|
|
|
|
.type-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-top: 0.5vh;
|
|
|
|
|
|
|
|
|
|
|
|
.type-btn {
|
|
|
|
|
|
padding: 2px 30px;
|
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
background-color: #d97706;
|
|
|
|
|
|
border-radius: 15px;
|
|
|
|
|
|
|
|
|
|
|
|
&.active {
|
|
|
|
|
|
background-color: #059669;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.type-num {
|
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.clasic-wrapper {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
width: 80%;
|
|
|
|
|
|
margin-top: 1.2vh;
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
justify-content: space-around;
|
|
|
|
|
|
|
|
|
|
|
|
.clasic-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
width: 45%;
|
|
|
|
|
|
margin-top: 0.6vh;
|
|
|
|
|
|
margin-bottom: -1vh;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
|
|
|
|
img {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.echart-wrapper {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
width: 85%;
|
|
|
|
|
|
height: 75%;
|
|
|
|
|
|
|
|
|
|
|
|
.lf-rt {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
width: 50%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
|
|
|
|
.progress-chart {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 80%;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.progress-legend {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
|
|
|
|
|
|
.legend-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.safe-wrapper {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
width: 40%;
|
|
|
|
|
|
height: 20%;
|
|
|
|
|
|
margin-bottom: 5%;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
column-gap: 1vw;
|
|
|
|
|
|
|
|
|
|
|
|
.safe-title {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-left: 1vw;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pending-count {
|
|
|
|
|
|
margin-bottom: 0.7vh;
|
|
|
|
|
|
font-size: 1.6rem;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: yellow;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 全局样式 */
|
|
|
|
|
|
</style>
|