yanghao 12 часов назад
Родитель
Сommit
bd5c15740e
1 измененных файлов с 0 добавлено и 2692 удалено
  1. 0 2692
      src/views/Home/demo.vue

+ 0 - 2692
src/views/Home/demo.vue

@@ -1,2692 +0,0 @@
-<script lang="ts" setup>
-import * as echarts from 'echarts'
-import '@vuepic/vue-datepicker/dist/main.css'
-import {
-  AlarmClock,
-  Checked,
-  Clock,
-  CollectionTag,
-  DataAnalysis,
-  DocumentChecked,
-  Files,
-  Flag,
-  Histogram,
-  Odometer,
-  Postcard,
-  Warning,
-  Search,
-  Refresh
-} from '@element-plus/icons-vue'
-import {
-  kanbanApi,
-  IotDangerApi,
-  IotHiddenApi,
-  IotInstrumentApi,
-  IotMeasureCertApi,
-  IotCertificateApi
-} from '@/api/pms/qhse/index'
-import { useUserStore } from '@/store/modules/user'
-import {
-  ANIMATION,
-  CHART_RENDERER,
-  DESIGN_HEIGHT,
-  DESIGN_WIDTH,
-  FONT_FAMILY,
-  THEME,
-  createTooltip
-} from '@/utils/kb'
-import { DICT_TYPE, getBoolDictOptions, getStrDictOptions } from '@/utils/dict'
-import { formatDate } from '@/utils/formatTime'
-
-defineOptions({
-  name: 'PmsQhseKanban'
-})
-
-type PermitStat = {
-  label: string
-  value: number
-  color: string
-}
-
-type SafeDayMap = Record<string, number>
-type SafeDayBarItem = {
-  name: string
-  value: number
-}
-
-type SummaryTabValue = 'home' | 'certificate'
-type QhseMetricValue = 'ltir' | 'trir' | 'pmva'
-const userStore = useUserStore()
-
-const type = ref('day')
-
-const handelChange = (value: any) => {
-  console.log('Selected time:', value)
-  // 在这里处理时间选择变化的逻辑,例如更新看板数据
-}
-
-function padDateValue(value: number) {
-  return `${value}`.padStart(2, '0')
-}
-
-function formatDateValue(date = new Date()) {
-  return `${date.getFullYear()}-${padDateValue(date.getMonth() + 1)}-${padDateValue(date.getDate())}`
-}
-
-function getCurrentPickerValue(pickerType: string) {
-  const now = new Date()
-
-  if (pickerType === 'year') {
-    return `${now.getFullYear()}`
-  }
-
-  if (pickerType === 'month') {
-    return `${now.getFullYear()}-${padDateValue(now.getMonth() + 1)}`
-  }
-
-  return formatDateValue(now)
-}
-
-const timeVal = ref(getCurrentPickerValue(type.value))
-
-const wrapperRef = ref<HTMLDivElement>()
-const hazardChartRef = ref<HTMLDivElement>()
-const safeDayChartRef = ref<HTMLDivElement>()
-const qhseTrendChartRef = ref<HTMLDivElement>()
-const socChartRef = ref<HTMLDivElement>()
-const scale = ref(1)
-const supportsZoom = ref(false)
-
-let resizeObserver: ResizeObserver | null = null
-let resizeRaf = 0
-let hazardChart: echarts.ECharts | null = null
-let safeDayChart: echarts.ECharts | null = null
-let qhseTrendChart: echarts.ECharts | null = null
-let socChart: echarts.ECharts | null = null
-
-const summaryTabs: Array<{ label: string; value: SummaryTabValue }> = [
-  { label: '首页看板', value: 'home' },
-  { label: '证书信息', value: 'certificate' }
-]
-
-const activeSummaryTab = ref<SummaryTabValue>('home')
-
-watch(
-  () => activeSummaryTab.value,
-  async (value) => {
-    if (value === 'certificate') {
-      await getCertificateBoardList()
-    } else {
-      nextTick(() => {
-        initHazardChart()
-        initSafeDayChart()
-        initQhseTrendChart()
-        initSocChart()
-        resizeHazardChart()
-        resizeSafeDayChart()
-        resizeQhseTrendChart()
-        resizeSocChart()
-      })
-    }
-  }
-)
-
-const pageTitle = computed(() =>
-  activeSummaryTab.value === 'certificate' ? '证书信息看板' : 'QHSE管理看板'
-)
-
-const summaryCards = ref([
-  {
-    title: '人工时(小时)',
-    value: 671987,
-    note: '',
-    accent: '#4f8dff',
-    glow: 'rgba(79, 141, 255, 0.22)',
-    icon: Clock
-  },
-  {
-    title: '车辆里程(公里)',
-    value: 631128,
-    note: '',
-    accent: '#4f8dff',
-    glow: 'rgba(79, 141, 255, 0.22)',
-    icon: Odometer
-  },
-  {
-    title: '重大风险数量',
-    value: 0,
-    note: '',
-    accent: '#ff5a62',
-    glow: 'rgba(255, 90, 98, 0.26)',
-    icon: Warning
-  },
-  {
-    title: '特种人员持证率',
-    value: 0,
-    note: 'Warn: 0',
-    accent: '#f2b800',
-    glow: 'rgba(242, 184, 0, 0.22)',
-    icon: Postcard
-  },
-  {
-    title: '设备检测合规率',
-    value: '98%',
-    note: '',
-    accent: '#f2b800',
-    glow: 'rgba(242, 184, 0, 0.22)',
-    icon: DataAnalysis
-  },
-  {
-    title: '作业许可证数量',
-    value: 0,
-    note: '',
-    accent: '#4f8dff',
-    glow: 'rgba(79, 141, 255, 0.22)',
-    icon: DocumentChecked
-  }
-])
-
-const hazardBars = ref([
-  { label: '总数', value: 0, color: '#4f8dff' },
-  { label: '已整改', value: 0, color: '#43c7ca' },
-  { label: '未整改', value: 0, color: '#ff981f' }
-])
-
-const riskZones = ref([
-  { title: '', desc: '办公区 / 展厅 / 主通道', color: '#25b36a', value: 0 },
-  { title: '', desc: '物料库 / 维修 / 装卸区', color: '#3d7cff', value: 0 },
-  { title: '', desc: '焊接 / 机加 / 吊装区', color: '#ff9827', value: 0 },
-  { title: '', desc: '危化库 / 试压区 / 配电房', color: '#ff5b61', value: 0 }
-])
-
-const permitStats = ref<PermitStat[]>([
-  { label: '个人防护', value: 18, color: '#4f8dff' },
-  { label: '规范操作', value: 26, color: '#43c7ca' },
-  { label: '规范指挥', value: 12, color: '#ffb14a' },
-  { label: '人员位置', value: 9, color: '#ff7a7a' },
-  { label: '作业场所', value: 15, color: '#8d8cff' }
-])
-
-const qualificationWarnings = ref([
-  { label: '证件过期', value: 0, accent: '#ff7a7a', key: 'expired' },
-  { label: '即将到期', value: 0, accent: '#e6ab00', key: 'warn' }
-])
-
-const qhseMetricTabs = [
-  { label: 'LTIR(百万工时)', value: 'ltir', accent: '#3d7cff', label2: '百万工时损失工时事故率' },
-  { label: 'TRIR(百万工时)', value: 'trir', accent: '#17b6c5', label2: '百万工时总可记录事件率' },
-  {
-    label: 'PMVA(百万公里)',
-    value: 'pmva',
-    accent: '#f2a93b',
-    label2: '百万公里可预防性交通事故率'
-  }
-] as const
-
-const activeQhseMetric = ref<QhseMetricValue>('ltir')
-const dangerGradeOptions = computed(() => getStrDictOptions(DICT_TYPE.DANGER_GRADE))
-
-const qhseTrendSeries = ref<Record<QhseMetricValue, Array<{ year: string; value: number }>>>({
-  ltir: [
-    { year: '2026年1月', value: 0.42 },
-    { year: '2026年2月', value: 0.39 },
-    { year: '2026年3月', value: 0.36 },
-    { year: '2026年4月', value: 0.34 },
-    { year: '2026年5月', value: 0.31 },
-    { year: '2026年6月', value: 0.29 },
-    { year: '2026年7月', value: 0.27 },
-    { year: '2026年8月', value: 0.25 },
-    { year: '2026年9月', value: 0.24 },
-    { year: '2026年10月', value: 0.22 },
-    { year: '2026年11月', value: 0.21 },
-    { year: '2026年12月', value: 0.2 }
-  ],
-  trir: [
-    { year: '1月', value: 0.78 },
-    { year: '2月', value: 0.75 },
-    { year: '3月', value: 0.73 },
-    { year: '4月', value: 0.7 },
-    { year: '5月', value: 0.68 },
-    { year: '6月', value: 0.66 },
-    { year: '7月', value: 0.63 },
-    { year: '8月', value: 0.61 },
-    { year: '9月', value: 0.59 },
-    { year: '10月', value: 0.57 },
-    { year: '11月', value: 0.55 },
-    { year: '12月', value: 0.54 }
-  ],
-  pmva: [
-    { year: '1月', value: 0.22 },
-    { year: '2月', value: 0.21 },
-    { year: '3月', value: 0.2 },
-    { year: '4月', value: 0.18 },
-    { year: '5月', value: 0.17 },
-    { year: '6月', value: 0.16 },
-    { year: '7月', value: 0.15 },
-    { year: '8月', value: 0.14 },
-    { year: '9月', value: 0.14 },
-    { year: '10月', value: 0.13 },
-    { year: '11月', value: 0.12 },
-    { year: '12月', value: 0.12 }
-  ]
-})
-
-const bottomCards = ref([
-  {
-    title: '体系合规',
-    icon: Files,
-    accent: '#39c6cc',
-    glow: 'rgba(57, 198, 204, 0.22)',
-    lines: ['内审已完成', '外审待安排']
-  },
-  {
-    title: '安全检测',
-    icon: Histogram,
-    accent: '#4f8dff',
-    glow: 'rgba(79, 141, 255, 0.2)',
-    lines: ['在用: 0台', '待检: 0项(重点关注)']
-  },
-  {
-    title: '应急演练',
-    icon: AlarmClock,
-    accent: '#ff5b61',
-    glow: 'rgba(255, 91, 97, 0.22)',
-    lines: ['年度应急演练:80次']
-  },
-  {
-    title: '职业健康',
-    icon: Checked,
-    accent: '#f2c11a',
-    glow: 'rgba(242, 193, 26, 0.2)',
-    lines: ['职业健康体检率', '98.7%(达标)']
-  },
-  {
-    title: '危废管控',
-    icon: Flag,
-    accent: '#28c98b',
-    glow: 'rgba(40, 201, 139, 0.2)',
-    lines: ['危废暂存合规', '三废处置100%达标']
-  }
-])
-
-const targetWrapperStyle = computed(() => ({
-  width: `${DESIGN_WIDTH * scale.value}px`,
-  height: `${DESIGN_HEIGHT * scale.value}px`
-}))
-
-const targetAreaStyle = computed(() => {
-  const style = {
-    width: `${DESIGN_WIDTH}px`,
-    height: `${DESIGN_HEIGHT}px`,
-    transformOrigin: '0 0'
-  } as Record<string, string | number>
-
-  if (supportsZoom.value) {
-    style.zoom = scale.value
-  } else {
-    style.transform = `scale(${scale.value})`
-  }
-
-  return style
-})
-
-function updateScale() {
-  cancelAnimationFrame(resizeRaf)
-
-  resizeRaf = requestAnimationFrame(() => {
-    const wrapper = wrapperRef.value
-    if (!wrapper) return
-
-    const { clientWidth, clientHeight } = wrapper
-    if (!clientWidth || !clientHeight) return
-
-    scale.value = Math.min(clientWidth / DESIGN_WIDTH, clientHeight / DESIGN_HEIGHT)
-
-    nextTick(() => {
-      resizeHazardChart()
-      resizeSafeDayChart()
-      resizeQhseTrendChart()
-      resizeSocChart()
-    })
-  })
-}
-
-const staticData = ref<any>({})
-const safeDay = ref<SafeDayMap>({})
-const safeDayBarData = ref<SafeDayBarItem[]>([])
-const summaryPanel = ref<any>(null)
-const total = ref(0)
-const instrumentExpired = ref(0)
-const safeDayDisplayOrder = ['瑞气能源', '瑞霖技术', '5#项目', '酸化压裂', '气体增产', '钻修井']
-
-function buildSafeDayBarData(baseData: SafeDayMap) {
-  const mappedData = Object.entries(baseData || {}).map(([name, value]) => ({
-    name,
-    value: Number(value) || 0
-  }))
-  const orderedData = safeDayDisplayOrder
-    .map((name) => mappedData.find((item) => item.name === name))
-    .filter(Boolean) as SafeDayBarItem[]
-  const remainingData = mappedData.filter((item) => !safeDayDisplayOrder.includes(item.name))
-
-  safeDayBarData.value = [...orderedData, ...remainingData]
-}
-
-async function getStatic() {
-  const res = await IotDangerApi.getDangerStatistics(userStore.user.deptId)
-  staticData.value = res.classify || []
-
-  riskZones.value.forEach((zone, index) => {
-    const item = staticData.value[index]
-    if (!item) return
-    zone.value = item.count || 0
-    zone.title = `${item.classify}区`
-  })
-}
-
-async function getCertStatic() {
-  const res = await IotMeasureCertApi.getIotMeasureCertStatistics(userStore.user.deptId)
-  qualificationWarnings.value[0].value = res.expired || 0
-  qualificationWarnings.value[1].value = res.warn || 0
-}
-
-async function getInstrumentOverview() {
-  const listData = await IotInstrumentApi.getInstrumentList({
-    pageNo: 1,
-    pageSize: 10,
-    deptId: userStore.user.deptId
-  })
-  total.value = listData.total || 0
-
-  const statData = await IotInstrumentApi.getInstrumentStatistics(userStore.user.deptId)
-  instrumentExpired.value = statData.expired || 0
-
-  bottomCards.value[1].lines = [
-    `在用: ${total.value}台`,
-    `待检: ${instrumentExpired.value}项(重点关注)`
-  ]
-}
-
-function getHazardChartOption(): echarts.EChartsOption {
-  return {
-    ...ANIMATION,
-    grid: {
-      left: 20,
-      right: 10,
-      top: 24,
-      bottom: 24,
-      containLabel: true
-    },
-    tooltip: createTooltip({
-      trigger: 'axis',
-      confine: true,
-      appendToBody: false,
-      axisPointer: {
-        type: 'shadow',
-        shadowStyle: {
-          color: 'rgba(31, 91, 184, 0.08)'
-        }
-      },
-      formatter(params: any) {
-        if (!params || (Array.isArray(params) && params.length === 0)) return ''
-        const item = Array.isArray(params) ? params[0] : params
-        return `${item.name}<br/>数量:${item.value}`
-      }
-    }),
-    xAxis: {
-      type: 'category',
-      data: hazardBars.value.map((item) => item.label),
-      axisLine: { show: false },
-      axisTick: { show: false },
-      axisLabel: {
-        color: '#5b6f8f',
-        fontSize: 16,
-        fontWeight: 700,
-        fontFamily: FONT_FAMILY,
-        interval: 0
-      }
-    },
-    yAxis: {
-      type: 'value',
-      name: '隐患数量',
-      nameLocation: 'end',
-      nameGap: 13,
-      nameTextStyle: {
-        color: '#657897',
-        fontSize: 12,
-        // fontWeight: 600,
-        fontFamily: FONT_FAMILY
-      },
-      axisLine: { show: false },
-      axisTick: { show: false },
-      axisLabel: {
-        color: '#8a9bb5',
-        fontSize: 13,
-        fontFamily: FONT_FAMILY
-      },
-      splitLine: {
-        lineStyle: {
-          color: THEME.split,
-          type: 'dashed'
-        }
-      }
-    },
-    series: [
-      {
-        type: 'bar',
-        data: hazardBars.value.map((item) => ({
-          value: item.value,
-          itemStyle: {
-            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-              { offset: 0, color: item.color },
-              { offset: 1, color: `${item.color}99` }
-            ]),
-            shadowBlur: 14,
-            shadowColor: item.color,
-            borderRadius: [5, 5, 0, 0]
-          }
-        })),
-        barWidth: 70,
-        label: {
-          show: true,
-          position: 'top',
-          color: '#3c5f96',
-          fontSize: 16,
-          fontWeight: 700,
-          fontFamily: FONT_FAMILY
-        },
-        emphasis: {
-          itemStyle: {
-            shadowBlur: 18
-          }
-        }
-      }
-    ]
-  }
-}
-
-function initHazardChart() {
-  if (!hazardChartRef.value) return
-  hazardChart?.dispose()
-  hazardChart = echarts.init(hazardChartRef.value, undefined, {
-    renderer: CHART_RENDERER
-  })
-  hazardChart.setOption(getHazardChartOption(), true)
-  hazardChart.off('click')
-  hazardChart.on('click', handleHazardBarClick)
-}
-
-function updateHazardChart() {
-  if (!hazardChart) return
-  hazardChart.clear()
-  hazardChart.setOption(getHazardChartOption(), { notMerge: true, lazyUpdate: false })
-}
-
-function resizeHazardChart() {
-  if (!hazardChart) return
-  hazardChart.resize({ animation: { duration: 300 } })
-}
-
-function destroyHazardChart() {
-  hazardChart?.dispose()
-  hazardChart = null
-}
-
-function getSafeDayChartOption(): echarts.EChartsOption {
-  const maxValue = Math.max(...safeDayBarData.value.map((item) => item.value), 0)
-  const axisMax = maxValue > 0 ? Math.ceil(maxValue / 300) * 300 : 300
-
-  return {
-    ...ANIMATION,
-    grid: {
-      left: 30,
-      right: 28,
-      top: 18,
-      bottom: 34,
-      containLabel: true
-    },
-    tooltip: createTooltip({
-      trigger: 'axis',
-      confine: true,
-      appendToBody: false,
-      axisPointer: {
-        type: 'shadow',
-        shadowStyle: {
-          color: 'rgba(79, 141, 255, 0.08)'
-        }
-      },
-      formatter(params: any) {
-        if (!params || (Array.isArray(params) && params.length === 0)) return ''
-        const item = Array.isArray(params) ? params[0] : params
-        return `${item.name}<br/>安全生产天数:<span style="font-weight:700">${item.value}</span>`
-      }
-    }),
-    xAxis: {
-      type: 'value',
-      max: axisMax,
-      axisLine: {
-        lineStyle: {
-          color: 'rgba(106, 144, 221, 0.45)'
-        }
-      },
-      axisTick: { show: false },
-      axisLabel: {
-        color: '#5b6f8f',
-        fontSize: 12,
-        fontWeight: 600,
-        fontFamily: FONT_FAMILY
-      },
-      splitLine: {
-        lineStyle: {
-          color: 'rgba(104, 139, 205, 0.32)',
-          type: 'dashed'
-        }
-      }
-    },
-    yAxis: {
-      type: 'category',
-      inverse: true,
-      data: safeDayBarData.value.map((item) => item.name),
-      axisLine: { show: false },
-      axisTick: { show: false },
-      axisLabel: {
-        color: '#2e4262',
-        fontSize: 14,
-        fontWeight: 700,
-        fontFamily: FONT_FAMILY
-      },
-      splitLine: { show: false }
-    },
-    series: [
-      {
-        name: '安全生产天数',
-        type: 'bar',
-        data: safeDayBarData.value.map((item) => item.value),
-        barWidth: 22,
-        showBackground: true,
-        backgroundStyle: {
-          color: 'rgba(116, 152, 216, 0.08)',
-          borderRadius: 11
-        },
-        itemStyle: {
-          color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
-            { offset: 0, color: '#6f9af0' },
-            { offset: 1, color: '#5f88de' }
-          ]),
-          borderRadius: [0, 11, 11, 0],
-          shadowBlur: 10,
-          shadowColor: 'rgba(95, 136, 222, 0.25)'
-        },
-        label: {
-          show: false
-        },
-        emphasis: {
-          itemStyle: {
-            shadowBlur: 14,
-            shadowColor: 'rgba(95, 136, 222, 0.38)'
-          }
-        }
-      }
-    ],
-    graphic: [
-      {
-        type: 'text',
-        left: 'center',
-        bottom: 0,
-        style: {
-          text: '安全生产天数',
-          fill: '#5b6f8f',
-          font: `700 12px ${FONT_FAMILY}`
-        }
-      }
-    ]
-  }
-}
-
-function initSafeDayChart() {
-  if (!safeDayChartRef.value) return
-  safeDayChart?.dispose()
-  safeDayChart = echarts.init(safeDayChartRef.value, undefined, {
-    renderer: CHART_RENDERER
-  })
-  safeDayChart.setOption(getSafeDayChartOption(), true)
-}
-
-function updateSafeDayChart() {
-  if (!safeDayChart) return
-  safeDayChart.clear()
-  safeDayChart.setOption(getSafeDayChartOption(), { notMerge: true, lazyUpdate: false })
-}
-
-function resizeSafeDayChart() {
-  if (!safeDayChart) return
-  safeDayChart.resize({ animation: { duration: 300 } })
-}
-
-function destroySafeDayChart() {
-  safeDayChart?.dispose()
-  safeDayChart = null
-}
-
-function getQhseTrendChartOption(): echarts.EChartsOption {
-  const activeTab =
-    qhseMetricTabs.find((item) => item.value === activeQhseMetric.value) || qhseMetricTabs[0]
-  const seriesData = qhseTrendSeries.value[activeQhseMetric.value] || []
-
-  return {
-    ...ANIMATION,
-    grid: {
-      left: 32,
-      right: 18,
-      top: 28,
-      bottom: 28,
-      containLabel: true
-    },
-    tooltip: createTooltip({
-      trigger: 'axis',
-      confine: true,
-      appendToBody: false,
-      axisPointer: {
-        type: 'line',
-        lineStyle: {
-          color: `${activeTab.accent}99`,
-          width: 1.5
-        }
-      },
-      formatter(params: any) {
-        if (!params || (Array.isArray(params) && params.length === 0)) return ''
-        const item = Array.isArray(params) ? params[0] : params
-        return `${item.axisValue}<br/>${activeTab.label}:${item.data}`
-      }
-    }),
-    xAxis: {
-      type: 'category',
-      boundaryGap: false,
-      data: seriesData.map((item) => item.year),
-      axisLine: {
-        lineStyle: {
-          color: 'rgba(106, 144, 221, 0.45)'
-        }
-      },
-      axisTick: { show: false },
-      axisLabel: {
-        color: '#5b6f8f',
-        rotate: 28,
-        fontSize: 13,
-        fontWeight: 600,
-        fontFamily: FONT_FAMILY
-      }
-    },
-    yAxis: {
-      type: 'value',
-      axisLine: { show: false },
-      axisTick: { show: false },
-      axisLabel: {
-        color: '#8a9bb5',
-        fontSize: 12,
-        fontFamily: FONT_FAMILY
-      },
-      splitLine: {
-        lineStyle: {
-          color: 'rgba(104, 139, 205, 0.22)',
-          type: 'dashed'
-        }
-      }
-    },
-    series: [
-      {
-        type: 'line',
-        smooth: true,
-        symbol: 'circle',
-        symbolSize: 9,
-        showSymbol: true,
-        data: seriesData.map((item) => item.value),
-        lineStyle: {
-          width: 3,
-          color: activeTab.accent
-        },
-        itemStyle: {
-          color: activeTab.accent,
-          borderColor: '#fff',
-          borderWidth: 2
-        },
-        areaStyle: {
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
-            { offset: 0, color: `${activeTab.accent}55` },
-            { offset: 1, color: `${activeTab.accent}08` }
-          ])
-        }
-      }
-    ]
-  }
-}
-
-function initQhseTrendChart() {
-  if (!qhseTrendChartRef.value) return
-  qhseTrendChart?.dispose()
-  qhseTrendChart = echarts.init(qhseTrendChartRef.value, undefined, {
-    renderer: CHART_RENDERER
-  })
-  qhseTrendChart.setOption(getQhseTrendChartOption(), { notMerge: true, lazyUpdate: false })
-}
-
-function updateQhseTrendChart() {
-  if (!qhseTrendChart) return
-  qhseTrendChart.clear()
-  qhseTrendChart.setOption(getQhseTrendChartOption(), { notMerge: true, lazyUpdate: false })
-}
-
-function resizeQhseTrendChart() {
-  if (!qhseTrendChart) return
-  qhseTrendChart.resize({ animation: { duration: 300 } })
-}
-
-function destroyQhseTrendChart() {
-  qhseTrendChart?.dispose()
-  qhseTrendChart = null
-}
-
-function getSocChartOption(): echarts.EChartsOption {
-  return {
-    ...ANIMATION,
-    title: {
-      text: 'SOC',
-      left: '34%',
-      top: '54%',
-      textAlign: 'center',
-      textVerticalAlign: 'middle',
-      textStyle: {
-        color: '#3d5f94',
-        fontSize: 24,
-        fontWeight: 800,
-        fontFamily: FONT_FAMILY
-      }
-    },
-    tooltip: createTooltip({
-      trigger: 'item',
-      confine: true,
-      appendToBody: false,
-      formatter(params: any) {
-        if (!params) return ''
-        return `${params.name}<br/>数量:${params.value}<br/>占比:${params.percent}%`
-      }
-    }),
-    legend: {
-      orient: 'vertical',
-      right: 100,
-      top: 'center',
-      itemWidth: 12,
-      itemHeight: 12,
-      icon: 'circle',
-      textStyle: {
-        color: '#5b6f8f',
-        fontSize: 15,
-        fontWeight: 600,
-        fontFamily: FONT_FAMILY
-      }
-    },
-    series: [
-      {
-        name: 'SOC卡类型',
-        type: 'pie',
-        radius: ['50%', '74%'],
-        center: ['34%', '54%'],
-        avoidLabelOverlap: true,
-        itemStyle: {
-          borderColor: 'rgba(255, 255, 255, 0.85)',
-          borderWidth: 2,
-          shadowBlur: 10,
-          shadowColor: 'rgba(31, 91, 184, 0.12)'
-        },
-        label: {
-          show: true,
-          color: '#4e6483',
-          fontSize: 13,
-          fontWeight: 700,
-          formatter: '{d}%'
-        },
-        labelLine: {
-          lineStyle: {
-            color: 'rgba(91, 111, 143, 0.5)'
-          },
-          length: 10,
-          length2: 8
-        },
-        data: permitStats.value.map((item) => ({
-          name: item.label,
-          value: item.value,
-          itemStyle: {
-            color: item.color
-          }
-        }))
-      }
-    ]
-  }
-}
-
-function initSocChart() {
-  if (!socChartRef.value) return
-  socChart?.dispose()
-  socChart = echarts.init(socChartRef.value, undefined, {
-    renderer: CHART_RENDERER
-  })
-  socChart.setOption(getSocChartOption(), { notMerge: true, lazyUpdate: false })
-}
-
-function updateSocChart() {
-  if (!socChart) return
-  socChart.clear()
-  socChart.setOption(getSocChartOption(), { notMerge: true, lazyUpdate: false })
-}
-
-function resizeSocChart() {
-  if (!socChart) return
-  socChart.resize({ animation: { duration: 300 } })
-}
-
-function destroySocChart() {
-  socChart?.dispose()
-  socChart = null
-}
-
-const tableRowStyle = ({ row }) => {
-  if (row.expired) {
-    return { backgroundColor: '#ffe6e6' }
-  }
-  if (row.alertWarn) {
-    return { backgroundColor: '#e19f1a' }
-  }
-  return {}
-}
-
-const tableRowClassName = ({ row }) => {
-  if (row.expired) {
-    return 'expired-row'
-  }
-  if (row.alertWarn) {
-    return 'alert-warn-row'
-  }
-  return ''
-}
-
-function formatPercent(numerator: number, denominator: number) {
-  if (!denominator || Number.isNaN(denominator)) {
-    return '0%'
-  }
-  return `${((numerator / denominator) * 100).toFixed(1)}%`
-}
-
-async function loadHomeBoard() {
-  summaryPanel.value = await kanbanApi.getKanban()
-
-  summaryCards.value[3].value = formatPercent(
-    summaryPanel.value.totdalCert - summaryPanel.value.expiredCert,
-    summaryPanel.value.totdalCert
-  )
-  summaryCards.value[3].note = `Warn: ${summaryPanel.value.warnCert || 0}`
-
-  hazardBars.value[0].value = summaryPanel.value.totalHazard || 0
-  hazardBars.value[1].value =
-    (summaryPanel.value.totalHazard || 0) - (summaryPanel.value.todoHazard || 0)
-  hazardBars.value[2].value = summaryPanel.value.todoHazard || 0
-
-  safeDay.value = (await kanbanApi.getSafeDay(userStore.getUser.deptId)) || {}
-  buildSafeDayBarData(safeDay.value)
-
-  await Promise.all([getStatic(), getCertStatic(), getInstrumentOverview()])
-
-  nextTick(() => {
-    updateHazardChart()
-    updateSafeDayChart()
-    updateQhseTrendChart()
-    updateSocChart()
-    resizeHazardChart()
-    resizeSafeDayChart()
-    resizeQhseTrendChart()
-    resizeSocChart()
-  })
-}
-
-const certLoading = ref(false)
-const certTableData = ref<any[]>([])
-const certTotal = ref(0)
-const certQueryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-
-  expired: undefined as string | undefined,
-  alertWarn: undefined as string | undefined
-})
-
-const certWarningDialogVisible = ref(false)
-const certWarningDialogTitle = ref('')
-const certWarningLoading = ref(false)
-const certWarningList = ref<any[]>([])
-const certWarningTotal = ref(0)
-const certWarningQueryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  deptId: userStore.getUser.deptId,
-  type: undefined as string | undefined,
-  classify: undefined as string | undefined,
-  userName: undefined as string | undefined,
-  expired: undefined as boolean | undefined,
-  alertWarn: undefined as boolean | undefined
-})
-
-const hazardDialogVisible = ref(false)
-const hazardDialogTitle = ref('')
-const hazardDialogLoading = ref(false)
-const hazardDialogList = ref<any[]>([])
-const hazardDialogTotal = ref(0)
-const hazardDialogQueryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  deptId: userStore.getUser.deptId,
-  status: undefined as string | undefined,
-  address: '',
-  source: ''
-})
-
-const dangerDialogVisible = ref(false)
-const dangerDialogTitle = ref('')
-const dangerDialogLoading = ref(false)
-const dangerDialogList = ref<any[]>([])
-const dangerDialogTotal = ref(0)
-const dangerDialogQueryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  riskGrade: '',
-  deptId: userStore.getUser.deptId
-})
-
-const instrumentDialogVisible = ref(false)
-const instrumentDialogTitle = ref('')
-const instrumentDialogLoading = ref(false)
-const instrumentDialogList = ref<any[]>([])
-const instrumentDialogTotal = ref(0)
-const instrumentDialogQueryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  measureName: undefined as string | undefined,
-  measureCertNo: undefined as string | undefined,
-  deptId: userStore.getUser.deptId,
-  expired: undefined as boolean | undefined,
-  alertWarn: undefined as boolean | undefined
-})
-
-const getCertificateTypeText = (type: string) => {
-  const map: Record<string, string> = {
-    personal: '个人证书',
-    organization: '组织证书',
-    other: '其他'
-  }
-  return map[type] || type
-}
-
-function formatDateCorrectly(timestamp: any) {
-  if (!timestamp) return ''
-  let time = Number(timestamp)
-  if (time < 10000000000) {
-    time = time * 1000
-  }
-  return formatDate(time).substring(0, 10)
-}
-
-let dialogFileView = ref(false)
-let fileList = ref([])
-const viewFile = (file) => {
-  fileList.value = file.split(',')
-  dialogFileView.value = true
-  // window.open(file)
-}
-
-const viewFileInfo = (file) => {
-  window.open(
-    'http://doc.deepoil.cc:8012/onlinePreview?url=' + encodeURIComponent(Base64.encode(file))
-  )
-}
-
-const extractFileName = (url: string): string => {
-  try {
-    // 移除查询参数和哈希
-    const cleanUrl = url.split('?')[0].split('#')[0]
-    // 获取最后一个斜杠后的内容
-    const parts = cleanUrl.split('/')
-    const fileName = parts[parts.length - 1]
-    // URL 解码
-    return decodeURIComponent(fileName) || url
-  } catch {
-    // 如果解析失败,返回原始 URL
-    return url
-  }
-}
-
-const handleDownload = async (url) => {
-  try {
-    const response = await fetch(url)
-    const blob = await response.blob()
-    const downloadUrl = window.URL.createObjectURL(blob)
-
-    const link = document.createElement('a')
-    link.href = downloadUrl
-    link.download = url.split('/').pop() // 自动获取文件名‌:ml-citation{ref="3" data="citationList"}
-    link.click()
-
-    URL.revokeObjectURL(downloadUrl)
-  } catch (error) {
-    console.error('下载失败:', error)
-  }
-}
-
-async function getCertificateBoardList() {
-  certLoading.value = true
-  try {
-    const data = await IotCertificateApi.getCertificateList({
-      ...certQueryParams
-      // deptId: userStore.user.deptId
-      // type: 'organization'
-    })
-    certTableData.value = data.list || []
-    certTotal.value = data.total || 0
-  } finally {
-    certLoading.value = false
-  }
-}
-
-async function getCertificateWarningList() {
-  certWarningLoading.value = true
-  try {
-    const data = await IotMeasureCertApi.getIotMeasureCertPage(certWarningQueryParams)
-    certWarningList.value = data.list || []
-    certWarningTotal.value = data.total || 0
-  } finally {
-    certWarningLoading.value = false
-  }
-}
-
-async function getHazardDialogList() {
-  hazardDialogLoading.value = true
-  try {
-    const data = await IotHiddenApi.getHiddenList(hazardDialogQueryParams)
-    hazardDialogList.value = data.list || []
-    hazardDialogTotal.value = data.total || 0
-  } finally {
-    hazardDialogLoading.value = false
-  }
-}
-
-async function getDangerDialogList() {
-  dangerDialogLoading.value = true
-  try {
-    const data = await IotDangerApi.getDangerList(dangerDialogQueryParams)
-    dangerDialogList.value = data.list || []
-    dangerDialogTotal.value = data.total || 0
-  } finally {
-    dangerDialogLoading.value = false
-  }
-}
-
-async function getInstrumentDialogList() {
-  instrumentDialogLoading.value = true
-  try {
-    const data = await IotInstrumentApi.getInstrumentList(instrumentDialogQueryParams)
-    instrumentDialogList.value = data.list || []
-    instrumentDialogTotal.value = data.total || 0
-  } finally {
-    instrumentDialogLoading.value = false
-  }
-}
-
-function openHazardDialog(barLabel: string) {
-  hazardDialogQueryParams.pageNo = 1
-  hazardDialogQueryParams.pageSize = 10
-  hazardDialogQueryParams.deptId = userStore.getUser.deptId
-  hazardDialogQueryParams.address = ''
-  hazardDialogQueryParams.source = ''
-
-  if (barLabel === '未整改') {
-    hazardDialogTitle.value = '未整改隐患列表'
-    hazardDialogQueryParams.status = 'todo'
-  } else if (barLabel === '已整改') {
-    hazardDialogTitle.value = '已整改隐患列表'
-    hazardDialogQueryParams.status = 'finished'
-  } else {
-    hazardDialogTitle.value = '隐患总数列表'
-    hazardDialogQueryParams.status = undefined
-  }
-
-  hazardDialogVisible.value = true
-  getHazardDialogList()
-}
-
-function openDangerDialog(zone: { title?: string }) {
-  const riskGradeLabel = String(zone.title || '')
-    .replace(/区$/, '')
-    .trim()
-  const matchedOption = dangerGradeOptions.value.find((item) => item.label === riskGradeLabel)
-  const riskGrade = matchedOption?.value || ''
-  dangerDialogQueryParams.pageNo = 1
-  dangerDialogQueryParams.pageSize = 10
-  dangerDialogQueryParams.deptId = userStore.getUser.deptId
-  dangerDialogQueryParams.riskGrade = riskGrade
-  dangerDialogTitle.value = riskGradeLabel ? `${riskGradeLabel}风险列表` : '风险列表'
-  dangerDialogVisible.value = true
-  getDangerDialogList()
-}
-
-function openInstrumentDialog(type: 'all' | 'expired') {
-  instrumentDialogQueryParams.pageNo = 1
-  instrumentDialogQueryParams.pageSize = 10
-  instrumentDialogQueryParams.measureName = undefined
-  instrumentDialogQueryParams.measureCertNo = undefined
-  instrumentDialogQueryParams.deptId = userStore.getUser.deptId
-  instrumentDialogQueryParams.alertWarn = undefined
-
-  if (type === 'expired') {
-    instrumentDialogTitle.value = '待检设备列表'
-    instrumentDialogQueryParams.expired = true
-  } else {
-    instrumentDialogTitle.value = '在用设备列表'
-    instrumentDialogQueryParams.expired = undefined
-  }
-
-  instrumentDialogVisible.value = true
-  getInstrumentDialogList()
-}
-
-function handleHazardBarClick(params: { name?: string }) {
-  if (!params?.name) return
-  openHazardDialog(params.name)
-}
-
-function openCertificateWarningDialog(type: 'expired' | 'warn') {
-  certWarningQueryParams.pageNo = 1
-  certWarningQueryParams.pageSize = 10
-  certWarningQueryParams.deptId = userStore.getUser.deptId
-  certWarningQueryParams.type = undefined
-  certWarningQueryParams.classify = undefined
-  certWarningQueryParams.userName = undefined
-
-  if (type === 'expired') {
-    certWarningDialogTitle.value = '证件过期'
-    certWarningQueryParams.expired = true
-    certWarningQueryParams.alertWarn = undefined
-  } else {
-    certWarningDialogTitle.value = '证件即将到期'
-    certWarningQueryParams.expired = undefined
-    certWarningQueryParams.alertWarn = true
-  }
-
-  certWarningDialogVisible.value = true
-  getCertificateWarningList()
-}
-
-function handleCertificateQuery() {
-  certQueryParams.pageNo = 1
-  getCertificateBoardList()
-}
-
-function resetCertificateQuery() {
-  certQueryParams.pageNo = 1
-  certQueryParams.pageSize = 10
-
-  certQueryParams.expired = undefined
-  certQueryParams.alertWarn = undefined
-  getCertificateBoardList()
-}
-
-watch(
-  () => activeSummaryTab.value,
-  async (value) => {
-    if (value === 'certificate') {
-      await getCertificateBoardList()
-    } else {
-      nextTick(() => {
-        resizeHazardChart()
-        resizeSafeDayChart()
-        resizeQhseTrendChart()
-        resizeSocChart()
-      })
-    }
-  }
-)
-
-watch(
-  () => type.value,
-  (value) => {
-    timeVal.value = getCurrentPickerValue(value)
-  }
-)
-
-onMounted(async () => {
-  // supportsZoom.value = typeof CSS !== 'undefined' && CSS.supports?.('zoom', '1') === true
-  nextTick(updateScale)
-  resizeObserver = new ResizeObserver(updateScale)
-  if (wrapperRef.value) {
-    resizeObserver.observe(wrapperRef.value)
-  }
-  initHazardChart()
-  initSafeDayChart()
-  initQhseTrendChart()
-  initSocChart()
-  window.addEventListener('resize', updateScale)
-  window.addEventListener('resize', resizeHazardChart)
-  window.addEventListener('resize', resizeSafeDayChart)
-  window.addEventListener('resize', resizeQhseTrendChart)
-  window.addEventListener('resize', resizeSocChart)
-
-  await loadHomeBoard()
-})
-
-onUnmounted(() => {
-  resizeObserver?.disconnect()
-  window.removeEventListener('resize', updateScale)
-  window.removeEventListener('resize', resizeHazardChart)
-  window.removeEventListener('resize', resizeSafeDayChart)
-  window.removeEventListener('resize', resizeQhseTrendChart)
-  window.removeEventListener('resize', resizeSocChart)
-  cancelAnimationFrame(resizeRaf)
-  destroyHazardChart()
-  destroySafeDayChart()
-  destroyQhseTrendChart()
-  destroySocChart()
-})
-</script>
-
-<template>
-  <div ref="wrapperRef" class="bg absolute top-0 left-0 size-full z-10">
-    <div class="mx-a overflow-hidden" :style="targetWrapperStyle">
-      <div id="qhse-kanban" class="bg qhse-board" :style="targetAreaStyle">
-        <header class="header">{{ pageTitle }}</header>
-
-        <div class="summary-toolbar">
-          <div class="summary-toolbar__tabs">
-            <button
-              v-for="tab in summaryTabs"
-              :key="tab.value"
-              type="button"
-              class="summary-toolbar__tab"
-              :class="{ 'is-active': activeSummaryTab === tab.value }"
-              @click="activeSummaryTab = tab.value"
-            >
-              {{ tab.label }}
-            </button>
-          </div>
-
-          <div v-if="activeSummaryTab === 'home'" class="summary-toolbar__date">
-            <span class="summary-toolbar__date-label">日期:</span>
-
-            <div class="flex items-center gap-2 datepicker">
-              <el-date-picker
-                v-model="timeVal"
-                :teleport="true"
-                :type="type === 'day' ? 'date' : type === 'month' ? 'month' : 'year'"
-                class="summary-toolbar__picker el-date-picker"
-                popper-class="summary-toolbar__picker-popper"
-                value-format="YYYY-MM-DD"
-                range-separator="~"
-                start-placeholder="开始"
-                end-placeholder="结束"
-                @change="handelChange"
-                style="width: 150px; height: 40px; margin-left: 10px; background-color: transparent"
-              />
-
-              <span class="bg-[#f5f9ff] rounded-2xl px-2 py-0 cursor-pointer">月度</span>
-              <span class="bg-[#f5f9ff] rounded-2xl px-2 py-0 cursor-pointer">季度</span>
-              <span class="bg-[#f5f9ff] rounded-2xl px-2 py-0 cursor-pointer">年度</span>
-              <span class="bg-[#f5f9ff] rounded-2xl px-2 py-0 cursor-pointer">初始化</span>
-            </div>
-          </div>
-        </div>
-
-        <div v-if="activeSummaryTab === 'home'" class="board-body">
-          <section class="panel summary-panel kb-stage-card kb-stage-card--1">
-            <div class="panel-title">
-              <span class="icon-decorator"><span></span><span></span></span>
-              QHSE关键数据
-            </div>
-            <div class="summary-grid">
-              <article
-                v-for="card in summaryCards"
-                :key="card.title"
-                class="summary-card summary-tile"
-                :style="
-                  {
-                    '--card-accent': card.accent,
-                    '--card-glow': card.glow
-                  } as any
-                "
-              >
-                <span class="summary-card__shine"></span>
-                <span class="summary-card__corner"></span>
-                <div class="summary-card__icon">
-                  <el-icon class="summary-card__icon-glyph">
-                    <component :is="card.icon" />
-                  </el-icon>
-                </div>
-                <div class="summary-card__content">
-                  <div class="summary-card__label">{{ card.title }}</div>
-                  <div class="summary-tile__meta">
-                    <span class="summary-tile__value">{{ card.value }}</span>
-                    <span class="summary-tile__note" :style="{ color: card.accent }">
-                      {{ card.note }}
-                    </span>
-                  </div>
-                </div>
-              </article>
-            </div>
-          </section>
-
-          <div class="board-main">
-            <div class="center-column">
-              <section class="panel board-panel board-panel--center kb-stage-card kb-stage-card--4">
-                <div class="panel-title panel-title--center">
-                  <span class="icon-decorator"><span></span><span></span></span>
-                  风险管控及隐患治理
-                </div>
-                <div class="risk-grid">
-                  <article
-                    v-for="zone in riskZones"
-                    :key="zone.title"
-                    class="risk-card risk-card--clickable"
-                    @click="openDangerDialog(zone)"
-                  >
-                    <div class="risk-card__title">
-                      <span class="risk-card__dot" :style="{ background: zone.color }"></span>
-                      <span :style="{ color: zone.color }">{{ zone.title }}</span>
-                      <CountTo
-                        :duration="2600"
-                        :end-val="zone.value"
-                        :start-val="0"
-                        :style="{ color: zone.color, fontSize: '22px' }"
-                      />
-                    </div>
-                    <div class="risk-card__desc">{{ zone.desc }}</div>
-                  </article>
-                </div>
-
-                <div class="risk-hazard-block mt-2!">
-                  <div
-                    ref="hazardChartRef"
-                    class="chart-panel chart-panel--echart risk-hazard-block__chart"
-                  ></div>
-                </div>
-              </section>
-            </div>
-
-            <div class="left-column">
-              <section class="panel board-panel kb-stage-card kb-stage-card--3">
-                <div class="panel-title panel-title--center">
-                  <span class="icon-decorator"><span></span><span></span></span>
-                  QHSE指标
-                </div>
-
-                <section class="board-panel kb-stage-card kb-stage-card--5">
-                  <div ref="safeDayChartRef" class="safe-day-chart-panel"></div>
-                </section>
-
-                <section class="board-panel kb-stage-card kb-stage-card--5">
-                  <div class="qhse-metric-tabs">
-                    <button
-                      v-for="item in qhseMetricTabs"
-                      :key="item.value"
-                      :title="item.label2"
-                      type="button"
-                      class="qhse-metric-tab"
-                      :class="{ 'is-active': activeQhseMetric === item.value }"
-                      @click="
-                        () => {
-                          activeQhseMetric = item.value
-                          nextTick(() => {
-                            updateQhseTrendChart()
-                            resizeQhseTrendChart()
-                          })
-                        }
-                      "
-                    >
-                      {{ item.label }}
-                    </button>
-                  </div>
-                  <div ref="qhseTrendChartRef" class="qhse-trend-chart-panel"></div>
-                </section>
-              </section>
-            </div>
-
-            <div class="right-column">
-              <section class="panel board-panel board-panel--center kb-stage-card kb-stage-card--4">
-                <div class="panel-title panel-title--center">
-                  <span class="icon-decorator"><span></span><span></span></span>
-                  行为安全与系统预警
-                </div>
-                <section class="board-panel kb-stage-card kb-stage-card--5 pt-2">
-                  <div ref="socChartRef" class="soc-chart-panel"></div>
-                </section>
-
-                <section class="board-panel kb-stage-card kb-stage-card--6 pl-4">
-                  <div class="qualification-panel">
-                    <div class="qualification-icon">
-                      <el-icon>
-                        <CollectionTag />
-                      </el-icon>
-                    </div>
-                    <div class="qualification-list">
-                      <div
-                        v-for="item in qualificationWarnings"
-                        :key="item.label"
-                        class="qualification-item qualification-item--clickable"
-                        @click="openCertificateWarningDialog(item.key)"
-                      >
-                        <span class="qualification-item__label">{{ item.label }}:</span>
-                        <strong :style="{ color: item.accent }">
-                          <CountTo
-                            :duration="2600"
-                            :end-val="item.value"
-                            :start-val="0"
-                            :style="{ color: item.accent }"
-                          />
-                          <span class="pl-2">人</span>
-                        </strong>
-                      </div>
-                    </div>
-                  </div>
-                </section>
-              </section>
-            </div>
-          </div>
-
-          <section class="bottom-grid">
-            <article
-              v-for="(card, index) in bottomCards"
-              :key="card.title"
-              class="panel bottom-card kb-stage-card"
-              :class="`kb-stage-card--${index + 7}`"
-              :style="
-                {
-                  '--card-accent': card.accent,
-                  '--card-glow': card.glow
-                } as any
-              "
-            >
-              <span class="summary-card__shine"></span>
-              <div class="bottom-card__header">
-                <div class="summary-card__icon bottom-card__icon">
-                  <el-icon class="summary-card__icon-glyph">
-                    <component :is="card.icon" />
-                  </el-icon>
-                </div>
-                <div class="bottom-card__title">{{ card.title }}</div>
-              </div>
-              <div class="bottom-card__content">
-                <template v-if="card.title === '安全检测'">
-                  <button
-                    type="button"
-                    class="bottom-card__action"
-                    @click="openInstrumentDialog('all')"
-                  >
-                    {{ card.lines[0] }}
-                  </button>
-                  <button
-                    type="button"
-                    class="bottom-card__action"
-                    @click="openInstrumentDialog('expired')"
-                  >
-                    {{ card.lines[1] }}
-                  </button>
-                </template>
-                <template v-else>
-                  <p v-for="line in card.lines" :key="line">{{ line }}</p>
-                </template>
-              </div>
-            </article>
-          </section>
-        </div>
-
-        <div v-else class="certificate-board">
-          <section class="panel certificate-filter-panel">
-            <div class="certificate-filters">
-              <div class="certificate-filter-item">
-                <span class="certificate-filter-item__label">是否过期</span>
-                <el-select
-                  v-model="certQueryParams.expired"
-                  clearable
-                  placeholder="请选择是否过期"
-                  class="certificate-filter-item__control"
-                >
-                  <el-option
-                    v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
-                    :key="dict.value"
-                    :label="dict.label"
-                    :value="dict.value"
-                  />
-                </el-select>
-              </div>
-
-              <div class="certificate-filter-item">
-                <span class="certificate-filter-item__label">是否预警</span>
-                <el-select
-                  v-model="certQueryParams.alertWarn"
-                  clearable
-                  placeholder="请选择是否过期"
-                  class="certificate-filter-item__control"
-                >
-                  <el-option
-                    v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
-                    :key="dict.value"
-                    :label="dict.label"
-                    :value="dict.value"
-                  />
-                </el-select>
-              </div>
-
-              <div class="certificate-filter-actions">
-                <el-button type="primary" :icon="Search" @click="handleCertificateQuery"
-                  >查询</el-button
-                >
-                <el-button :icon="Refresh" @click="resetCertificateQuery">重置</el-button>
-              </div>
-            </div>
-          </section>
-
-          <section class="panel certificate-table-panel">
-            <el-table
-              :loading="certLoading"
-              :data="certTableData"
-              :row-style="tableRowStyle"
-              :row-class-name="tableRowClassName"
-              height="100vh"
-              :header-cell-style="{
-                height: '42px'
-              }"
-              class="device-list-table production-brief-table"
-            >
-              <el-table-column label="序号" width="80" align="center">
-                <template #default="scope">
-                  {{ (certQueryParams.pageNo - 1) * certQueryParams.pageSize + scope.$index + 1 }}
-                </template>
-              </el-table-column>
-
-              <el-table-column label="证书类别" align="center" width="150" prop="classify">
-                <template #default="scope">
-                  <dict-tag :type="DICT_TYPE.ORG_CERT" :value="scope.row.classify" />
-                </template>
-              </el-table-column>
-              <el-table-column
-                label="证书名称"
-                align="center"
-                prop="certName"
-                show-overflow-tooltip
-              />
-
-              <el-table-column label="所在部门" align="center" prop="deptName" />
-
-              <el-table-column label="颁发机构" align="center" prop="certOrg" />
-
-              <el-table-column label="证书标准" align="center" prop="certStandard" />
-
-              <el-table-column label="颁发时间" align="center" prop="certIssue">
-                <template #default="scope">
-                  {{ formatDateCorrectly(scope.row.certIssue) }}
-                </template>
-              </el-table-column>
-
-              <el-table-column label="有效期" align="center">
-                <template #default="scope">
-                  {{ formatDateCorrectly(scope.row.certExpire) }}
-                </template>
-              </el-table-column>
-
-              <el-table-column label="到期提醒" align="center">
-                <template #default="scope"> {{ scope.row.noticeBefore }}天前提醒 </template>
-              </el-table-column>
-
-              <el-table-column label="备注" align="center" prop="remark" />
-              <el-table-column label="操作" align="center" fixed="right" min-width="180px" action>
-                <template #default="scope">
-                  <el-button
-                    link
-                    type="success"
-                    v-if="scope.row.certPic"
-                    @click="viewFile(scope.row.certPic)"
-                  >
-                    查看证书
-                  </el-button>
-                </template>
-              </el-table-column>
-            </el-table>
-
-            <div class="certificate-pagination">
-              <Pagination
-                :total="certTotal"
-                v-model:page="certQueryParams.pageNo"
-                v-model:limit="certQueryParams.pageSize"
-                @pagination="getCertificateBoardList"
-              />
-            </div>
-
-            <div class="shadow rounded-lg p-3 min-w-0 flex gap-4 justify-between">
-              <div
-                class="bg-[#fef0f0] text-[#f57979] px-3 flex-1 flex items-center justify-center text-center py-2 text-sm rounded-md"
-                ><el-icon class="stats-card__icon" :size="28">
-                  <Icon icon="ep:info-filled" /> </el-icon
-                >证书已过期红色预警</div
-              >
-              <div
-                class="bg-[#fdf6ec] flex-1 text-[#e6a23c] text-center flex items-center justify-center px-3 py-2 text-sm rounded-md"
-                ><el-icon class="stats-card__icon" :size="28">
-                  <Icon icon="ep:bell-filled" /> </el-icon
-                >证书60天橙色预警</div
-              >
-            </div>
-          </section>
-        </div>
-      </div>
-    </div>
-  </div>
-
-  <Dialog v-model="dialogFileView" title="附件">
-    <div
-      v-for="(file, index) in fileList"
-      :key="index"
-      class="mt-5 flex min-w-0 items-center justify-between gap-5"
-    >
-      <span class="file-name-text flex-1 min-w-0">{{ extractFileName(file) }}</span>
-      <div class="flex shrink-0 items-center gap-5 flex-nowrap">
-        <el-button link type="primary" @click="viewFileInfo(file)">
-          <Icon icon="ep:view" class="mr-2px" />查看
-        </el-button>
-        <el-button link type="primary" @click="handleDownload(file)">
-          <Icon icon="ep:download" class="mr-2px" />下载
-        </el-button>
-      </div>
-    </div>
-
-    <template #footer>
-      <div class="dialog-footer mt-10">
-        <el-button type="primary" @click="dialogFileView = false">确认</el-button>
-      </div>
-    </template>
-  </Dialog>
-
-  <Dialog v-model="certWarningDialogVisible" :title="certWarningDialogTitle" width="1100px">
-    <div class="certificate-warning-dialog">
-      <el-table
-        :loading="certWarningLoading"
-        :data="certWarningList"
-        :row-style="tableRowStyle"
-        :row-class-name="tableRowClassName"
-        height="400"
-        class="device-list-table production-brief-table"
-      >
-        <el-table-column label="序号" width="80" align="center">
-          <template #default="scope">
-            {{
-              (certWarningQueryParams.pageNo - 1) * certWarningQueryParams.pageSize +
-              scope.$index +
-              1
-            }}
-          </template>
-        </el-table-column>
-        <el-table-column label="证书类型" align="center" prop="type" width="120">
-          <template #default="scope">
-            {{ getCertificateTypeText(scope.row.type) }}
-          </template>
-        </el-table-column>
-        <el-table-column label="证书类别" align="center" width="150" prop="classify">
-          <template #default="scope">
-            <dict-tag
-              v-if="scope.row.type === 'organization'"
-              :type="DICT_TYPE.ORG_CERT"
-              :value="scope.row.classify"
-            />
-            <dict-tag v-else :type="DICT_TYPE.PERSON_CERT" :value="scope.row.classify" />
-          </template>
-        </el-table-column>
-        <el-table-column
-          label="证书名称"
-          align="center"
-          prop="certName"
-          show-overflow-tooltip
-          width="100"
-        />
-        <el-table-column label="所属人" align="center" prop="userName" width="100" />
-        <el-table-column
-          label="所在部门"
-          align="center"
-          prop="deptName"
-          width="100"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="颁发机构"
-          align="center"
-          prop="certOrg"
-          width="180"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="证书标准"
-          align="center"
-          prop="certStandard"
-          width="100"
-          show-overflow-tooltip
-        />
-        <el-table-column label="颁发时间" align="center" prop="certIssue" width="120">
-          <template #default="scope">
-            {{ formatDateCorrectly(scope.row.certIssue) }}
-          </template>
-        </el-table-column>
-        <el-table-column label="有效期" align="center" prop="certExpire" width="120">
-          <template #default="scope">
-            {{ formatDateCorrectly(scope.row.certExpire) }}
-          </template>
-        </el-table-column>
-        <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
-        <el-table-column label="操作" align="center" fixed="right" min-width="100px">
-          <template #default="scope">
-            <el-button
-              link
-              type="success"
-              v-if="scope.row.certPic"
-              @click="viewFile(scope.row.certPic)"
-            >
-              查看证书
-            </el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <div class="certificate-pagination">
-        <Pagination
-          :total="certWarningTotal"
-          v-model:page="certWarningQueryParams.pageNo"
-          v-model:limit="certWarningQueryParams.pageSize"
-          @pagination="getCertificateWarningList"
-        />
-      </div>
-    </div>
-  </Dialog>
-
-  <Dialog v-model="hazardDialogVisible" :title="hazardDialogTitle" width="1400px">
-    <div class="certificate-warning-dialog">
-      <el-table
-        :loading="hazardDialogLoading"
-        :data="hazardDialogList"
-        height="400"
-        class="device-list-table production-brief-table"
-      >
-        <el-table-column label="序号" width="80" align="center">
-          <template #default="scope">
-            {{
-              (hazardDialogQueryParams.pageNo - 1) * hazardDialogQueryParams.pageSize +
-              scope.$index +
-              1
-            }}
-          </template>
-        </el-table-column>
-        <el-table-column label="单位" align="center" prop="deptName" min-width="140" />
-        <el-table-column label="类型" align="center" prop="classifyName" min-width="120" />
-        <el-table-column label="来源" align="center" min-width="110">
-          <template #default="scope">
-            <dict-tag :type="DICT_TYPE.QHSE_HAZARD_SOURCE" :value="scope.row.source" />
-          </template>
-        </el-table-column>
-        <el-table-column label="地点" align="center" prop="address" min-width="140" />
-        <el-table-column
-          label="存在问题"
-          align="center"
-          prop="problem"
-          min-width="180"
-          show-overflow-tooltip
-        />
-        <el-table-column label="隐患附件" align="center" min-width="100">
-          <template #default="{ row }">
-            <el-button v-if="row.hazardFile" link type="primary" @click="viewFile(row.hazardFile)">
-              查看
-            </el-button>
-            <span v-else>-</span>
-          </template>
-        </el-table-column>
-        <el-table-column
-          label="整改人"
-          align="center"
-          prop="correctPersonName"
-          min-width="100"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="整改要求"
-          align="center"
-          prop="correctStandard"
-          min-width="160"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="整改情况"
-          align="center"
-          prop="rectifyDesc"
-          min-width="160"
-          show-overflow-tooltip
-        />
-        <el-table-column label="整改附件" align="center" min-width="100">
-          <template #default="{ row }">
-            <el-button
-              v-if="row.rectifyFile"
-              link
-              type="primary"
-              @click="viewFile(row.rectifyFile)"
-            >
-              查看
-            </el-button>
-            <span v-else>-</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="排查时间" align="center" min-width="120">
-          <template #default="{ row }">
-            {{ formatDateCorrectly(row.checkTime) }}
-          </template>
-        </el-table-column>
-        <el-table-column label="状态" align="center" min-width="90">
-          <template #default="scope">
-            <dict-tag :type="DICT_TYPE.QHSE_HAZARD_STATUS" :value="scope.row.status" />
-          </template>
-        </el-table-column>
-        <el-table-column
-          label="备注"
-          align="center"
-          prop="remark"
-          min-width="120"
-          show-overflow-tooltip
-        />
-      </el-table>
-
-      <div class="certificate-pagination">
-        <Pagination
-          :total="hazardDialogTotal"
-          v-model:page="hazardDialogQueryParams.pageNo"
-          v-model:limit="hazardDialogQueryParams.pageSize"
-          @pagination="getHazardDialogList"
-        />
-      </div>
-    </div>
-  </Dialog>
-
-  <Dialog v-model="dangerDialogVisible" :title="dangerDialogTitle" width="1400px">
-    <div class="certificate-warning-dialog">
-      <el-table
-        :loading="dangerDialogLoading"
-        :data="dangerDialogList"
-        height="400"
-        class="device-list-table production-brief-table"
-      >
-        <el-table-column label="序号" width="80" align="center">
-          <template #default="scope">
-            {{
-              (dangerDialogQueryParams.pageNo - 1) * dangerDialogQueryParams.pageSize +
-              scope.$index +
-              1
-            }}
-          </template>
-        </el-table-column>
-        <el-table-column
-          label="区域/位置"
-          align="center"
-          prop="region"
-          min-width="140"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="危险因素描述"
-          align="center"
-          prop="elementDescription"
-          min-width="180"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="可导致的后果"
-          align="center"
-          prop="maybeResult"
-          min-width="180"
-          show-overflow-tooltip
-        />
-        <el-table-column label="风险评价" align="center">
-          <el-table-column prop="evalKn" label="可能性 (L)" width="100" align="center">
-            <template #default="{ row }">
-              {{ row.evalKn }}
-            </template>
-          </el-table-column>
-          <el-table-column prop="evalYz" label="严重性 (S)" width="100" align="center">
-            <template #default="{ row }">
-              {{ row.evalYz }}
-            </template>
-          </el-table-column>
-          <el-table-column prop="evalFxz" label="风险值 (R)" width="100" align="center">
-            <template #default="{ row }">
-              {{ row.evalFxz }}
-            </template>
-          </el-table-column>
-          <el-table-column prop="riskGrade" label="风险等级" width="100" align="center">
-            <template #default="scope">
-              <dict-tag :type="DICT_TYPE.DANGER_GRADE" :value="scope.row.riskGrade" />
-            </template>
-          </el-table-column>
-        </el-table-column>
-        <el-table-column
-          label="控制措施"
-          align="center"
-          prop="controlMethod"
-          show-overflow-tooltip
-        />
-        <el-table-column
-          label="责任人"
-          align="center"
-          prop="charge"
-          min-width="100"
-          show-overflow-tooltip
-        />
-      </el-table>
-
-      <div class="certificate-pagination">
-        <Pagination
-          :total="dangerDialogTotal"
-          v-model:page="dangerDialogQueryParams.pageNo"
-          v-model:limit="dangerDialogQueryParams.pageSize"
-          @pagination="getDangerDialogList"
-        />
-      </div>
-    </div>
-  </Dialog>
-
-  <Dialog v-model="instrumentDialogVisible" :title="instrumentDialogTitle" width="1400px">
-    <div class="certificate-warning-dialog">
-      <el-table
-        :loading="instrumentDialogLoading"
-        :data="instrumentDialogList"
-        :row-style="tableRowStyle"
-        :row-class-name="tableRowClassName"
-        height="400"
-        class="device-list-table production-brief-table"
-      >
-        <el-table-column label="序号" width="80" align="center">
-          <template #default="scope">
-            {{
-              (instrumentDialogQueryParams.pageNo - 1) * instrumentDialogQueryParams.pageSize +
-              scope.$index +
-              1
-            }}
-          </template>
-        </el-table-column>
-        <el-table-column label="名称" align="center" prop="measureName" min-width="140" />
-        <el-table-column label="编码" align="center" prop="measureCode" min-width="120" />
-        <el-table-column label="序列号" align="center" prop="serialNo" min-width="120" />
-        <el-table-column label="部门名称" align="center" prop="deptName" min-width="140" />
-        <el-table-column label="计量单位" align="center" prop="measureUnit" min-width="100" />
-        <el-table-column label="责任人" align="center" prop="dutyPerson" min-width="100" />
-        <el-table-column label="品牌" align="center" prop="brand" min-width="100" />
-        <el-table-column label="规格型号" align="center" prop="modelName" min-width="120" />
-        <el-table-column label="分类" align="center" min-width="120">
-          <template #default="scope">
-            <dict-tag :type="DICT_TYPE.MEASURE_TYPE" :value="scope.row.classify" />
-          </template>
-        </el-table-column>
-        <el-table-column label="采购日期" align="center" min-width="120">
-          <template #default="scope">
-            {{ formatDateCorrectly(scope.row.buyDate) }}
-          </template>
-        </el-table-column>
-        <el-table-column label="价格" align="center" prop="measurePrice" min-width="100" />
-        <el-table-column
-          label="备注"
-          align="center"
-          prop="remark"
-          min-width="140"
-          show-overflow-tooltip
-        />
-      </el-table>
-
-      <div class="certificate-pagination">
-        <Pagination
-          :total="instrumentDialogTotal"
-          v-model:page="instrumentDialogQueryParams.pageNo"
-          v-model:limit="instrumentDialogQueryParams.pageSize"
-          @pagination="getInstrumentDialogList"
-        />
-      </div>
-    </div>
-  </Dialog>
-</template>
-
-<style lang="scss" scoped>
-@import url('@/styles/qhse_kb.scss');
-
-:deep(.el-select__wrapper) {
-  // height: 38px !important;
-  font-size: 14px !important;
-}
-.qhse-board {
-  color: #24364f;
-}
-
-.board-body {
-  padding: 5px 18px 24px;
-}
-
-.summary-toolbar {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  padding: 10px 18px 6px;
-}
-
-.summary-toolbar__tabs {
-  display: flex;
-  align-items: center;
-  gap: 12px;
-}
-
-.summary-toolbar__tab {
-  min-width: 120px;
-  height: 40px;
-  padding: 0 22px;
-  font-family: YouSheBiaoTiHei, sans-serif;
-  font-size: 17px;
-  color: #2b76e9;
-  background: linear-gradient(180deg, rgb(255 255 255 / 92%) 0%, rgb(228 239 255 / 90%) 100%);
-  border: 1px solid rgb(126 174 244 / 44%);
-  border-radius: 10px;
-  box-shadow:
-    inset 0 1px 0 rgb(255 255 255 / 96%),
-    0 8px 14px rgb(63 103 171 / 8%);
-  cursor: pointer;
-  transition:
-    transform 0.2s ease,
-    box-shadow 0.2s ease,
-    background 0.2s ease,
-    color 0.2s ease;
-}
-
-.summary-toolbar__tab.is-active {
-  color: #fff;
-  background: linear-gradient(180deg, #59a6ff 0%, #2d78ea 100%);
-  border-color: rgb(62 122 223 / 88%);
-  box-shadow:
-    inset 0 1px 0 rgb(255 255 255 / 22%),
-    0 10px 18px rgb(45 120 234 / 20%);
-}
-
-.summary-toolbar__tab:hover {
-  transform: translateY(-1px);
-}
-
-.summary-toolbar__date {
-  display: flex;
-  align-items: center;
-  gap: 10px;
-}
-
-.summary-toolbar__date-label {
-  font-size: 18px;
-  font-weight: 700;
-  color: #5c7393;
-}
-
-.bottom-card__action {
-  display: block;
-  width: 100%;
-  padding: 0;
-  text-align: left;
-  color: #617491;
-  background: transparent;
-  border: none;
-  cursor: pointer;
-  transition: color 0.2s ease;
-  font-size: 18px;
-  font-weight: 700;
-  color: #5c7393;
-}
-
-.bottom-card__action:hover {
-  color: #2d78ea;
-}
-
-.risk-card--clickable {
-  cursor: pointer;
-  transition:
-    transform 0.2s ease,
-    box-shadow 0.2s ease;
-}
-
-.risk-card--clickable:hover {
-  transform: translateY(-2px);
-}
-
-.summary-toolbar__picker {
-  width: 190px;
-}
-
-.summary-toolbar__picker :deep(.el-input__wrapper) {
-  min-height: 38px;
-  background: rgb(255 255 255 / 80%);
-  border: 1px solid rgb(137 176 235 / 38%);
-  border-radius: 10px;
-  font-size: 14px !important;
-  box-shadow:
-    inset 0 1px 0 rgb(255 255 255 / 92%),
-    0 8px 14px rgb(63 103 171 / 6%);
-}
-
-.summary-toolbar__picker :deep(.el-input__inner),
-.summary-toolbar__picker :deep(.el-range-input),
-.summary-toolbar__picker :deep(.el-input__prefix),
-.summary-toolbar__picker :deep(.el-input__suffix) {
-  font-size: 14px !important;
-}
-
-.summary-toolbar__picker :deep(.el-input__inner::placeholder),
-.summary-toolbar__picker :deep(.el-range-input::placeholder) {
-  font-size: 14px !important;
-}
-
-:deep(.summary-toolbar__picker-popper) {
-  font-size: 14px;
-}
-
-:deep(.summary-toolbar__picker-popper .el-date-table),
-:deep(.summary-toolbar__picker-popper .el-date-table th),
-:deep(.summary-toolbar__picker-popper .el-date-table td),
-:deep(.summary-toolbar__picker-popper .el-month-table),
-:deep(.summary-toolbar__picker-popper .el-year-table),
-:deep(.summary-toolbar__picker-popper .el-picker-panel__icon-btn),
-:deep(.summary-toolbar__picker-popper .el-date-picker__header-label) {
-  font-size: 14px !important;
-}
-
-.summary-panel {
-  padding: 0 10px 10px;
-  height: 200px;
-}
-
-.summary-grid {
-  display: grid;
-  grid-template-columns: repeat(6, minmax(0, 1fr));
-  gap: 24px;
-  margin-top: 10px;
-}
-
-.summary-tile {
-  display: flex;
-  height: 136px;
-  padding: 28px 18px 24px;
-  border-radius: 22px;
-  align-items: center;
-  gap: 22px;
-}
-
-.summary-card__content {
-  position: relative;
-  z-index: 2;
-  min-width: 0;
-  flex: 1;
-}
-
-.summary-tile__meta {
-  display: flex;
-  margin-top: 14px;
-  align-items: baseline;
-  gap: 8px;
-}
-
-.summary-tile__value {
-  font-family: YouSheBiaoTiHei, sans-serif;
-  font-size: 30px;
-  line-height: 1;
-  color: #114a9b;
-  letter-spacing: 1px;
-}
-
-.summary-tile__note {
-  font-size: 16px;
-  font-weight: 700;
-}
-
-.board-main {
-  display: grid;
-  grid-template-columns: 1.02fr 1fr 0.98fr;
-  gap: 28px;
-  margin-top: 10px;
-}
-
-.left-column,
-.right-column {
-  display: grid;
-  gap: 24px;
-  align-content: start;
-}
-.left-column {
-  height: 540px;
-}
-.board-panel {
-  min-height: 258px;
-}
-
-.board-panel.kb-stage-card:hover {
-  transform: none;
-}
-
-.board-panel--center {
-  min-height: 540px;
-  height: 540px;
-}
-
-.chart-panel {
-  margin-top: 18px;
-}
-
-.chart-panel--echart {
-  height: 188px;
-}
-
-.safe-day-chart-panel {
-  height: 230px;
-}
-
-.qhse-metric-tabs {
-  display: flex;
-  justify-content: left;
-  gap: 5px;
-  padding-left: 30px;
-}
-
-.qhse-metric-tab {
-  min-height: 24px;
-  padding: 5px 12px;
-  font-size: 13px;
-  font-weight: 700;
-  line-height: 1.35;
-  color: #4d6487;
-  text-align: center;
-  background: linear-gradient(180deg, rgb(255 255 255 / 86%) 0%, rgb(228 239 255 / 82%) 100%);
-  border: 1px solid rgb(118 167 238 / 28%);
-  border-radius: 14px;
-  box-shadow:
-    inset 0 1px 0 rgb(255 255 255 / 92%),
-    0 10px 16px rgb(52 94 164 / 8%);
-  cursor: pointer;
-  transition:
-    transform 0.2s ease,
-    box-shadow 0.2s ease,
-    color 0.2s ease,
-    background 0.2s ease,
-    border-color 0.2s ease;
-}
-
-.qhse-metric-tab:hover {
-  transform: translateY(-1px);
-  box-shadow:
-    inset 0 1px 0 rgb(255 255 255 / 94%),
-    0 12px 18px rgb(52 94 164 / 10%);
-}
-
-.qhse-metric-tab.is-active {
-  color: #fff;
-  background: linear-gradient(180deg, #63adff 0%, #347eea 100%);
-  border-color: rgb(62 122 223 / 76%);
-  box-shadow:
-    inset 0 1px 0 rgb(255 255 255 / 18%),
-    0 12px 20px rgb(45 120 234 / 22%);
-}
-
-.qhse-trend-chart-panel {
-  height: 220px;
-  // margin-top: 14px;
-}
-
-.soc-chart-panel {
-  height: 280px;
-}
-
-.panel-title--center {
-  justify-content: center;
-  padding-left: 0;
-  font-size: 32px;
-}
-
-.panel-title--center .icon-decorator {
-  left: 28px;
-}
-
-.risk-grid {
-  display: grid;
-  padding: 10px 24px 12px;
-  // margin-top: 18px;
-  grid-template-columns: repeat(2, minmax(0, 1fr));
-  gap: 10px 18px;
-}
-
-.risk-hazard-block {
-  padding: 0 24px 5px;
-  margin-top: 18px;
-}
-
-.risk-hazard-block__title {
-  margin-top: 0;
-}
-
-.risk-hazard-block__chart {
-  margin-top: 12px;
-}
-
-.risk-card {
-  min-height: 124px;
-  padding: 20px 18px;
-  background: linear-gradient(180deg, rgb(255 255 255 / 42%) 0%, rgb(220 232 250 / 28%) 100%);
-  border: 1px solid rgb(255 255 255 / 58%);
-  border-radius: 18px;
-  box-shadow:
-    inset 0 1px 0 rgb(255 255 255 / 74%),
-    0 8px 18px rgb(63 103 171 / 7%);
-}
-
-.risk-card__title {
-  display: flex;
-  font-size: 16px;
-  font-weight: 800;
-  align-items: center;
-  gap: 8px;
-}
-
-.risk-card__dot {
-  width: 14px;
-  height: 14px;
-  border-radius: 999px;
-  box-shadow: 0 0 0 4px rgb(255 255 255 / 35%);
-}
-
-.risk-card__desc {
-  margin-top: 18px;
-  font-size: 13px;
-  font-weight: 600;
-  color: #6f819a;
-  line-height: 1.35;
-}
-
-.qualification-panel {
-  display: grid;
-  margin-top: 26px;
-  padding: 28px;
-  grid-template-columns: 140px 1fr;
-  padding-left: 120px;
-  align-items: center;
-  gap: 26px;
-}
-
-.qualification-icon {
-  display: flex;
-  width: 132px;
-  height: 132px;
-  font-size: 74px;
-  color: #f09717;
-  background: radial-gradient(
-    circle at 50% 40%,
-    rgb(255 255 255 / 74%) 0%,
-    rgb(231 240 255 / 42%) 100%
-  );
-  border: 1px solid rgb(255 255 255 / 72%);
-  border-radius: 32px;
-  box-shadow:
-    inset 0 1px 0 rgb(255 255 255 / 85%),
-    0 10px 22px rgb(55 94 160 / 10%);
-  align-items: center;
-  justify-content: center;
-}
-
-.qualification-list {
-  display: grid;
-  gap: 22px;
-}
-
-.qualification-item {
-  font-size: 24px;
-  font-weight: 700;
-  color: #516785;
-}
-
-.qualification-item--clickable {
-  cursor: pointer;
-  transition:
-    transform 0.18s ease,
-    opacity 0.18s ease;
-}
-
-.qualification-item--clickable:hover {
-  transform: translateX(4px);
-  opacity: 0.9;
-}
-
-.qualification-item strong {
-  font-size: 28px;
-}
-
-.certificate-warning-dialog {
-  display: flex;
-  flex-direction: column;
-  gap: 14px;
-}
-
-.bottom-grid {
-  display: grid;
-  margin-top: 10px;
-  grid-template-columns: repeat(5, minmax(0, 1fr));
-  gap: 24px;
-}
-
-.bottom-card {
-  min-height: 180px;
-  padding: 15px 22px 0;
-  overflow: hidden;
-}
-
-.bottom-card__header {
-  display: flex;
-  align-items: center;
-  gap: 16px;
-}
-
-.bottom-card__icon {
-  width: 54px;
-  height: 54px;
-}
-
-.bottom-card__title {
-  font-family: YouSheBiaoTiHei, sans-serif;
-  font-size: 24px;
-  color: #114a9b;
-  letter-spacing: 1px;
-}
-
-.bottom-card__content {
-  position: relative;
-  z-index: 2;
-  margin-top: 18px;
-}
-
-.bottom-card__content p {
-  margin: 0;
-  font-size: 18px;
-  font-weight: 700;
-  color: #5d718e;
-  line-height: 1.7;
-}
-
-.certificate-board {
-  padding: 16px 18px 24px;
-}
-
-.certificate-filter-panel {
-  padding: 18px 20px 12px;
-}
-
-.certificate-filters {
-  display: grid;
-  grid-template-columns: repeat(4, minmax(0, 1fr)) auto;
-  gap: 18px 20px;
-  align-items: end;
-}
-
-.certificate-filter-item {
-  display: flex;
-  align-items: center;
-  gap: 14px;
-}
-
-.certificate-filter-item__label {
-  flex-shrink: 0;
-  font-size: 16px;
-  font-weight: 700;
-  color: #4f6583;
-}
-
-.certificate-filter-item__control {
-  flex: 1;
-  height: 42px;
-}
-
-.certificate-filter-item__control :deep(.el-select__wrapper),
-.certificate-filter-item__control :deep(.el-input__wrapper) {
-  height: 42px !important;
-  min-height: 42px !important;
-  border-radius: 12px;
-}
-
-.certificate-filter-actions :deep(.el-button) {
-  height: 42px;
-  line-height: 42px;
-  padding: 0 22px;
-  border-radius: 12px;
-}
-
-.certificate-filter-item__control :deep(.el-input__inner) {
-  height: 42px;
-  line-height: 42px;
-}
-
-.certificate-filter-actions {
-  display: flex;
-  align-items: center;
-  gap: 10px;
-  justify-content: flex-end;
-}
-
-.certificate-table-panel {
-  margin-top: 18px;
-  padding: 18px 18px 12px;
-  height: 860px;
-  display: flex;
-  flex-direction: column;
-}
-
-.certificate-table {
-  flex: 1;
-}
-
-.file-name-text {
-  display: block;
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-
-// :deep(.el-input__inner) {
-//   font-size: 14px !important;
-//   border: none !important;
-//   font-size: 14px !important;
-// }
-
-:deep(.datepicker .el-input__wrapper) {
-  background: transparent !important;
-  border: none !important;
-  font-size: 14px !important;
-}
-
-:deep(.el-input) {
-  border: none !important;
-  outline: none !important;
-  font-size: 14px !important;
-}
-
-:deep(.el-date-editor.el-input, .el-date-editor.el-input__inner) {
-  border: none !important;
-  font-size: 14px !important;
-}
-
-:deep(.el-date-editor.is-active) {
-  border-color: #ff6a00 !important;
-}
-
-.certificate-pagination {
-  padding-top: 0px;
-}
-
-.production-brief-table {
-  width: 100%;
-
-  :deep(.el-table__header-wrapper th.el-table__cell) {
-    font-size: calc(16px * var(--kb-scale, 1));
-    line-height: 1.2;
-  }
-
-  :deep(.el-table__body td.el-table__cell) {
-    padding: calc(7px * var(--kb-scale, 1)) 0;
-    font-size: calc(14px * var(--kb-scale, 1));
-  }
-}
-
-.summary-toolbar__picker {
-  :deep(*) {
-    font-size: 14px !important;
-  }
-
-  :deep(input) {
-    font-size: 14px !important;
-    line-height: 14px !important;
-  }
-}
-
-/* 过期行的红色背景 - 基础状态 */
-:deep(.el-table__body tr.expired-row > td.el-table__cell) {
-  background-color: #ffe6e6 !important;
-}
-
-/* 过期行 - 鼠标悬浮状态 */
-:deep(.el-table__body tr.expired-row:hover > td.el-table__cell) {
-  background-color: #ffcccc !important;
-}
-
-/* 确保斑马纹不影响过期行 */
-:deep(.el-table__body tr.expired-row.el-table__row--striped > td.el-table__cell) {
-  background-color: #ffe6e6 !important;
-}
-
-:deep(.el-table__body tr.expired-row.el-table__row--striped:hover > td.el-table__cell) {
-  background-color: #ffcccc !important;
-}
-
-/* 预警行的橙色背景 - 基础状态 */
-:deep(.el-table__body tr.alert-warn-row > td.el-table__cell) {
-  background-color: #fff1df !important;
-}
-
-/* 预警行 - 鼠标悬浮状态 */
-:deep(.el-table__body tr.alert-warn-row:hover > td.el-table__cell) {
-  background-color: #ffe2bf !important;
-}
-
-/* 确保斑马纹不影响预警行 */
-:deep(.el-table__body tr.alert-warn-row.el-table__row--striped > td.el-table__cell) {
-  background-color: #fff1df !important;
-}
-
-:deep(.el-table__body tr.alert-warn-row.el-table__row--striped:hover > td.el-table__cell) {
-  background-color: #ffe2bf !important;
-}
-</style>