bug修复

This commit is contained in:
chenlin
2025-12-13 18:14:32 +08:00
parent 8f4bf593dc
commit 3e9ac2b357
6 changed files with 210 additions and 134 deletions

View File

@@ -77,11 +77,11 @@
<div class="type-wrapper"> <div class="type-wrapper">
<div class="type-item"> <div class="type-item">
<span class="type-btn yellow">重大</span> <span class="type-btn yellow">重大</span>
<span class="type-num">{{ mockData.hiddenDangerData.severityCount }}</span> <span class="type-num cursor-pointer" @click="handleSeverityCountClick">{{ mockData.hiddenDangerData.severityCount }}</span>
</div> </div>
<div class="type-item"> <div class="type-item">
<span class="type-btn green">一般</span> <span class="type-btn green">一般</span>
<span class="type-num">{{ mockData.hiddenDangerData.generalCount }}</span> <span class="type-num cursor-pointer" @click="handleGeneralCountClick">{{ mockData.hiddenDangerData.generalCount }}</span>
</div> </div>
<!-- <div class="type-item"> <!-- <div class="type-item">
<span class="type-btn blue">较大</span> <span class="type-btn blue">较大</span>
@@ -96,7 +96,7 @@
<div class="legend-item"><span class="dot green"></span>已处理</div> <div class="legend-item"><span class="dot green"></span>已处理</div>
</div> </div>
<div class="progress-legend"> <div class="progress-legend">
<!-- <div class="legend-item"><span class="dot yellow"></span>待排查</div>--> <!-- <div class="legend-item"><span class="dot yellow"></span>待排查</div>-->
<div class="legend-item"><span class="dot blue"></span>处理中</div> <div class="legend-item"><span class="dot blue"></span>处理中</div>
</div> </div>
</div> </div>
@@ -108,11 +108,11 @@
<div class="tabs"> <div class="tabs">
<span class="tab" :class="{ active: activeTab === '高危作业' }" @click="handleTabClick('高危作业')">高危作业</span> <span class="tab" :class="{ active: activeTab === '高危作业' }" @click="handleTabClick('高危作业')">高危作业</span>
<span class="divider">|</span> <span class="divider">|</span>
<span <span class="tab" :class="{ active: activeTab === '安全培训考试' }"
class="tab" :class="{ active: activeTab === '安全培训考试' }"
@click="handleTabClick('安全培训考试')">安全培训考试</span> @click="handleTabClick('安全培训考试')">安全培训考试</span>
<span class="divider">|</span> <span class="divider">|</span>
<span class="tab" :class="{ active: activeTab === '应急预案及演练' }" @click="handleTabClick('应急预案及演练')">应急预案及演练</span> <span class="tab" :class="{ active: activeTab === '应急预案及演练' }"
@click="handleTabClick('应急预案及演练')">应急预案及演练</span>
</div> </div>
</div> </div>
<img style="margin: 8px 0" src="@/assets/images/title_border_line.png" /> <img style="margin: 8px 0" src="@/assets/images/title_border_line.png" />
@@ -128,17 +128,17 @@ class="tab" :class="{ active: activeTab === '安全培训考试' }"
<div class="row-item"> <div class="row-item">
<img src="@/assets/images/screen/unfinished.png" width="20" /> <img src="@/assets/images/screen/unfinished.png" width="20" />
<span class="row-item-title">未开始数量</span> <span class="row-item-title">未开始数量</span>
<span class="row-item-number">{{unfinishedCount}}</span> <span class="row-item-number">{{ unfinishedCount }}</span>
</div> </div>
<div class="row-item"> <div class="row-item">
<img src="@/assets/images/screen/inProcess.png" width="20" /> <img src="@/assets/images/screen/inProcess.png" width="20" />
<span class="row-item-title">进行中数量</span> <span class="row-item-title">进行中数量</span>
<span class="row-item-number">{{inProgressCount}}</span> <span class="row-item-number">{{ inProgressCount }}</span>
</div> </div>
<div class="row-item"> <div class="row-item">
<img src="@/assets/images/screen/finished.png" width="20" /> <img src="@/assets/images/screen/finished.png" width="20" />
<span class="row-item-title">已完成数量</span> <span class="row-item-title">已完成数量</span>
<span class="row-item-number">{{finishedCount}}</span> <span class="row-item-number">{{ finishedCount }}</span>
</div> </div>
</div> </div>
<div style="display: flex; width: 100%;"> <div style="display: flex; width: 100%;">
@@ -161,7 +161,8 @@ class="tab" :class="{ active: activeTab === '安全培训考试' }"
<img width="50%" style="margin: 8px 0" src="@/assets/images/line_1.png" /> <img width="50%" style="margin: 8px 0" src="@/assets/images/line_1.png" />
</div> </div>
</div> </div>
<AlertList maxHeight="40vh" style="margin-left: 1vw;" :table-title="trainingTableTitle" :list-data="drillList" /> <AlertList maxHeight="40vh" style="margin-left: 1vw;" :table-title="trainingTableTitle"
:list-data="drillList" />
</template> </template>
</div> </div>
</div> </div>
@@ -192,11 +193,13 @@ class="tab" :class="{ active: activeTab === '安全培训考试' }"
</div> </div>
<div class="col-item" style=" display: flex;margin-left: 2vw; align-items: center;"> <div class="col-item" style=" display: flex;margin-left: 2vw; align-items: center;">
<span>待处理</span> <span>待处理</span>
<span style="font-size: 1.2rem; marker-start: 2vw; color: yellow;">{{ mockData.alertData.pending }}</span> <span style="font-size: 1.2rem; marker-start: 2vw; color: yellow;">{{ mockData.alertData.pending
}}</span>
</div> </div>
<div class="col-item" style=" display: flex;margin-left: 2vw; align-items: center;"> <div class="col-item" style=" display: flex;margin-left: 2vw; align-items: center;">
<span>处理中</span> <span>处理中</span>
<span style="font-size: 1.2rem; marker-start: 2vw; color: yellow;">{{ mockData.alertData.processing }}</span> <span style="font-size: 1.2rem; marker-start: 2vw; color: yellow;">{{ mockData.alertData.processing
}}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -218,7 +221,8 @@ class="tab" :class="{ active: activeTab === '安全培训考试' }"
<div class="col-item"> <div class="col-item">
<img src="@/assets/images/screen/warning_img.png" width="23" /> <img src="@/assets/images/screen/warning_img.png" width="23" />
<span>超时工单数</span> <span>超时工单数</span>
<span style="font-size: 1.2rem; marker-start: 2vw; color: red;">{{ mockData.timeoutWorkOrders.total }}</span> <span style="font-size: 1.2rem; marker-start: 2vw; color: red;">{{ mockData.timeoutWorkOrders.total
}}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -229,14 +233,12 @@ class="tab" :class="{ active: activeTab === '安全培训考试' }"
</div> </div>
<div class="center-container"> <div class="center-container">
<!-- 中部区域 --> <!-- 中部区域 -->
<ParkCenter <ParkCenter :parkName="selectedPark" :backgroundImage="backgroundImage" @point-hover="handlePointHover"
:parkName="selectedPark" :backgroundImage="backgroundImage" @point-hover="handlePointHover"
@point-leave="handlePointLeave" /> @point-leave="handlePointLeave" />
</div> </div>
<!-- 点位信息弹窗 --> <!-- 点位信息弹窗 -->
<PointInfoPopup <PointInfoPopup v-if="showPointPopup" :visible="showPointPopup" :point-info="currentPoint"
v-if="showPointPopup" :visible="showPointPopup" :point-info="currentPoint"
:park-name="selectedPark || '雄安园区'" /> :park-name="selectedPark || '雄安园区'" />
</div> </div>
</div> </div>
@@ -283,8 +285,14 @@ const unfinishedCount = ref<number>(0)
const inProgressCount = ref<number>(0) const inProgressCount = ref<number>(0)
const finishedCount = ref<number>(0) const finishedCount = ref<number>(0)
const handleSeverityCountClick = () => {
window.open('http://10.0.64.20/configcenter/console/device-manage', '_blank')
}
const handleGeneralCountClick = () => {
window.open('http://10.0.64.20/configcenter/console/device-manage', '_blank')
}
const tableTitle = [ const tableTitle = [
{ {
name: '培训(考试)名称', name: '培训(考试)名称',
key: 'examname' key: 'examname'
@@ -301,9 +309,9 @@ const finishedCount = ref<number>(0)
name: '考试通过率(%)', name: '考试通过率(%)',
key: 'exampassrate', key: 'exampassrate',
} }
] ]
const trainingTableTitle = [ const trainingTableTitle = [
{ {
name: '演练名称', name: '演练名称',
key: 'drill_plan_name' key: 'drill_plan_name'
@@ -316,11 +324,11 @@ const finishedCount = ref<number>(0)
name: '参与人数', name: '参与人数',
key: 'drill_count' key: 'drill_count'
} }
] ]
const examList = ref<any[]>([]) const examList = ref<any[]>([])
const drillList = ref<any[]>([]) const drillList = ref<any[]>([])
const query = reactive({ const query = reactive({
@@ -543,12 +551,14 @@ const initProgressChart = () => {
labelLayout: function (params: any) { labelLayout: function (params: any) {
const isLeft = params.labelRect.x < width / 2; const isLeft = params.labelRect.x < width / 2;
const points = params.labelLinePoints; const points = params.labelLinePoints;
if (points && points.length && points[2]) {
points[2][0] = isLeft points[2][0] = isLeft
? params.labelRect.x ? params.labelRect.x
: params.labelRect.x + params.labelRect.width; : params.labelRect.x + params.labelRect.width;
return { return {
labelLinePoints: points labelLinePoints: points
}; };
}
}, },
} }
] ]
@@ -844,12 +854,14 @@ const handleHiddenDangerPannelData = (query) => {
labelLayout: function (params: any) { labelLayout: function (params: any) {
const isLeft = params.labelRect.x < width / 2; const isLeft = params.labelRect.x < width / 2;
const points = params.labelLinePoints; const points = params.labelLinePoints;
if (points && points.length && points[2]) {
points[2][0] = isLeft points[2][0] = isLeft
? params.labelRect.x ? params.labelRect.x
: params.labelRect.x + params.labelRect.width; : params.labelRect.x + params.labelRect.width;
return { return {
labelLinePoints: points labelLinePoints: points
}; };
}
}, },
} }
] ]
@@ -1002,7 +1014,7 @@ const handleTabClick = async (tab: string) => {
code1 = "fire_drill_detail" code1 = "fire_drill_detail"
getDangerDetail(query.campus_id).then(res => { getDangerDetail(query.campus_id).then(res => {
const list = res.records.map((item: any) => ({description: item.contenttext})) const list = res.records.map((item: any) => ({ description: item.contenttext }))
dangerList.value = list dangerList.value = list
}) })
@@ -1186,6 +1198,9 @@ onUnmounted(() => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.cursor-pointer {
cursor: pointer;
}
/* 响应式设计 - 自动适应不同屏幕尺寸 */ /* 响应式设计 - 自动适应不同屏幕尺寸 */
@media (width <=1200px) { @media (width <=1200px) {
.dashboard-container { .dashboard-container {

View File

@@ -15,7 +15,7 @@
<div class="list-wrapper" ref="listWrapperRef" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave"> <div class="list-wrapper" ref="listWrapperRef" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
<!-- 表格模式 --> <!-- 表格模式 -->
<template v-if="tableTitle && tableTitle.length > 0"> <template v-if="tableTitle && tableTitle.length > 0">
<div class="table-row" v-for="(item, index) in listData" :key="`table-${index}`" @mouseenter="handleMouseEnter"> <div @click="handleItemClick(item)" class="table-row cursor-pointer" v-for="(item, index) in listData" :key="`table-${index}`" @mouseenter="handleMouseEnter">
<div class="table-cell" v-for="(title, cellIndex) in tableTitle" :key="`cell-${index}-${cellIndex}`"> <div class="table-cell" v-for="(title, cellIndex) in tableTitle" :key="`cell-${index}-${cellIndex}`">
{{ item[title.key] || '-' }} {{ item[title.key] || '-' }}
</div> </div>
@@ -24,7 +24,7 @@
<!-- 列表模式 --> <!-- 列表模式 -->
<template v-else> <template v-else>
<div class="list-item" v-for="(item, index) in listData" :key="`list-${index}`" @mouseenter="handleMouseEnter"> <div @click="handleItemClick(item)" class="list-item cursor-pointer" v-for="(item, index) in listData" :key="`list-${index}`" @mouseenter="handleMouseEnter">
<span class="alert-text" :class="[{ error: item.alarm_level_code == 'severity' }, { warn: item.alarm_level_code == 'major' }]"> <span class="alert-text" :class="[{ error: item.alarm_level_code == 'severity' }, { warn: item.alarm_level_code == 'major' }]">
{{ (index + 1) }} {{ item.description }} {{ (index + 1) }} {{ item.description }}
</span> </span>
@@ -71,9 +71,14 @@ const props = withDefaults(defineProps<Props>(), {
const listWrapperRef = ref<HTMLElement | null>(null) const listWrapperRef = ref<HTMLElement | null>(null)
let scrollTimer: NodeJS.Timeout | null = null let scrollTimer: NodeJS.Timeout | null = null
let isScrolling = false let isScrolling = false
let scrollDirection: 'down' | 'up' = 'down' // 滚动方向:向下或向上
const handleItemClick = (item: AlertItem) => {
window.open(`http://10.0.64.20/configcenter/console/device-manage`, '_blank')
}
// 自动滚动功能 // 自动滚动功能
const startAutoScroll = () => { const startAutoScroll = (resetToTop: boolean = false) => {
if (!props.autoScroll || !listWrapperRef.value) return if (!props.autoScroll || !listWrapperRef.value) return
const wrapper = listWrapperRef.value const wrapper = listWrapperRef.value
@@ -83,20 +88,61 @@ const startAutoScroll = () => {
// 只有当内容高度超过容器高度时才启动滚动 // 只有当内容高度超过容器高度时才启动滚动
if (scrollHeight <= clientHeight) return if (scrollHeight <= clientHeight) return
// 如果是首次启动或需要重置,从顶部开始向下滚动
if (resetToTop) {
wrapper.scrollTop = 0
scrollDirection = 'down'
} else {
// 否则根据当前位置判断滚动方向
const currentScrollTop = wrapper.scrollTop
const maxScrollTop = scrollHeight - clientHeight
if (currentScrollTop >= maxScrollTop - 1) {
// 在底部,向上滚动
scrollDirection = 'up'
} else if (currentScrollTop <= 1) {
// 在顶部,向下滚动
scrollDirection = 'down'
}
// 在中间位置,保持当前方向(或默认向下)
}
isScrolling = true isScrolling = true
let currentScrollTop = listWrapperRef.value.scrollTop
const scroll = () => { const scroll = () => {
if (!isScrolling || !wrapper) return if (!isScrolling || !wrapper) return
currentScrollTop += props.scrollSpeed const currentScrollTop = wrapper.scrollTop
const maxScrollTop = scrollHeight - clientHeight
// 如果滚动到底部,重置到顶部 if (scrollDirection === 'down') {
if (currentScrollTop >= scrollHeight - clientHeight) { // 向下滚动
currentScrollTop = 0 const nextScrollTop = currentScrollTop + props.scrollSpeed
wrapper.scrollTop = 0
if (nextScrollTop >= maxScrollTop) {
// 滚动到底部,切换方向为向上
wrapper.scrollTop = maxScrollTop
scrollDirection = 'up'
// 在底部停留一小段时间后开始向上滚动
scrollTimer = setTimeout(scroll, 500)
return
} else { } else {
wrapper.scrollTop = currentScrollTop wrapper.scrollTop = nextScrollTop
}
} else {
// 向上滚动
const nextScrollTop = currentScrollTop - props.scrollSpeed
if (nextScrollTop <= 0) {
// 滚动到顶部,切换方向为向下
wrapper.scrollTop = 0
scrollDirection = 'down'
// 在顶部停留一小段时间后开始向下滚动
scrollTimer = setTimeout(scroll, 500)
return
} else {
wrapper.scrollTop = nextScrollTop
}
} }
scrollTimer = setTimeout(scroll, 50) // 每50ms滚动一次实现平滑效果 scrollTimer = setTimeout(scroll, 50) // 每50ms滚动一次实现平滑效果
@@ -121,8 +167,9 @@ const handleMouseEnter = () => {
const handleMouseLeave = () => { const handleMouseLeave = () => {
if (props.autoScroll) { if (props.autoScroll) {
// 延迟启动滚动,避免鼠标快速进出 // 延迟启动滚动,避免鼠标快速进出
// 从当前位置继续,不重置
setTimeout(() => { setTimeout(() => {
startAutoScroll() startAutoScroll(false)
}, 500) }, 500)
} }
} }
@@ -133,7 +180,8 @@ watch(() => props.listData, () => {
if (props.autoScroll) { if (props.autoScroll) {
stopAutoScroll() stopAutoScroll()
setTimeout(() => { setTimeout(() => {
startAutoScroll() // 数据更新后从顶部重新开始
startAutoScroll(true)
}, 1000) // 数据更新后1秒开始滚动 }, 1000) // 数据更新后1秒开始滚动
} }
}) })
@@ -141,9 +189,9 @@ watch(() => props.listData, () => {
onMounted(() => { onMounted(() => {
if (props.autoScroll) { if (props.autoScroll) {
// 组件挂载后延迟启动滚动 // 组件挂载后延迟启动滚动,从顶部开始
setTimeout(() => { setTimeout(() => {
startAutoScroll() startAutoScroll(true)
}, 2000) }, 2000)
} }
}) })
@@ -154,6 +202,9 @@ onUnmounted(() => {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.cursor-pointer {
cursor: pointer;
}
.list-content { .list-content {
display: flex; display: flex;
width: 68%; width: 68%;

View File

@@ -193,6 +193,15 @@ watch(() => props.hiddenDangerData?.progress, (newVal) => {
refreshProcessCharts(newVal) refreshProcessCharts(newVal)
}, { deep: true }) }, { deep: true })
// 辅助函数:安全地将值转换为数字,处理 NaN 和字符串 "NaN" 的情况
const safeNumber = (val: any): number => {
if (val === null || val === undefined || val === '' || val === 'NaN') {
return 0
}
const num = Number(val)
return isNaN(num) ? 0 : num
}
// 更新图表数据 // 更新图表数据
const refreshProcessCharts = (process): void => { const refreshProcessCharts = (process): void => {
if (!props.hiddenDangerData?.progress) { if (!props.hiddenDangerData?.progress) {
@@ -200,11 +209,12 @@ const refreshProcessCharts = (process): void => {
return return
} }
const option = { ...progressChartOption.value } const option = { ...progressChartOption.value }
// 确保所有 value 都是有效的数字,将字符串 "NaN" 或真正的 NaN 转换为 0
option.series[0].data = [ option.series[0].data = [
{ value: process.overdue || 0, name: '已逾期', itemStyle: { color: '#ef4444' } }, { value: safeNumber(process.overdue), name: '已逾期', itemStyle: { color: '#ef4444' } },
{ value: process.processed || 0, name: '已处理', itemStyle: { color: '#10b981' } }, { value: safeNumber(process.processed), name: '已处理', itemStyle: { color: '#10b981' } },
// { value: process.pending || 0, name: '待排查', itemStyle: { color: '#eab308' } }, // { value: safeNumber(process.pending), name: '待排查', itemStyle: { color: '#eab308' } },
{ value: process.processing || 0, name: '处理中', itemStyle: { color: '#3b82f6' } } { value: safeNumber(process.processing), name: '处理中', itemStyle: { color: '#3b82f6' } }
] ]
progressChartOption.value = option progressChartOption.value = option
} }

View File

@@ -3,7 +3,7 @@
<!-- 顶部标题栏 --> <!-- 顶部标题栏 -->
<div class="header-container"> <div class="header-container">
<div class="header-left"> <div class="header-left">
<div class="back-button" @click="openRegionSelector"> 总部 <div class="back-button" @click="openRegionSelector"> 集团
<span>···</span> <span>···</span>
</div> </div>
</div> </div>

View File

@@ -374,9 +374,9 @@ const loadDashboardData = async (): Promise<void> => {
try { try {
// 获取风险预警详情数据 // 获取风险预警详情数据
getTableList('risk_alert_detail', query).then(risk_alert_detail => { getTableList('risk_alert_detail', query).then(risk_alert_detail => {
if (risk_alert_detail.records && risk_alert_detail.records.length > 0) { // if (risk_alert_detail.records && risk_alert_detail.records.length > 0) {
dashboardData.value.alertData.details = risk_alert_detail.records dashboardData.value.alertData.details = risk_alert_detail.records || []
} // }
}).catch(error => { }).catch(error => {
console.error('获取风险预警详情数据失败:', error) console.error('获取风险预警详情数据失败:', error)
}) })

View File

@@ -32,7 +32,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
// 本地跨域代理. 目前注释的原因暂时没有用途server 端已经支持跨域 // 本地跨域代理. 目前注释的原因暂时没有用途server 端已经支持跨域
proxy: { proxy: {
['/admin-api']: { ['/admin-api']: {
target: 'http://10.28.117.48:48080', target: 'http://carson.wang:48080',
ws: false, ws: false,
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(new RegExp(`^/admin-api`), ''), rewrite: (path) => path.replace(new RegExp(`^/admin-api`), ''),