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