Files
lc_frontend/src/views/screen/companyScreen.vue
yang chen 20680032bc feat(screen): 隐藏隐患排查治理标题并调整子标题
- 在 companyScreen.vue 中隐藏“隐患排查治理”标题
- 在 HiddenDangerPanel.vue 中将“分类风险”更改为“隐患等级”
- 在 mainScreenV1.vue 中同步修改标题和子标题显示逻辑
- 在 parkScreen.vue 中隐藏“隐患排查治理”标题
2025-10-31 14:58:49 +08:00

1874 lines
58 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>