2025-08-26 16:09:27 +08:00
|
|
|
|
<template>
|
2025-08-28 10:33:55 +08:00
|
|
|
|
<SmallBorder :titleText="styleConfig.titleText" :title-option="styleConfig.titleOption"
|
|
|
|
|
:headerOption="styleConfig.headerOption">
|
|
|
|
|
<div class="content_box">
|
|
|
|
|
<vue3-seamless-scroll v-if="option.dataset && option.dataset.length > 0" class="seamless" :list="option.dataset"
|
|
|
|
|
:limitScrollNum="rowNum" :hover="true" :step="waitTime" :wheel="true" :isWatch="true">
|
|
|
|
|
<div v-for="(item, index) in option.dataset" class="detail flex_column" :key="index">
|
|
|
|
|
<div class="flex_v cursor" :style="!showRankNum ? { paddingLeft: 0 } : { paddingLeft: '24px' }"
|
|
|
|
|
@click="handleOpenDialog(item)">
|
|
|
|
|
<div v-if="index === 0 && showRankNum" class="levelOneIcon flex_c">{{ index + 1 }}</div>
|
|
|
|
|
<div v-else-if="index === 1 && showRankNum" class="levelTwoIcon flex_c">{{ index + 1 }}</div>
|
|
|
|
|
<div v-else-if="showRankNum" class="levelOtherIcon flex_c">{{ index + 1 }}</div>
|
|
|
|
|
<div class="item_content">
|
|
|
|
|
<div class="item_header flex">
|
|
|
|
|
<div class="item_level_text">{{ item.alarmLevel }}</div>
|
|
|
|
|
<div class="item_title_text">{{ item.alarmDescname }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="item_footer">
|
|
|
|
|
<div class="item_dept">{{ item.compName }}</div>
|
|
|
|
|
<div class="item_time">{{ item.alarmTime }}</div>
|
|
|
|
|
</div>
|
2025-08-26 16:09:27 +08:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-08-28 10:33:55 +08:00
|
|
|
|
</vue3-seamless-scroll>
|
|
|
|
|
<div v-else style="
|
2025-08-26 16:09:27 +08:00
|
|
|
|
color: #fff;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
">
|
2025-08-28 10:33:55 +08:00
|
|
|
|
暂无数据
|
2025-08-26 16:09:27 +08:00
|
|
|
|
</div>
|
2025-08-28 10:33:55 +08:00
|
|
|
|
<n-modal v-model:show="status.showDialog" :class="['custom-tab-modal']" title="报警详情" preset="card"
|
|
|
|
|
@on-after-leave="handleCloseDialog" :draggable="{ bounds: 'none' }"
|
|
|
|
|
:style="{ width: '800px', height: '516px' }">
|
|
|
|
|
<div v-if="isLoading" style="display: flex; width: 100%; justify-content: center; margin-top: 16px">
|
|
|
|
|
<n-spin size="large" />
|
|
|
|
|
</div>
|
|
|
|
|
<n-grid v-else cols="24" x-gap="12" style="overflow-y: auto; padding-bottom: 12px">
|
|
|
|
|
<!-- <div class="enterBtn" @click="handleMeetingBtnClick">会商</div> -->
|
|
|
|
|
<!-- 左侧详情 -->
|
|
|
|
|
<n-gi :span="14">
|
|
|
|
|
<!-- 基础信息 -->
|
|
|
|
|
<div class="detail-item">
|
|
|
|
|
<label>报警时间:</label>
|
|
|
|
|
<span>{{ convertTimestampToDateTime((status.selectedRow as any)?.alarmRecord.alarmTime) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="detail-item">
|
|
|
|
|
<label>处置状态:</label>
|
|
|
|
|
<span style="color: #ff9100">{{ (status.selectedRow as any)?.handleStatus }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="detail-item">
|
|
|
|
|
<label>风险等级:</label>
|
|
|
|
|
<span :style="{ color: '' }">{{ (status.selectedRow as any)?.alarmRecord.riskIdentName }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="detail-item">
|
|
|
|
|
<label>报警等级:</label>
|
|
|
|
|
<span class="highlight" style="color: #ff5454">{{
|
|
|
|
|
(status.selectedRow as any)?.alarmRecord.alarmLevel
|
|
|
|
|
}}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="detail-item">
|
|
|
|
|
<label>风险描述:</label>
|
|
|
|
|
<span class="highlight">{{ (status.selectedRow as any)?.alarmRecord.alarmDesc }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="detail-item">
|
|
|
|
|
<label>报警区域:</label>
|
|
|
|
|
<span>{{
|
|
|
|
|
(status.selectedRow as any)?.pathName
|
|
|
|
|
.substring(1, (status.selectedRow as any)?.pathName.length)
|
|
|
|
|
.replaceAll('/', '-') + (((status.selectedRow as any)?.subareaName && (status.selectedRow as
|
|
|
|
|
any)?.subareaName !== '') ? (status.selectedRow as any)?.subareaName : '')
|
|
|
|
|
}}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="detail-item" style="display: flex; flex-direction: column"
|
|
|
|
|
v-if="(status.selectedRow as any)?.camerainfos.length > 0">
|
|
|
|
|
<label>警情关联设备:</label>
|
|
|
|
|
<div class="screenshot-placeholder">
|
|
|
|
|
<n-tabs type="line" animated>
|
|
|
|
|
<n-tab-pane v-for="(item, index) in (status.selectedRow as any)?.camerainfos" :name="item.deviceCode"
|
|
|
|
|
:tab="item.deviceName">
|
|
|
|
|
<div class="detail-item" style="display: flex; flex-direction: column">
|
|
|
|
|
<label>报警截图:</label>
|
|
|
|
|
<div style="display: flex">
|
|
|
|
|
<img :src="'/awimg/' + removeProtocolIpPort((status.selectedRow as any)?.alarmRecord.originImg)"
|
|
|
|
|
style="width: 180px; margin-right: 16px" />
|
|
|
|
|
<img :src="'/awimg/' + removeProtocolIpPort((status.selectedRow as any)?.alarmRecord.alarmImg)"
|
|
|
|
|
style="width: 180px" />
|
|
|
|
|
</div>
|
2025-08-26 17:36:59 +08:00
|
|
|
|
</div>
|
2025-08-28 10:33:55 +08:00
|
|
|
|
<div class="detail-item" style="display: flex; flex-direction: column">
|
|
|
|
|
<label>警情录像回放:</label>
|
|
|
|
|
<div class="screenshot-placeholder playback-placeholder" style="width: 350px; height: 200px">
|
|
|
|
|
<play-back :device-id="item.deviceCode"
|
|
|
|
|
:start="new Date((status.selectedRow as any)?.startTime)"
|
|
|
|
|
:end="new Date((status.selectedRow as any)?.endTime)"></play-back>
|
|
|
|
|
</div>
|
2025-08-26 17:36:59 +08:00
|
|
|
|
</div>
|
2025-08-28 10:33:55 +08:00
|
|
|
|
<div class="detail-item" style="display: flex; flex-direction: column">
|
|
|
|
|
<label>视频实时画面:</label>
|
|
|
|
|
<div class="screenshot-placeholder playback-placeholder" style="width: 350px; height: 200px">
|
|
|
|
|
<play-live :device-id="item.deviceCode"></play-live>
|
|
|
|
|
</div>
|
2025-08-26 17:36:59 +08:00
|
|
|
|
</div>
|
2025-08-28 10:33:55 +08:00
|
|
|
|
</n-tab-pane>
|
|
|
|
|
</n-tabs>
|
|
|
|
|
<br />
|
|
|
|
|
</div>
|
2025-08-26 17:36:59 +08:00
|
|
|
|
</div>
|
2025-08-28 10:33:55 +08:00
|
|
|
|
</n-gi>
|
|
|
|
|
<n-gi :span="10" style="height: 100%; display: flex; align-items: center">
|
|
|
|
|
<n-timeline size="medium">
|
|
|
|
|
<n-timeline-item class="timeLineClass" v-for="(item, index) in (status.selectedRow as any)?.taskLogs"
|
|
|
|
|
:key="index" type="info" :title="item.task_name" :content="item.task_detail" :time="item.task_begin" />
|
|
|
|
|
</n-timeline>
|
|
|
|
|
</n-gi>
|
|
|
|
|
</n-grid>
|
|
|
|
|
</n-modal>
|
|
|
|
|
</div>
|
|
|
|
|
</SmallBorder>
|
2025-08-26 16:09:27 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
// @ts-nocheck
|
|
|
|
|
|
2025-08-28 10:33:55 +08:00
|
|
|
|
import { PropType, toRefs, shallowReactive, watch, computed, ref, reactive, onMounted, onUnmounted } from 'vue'
|
2025-08-26 16:09:27 +08:00
|
|
|
|
import { CreateComponentType } from '@/packages/index.d'
|
|
|
|
|
import { useChartDataFetch } from '@/hooks'
|
|
|
|
|
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
|
2025-08-28 10:33:55 +08:00
|
|
|
|
import { option as configOption, styleConfig } from './config'
|
2025-08-26 16:09:27 +08:00
|
|
|
|
import { Vue3SeamlessScroll } from 'vue3-seamless-scroll'
|
|
|
|
|
import PlayBack from '@/components/Pages/yushiVideo/playback.vue'
|
|
|
|
|
import PlayLive from '@/components/Pages/yushiVideo/playLive.vue'
|
|
|
|
|
import axiosInstance from '@/api/axios'
|
|
|
|
|
import VChart from 'vue-echarts'
|
2025-08-28 10:33:55 +08:00
|
|
|
|
import SmallBorder from '../SmallBorder01Co/index.vue'
|
|
|
|
|
import { height } from 'dom-helpers'
|
2025-09-02 14:39:25 +08:00
|
|
|
|
import axios from 'axios'
|
2025-08-26 16:09:27 +08:00
|
|
|
|
const props = defineProps({
|
|
|
|
|
chartConfig: {
|
|
|
|
|
type: Object as PropType<CreateComponentType & typeof option>,
|
|
|
|
|
required: true
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
const isLoading = ref(false)
|
|
|
|
|
const pointChartOption = ref(null)
|
|
|
|
|
const status = reactive({
|
|
|
|
|
showDialog: false,
|
|
|
|
|
selectedRow: null
|
|
|
|
|
})
|
|
|
|
|
const { rowNum, waitTime, showRankNum } = toRefs(props.chartConfig.option)
|
|
|
|
|
const { w, h } = toRefs(props.chartConfig.attr)
|
|
|
|
|
|
2025-08-27 15:25:37 +08:00
|
|
|
|
|
2025-08-26 16:09:27 +08:00
|
|
|
|
const option = shallowReactive({
|
|
|
|
|
dataset: configOption.dataset
|
|
|
|
|
})
|
|
|
|
|
|
2025-08-27 15:25:37 +08:00
|
|
|
|
|
|
|
|
|
|
2025-08-26 17:36:59 +08:00
|
|
|
|
const handleChangeDevice = (value: string | number) => {
|
|
|
|
|
status.selectedRow.cameraId = value
|
|
|
|
|
// if (status.selectedRow.alarmGroup.find(item => item.cameraId === value)) {
|
|
|
|
|
// status.selectedRow.startTime = status.alarmGroup.find(item => item.cameraId === value).startTime
|
|
|
|
|
// status.selectedRow.endTime = status.alarmGroup.find(item => item.cameraId === value).endTime
|
|
|
|
|
// }
|
2025-08-26 16:09:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 17:36:59 +08:00
|
|
|
|
const handleOpenDialog = async (row: any) => {
|
|
|
|
|
const alarmUUID = row.alarmUUID
|
|
|
|
|
|
|
|
|
|
status.showDialog = true
|
|
|
|
|
isLoading.value = true
|
|
|
|
|
//@ts-ignore
|
|
|
|
|
axiosInstance({
|
|
|
|
|
method: 'GET',
|
|
|
|
|
url: `${window.htconfig.API_IP}aw/bigScreen/qisuo/alarmRecordDetail/${alarmUUID}`,
|
|
|
|
|
|
|
|
|
|
responseType: 'json'
|
|
|
|
|
}).then((res: any) => {
|
|
|
|
|
//@ts-ignore
|
|
|
|
|
isLoading.value = false
|
|
|
|
|
|
|
|
|
|
const result = res.value
|
|
|
|
|
|
|
|
|
|
if (result) {
|
|
|
|
|
//@ts-ignore
|
|
|
|
|
status.selectedRow = {
|
|
|
|
|
...result,
|
|
|
|
|
cameraId: result.camerainfos[0]?.deviceCode,
|
|
|
|
|
startTime: (result.alarmRecord.alarmTime - 15 * 1000),
|
|
|
|
|
endTime: (result.alarmRecord.alarmTime + 15 * 1000)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
//@ts-ignore
|
|
|
|
|
}
|
2025-08-26 16:09:27 +08:00
|
|
|
|
|
|
|
|
|
// 手动更新
|
|
|
|
|
watch(
|
|
|
|
|
() => props.chartConfig.option.dataset,
|
|
|
|
|
(newData: any) => {
|
|
|
|
|
option.dataset = newData
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
immediate: true,
|
|
|
|
|
deep: false
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 预览更新
|
|
|
|
|
useChartDataFetch(props.chartConfig, useChartEditStore, (newData: any) => {
|
|
|
|
|
option.dataset = newData
|
|
|
|
|
})
|
|
|
|
|
|
2025-08-26 17:36:59 +08:00
|
|
|
|
const removeProtocolIpPort = (url) => {
|
|
|
|
|
// 使用正则表达式匹配并移除协议、IP地址和端口
|
|
|
|
|
return url.replace(/^(?:https?:\/\/)?(?:[\d.]+|\[[\da-fA-F:.]+\])(?::\d+)?\/?/, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const convertTimestampToTime = timestamp => {
|
|
|
|
|
const date = new Date(timestamp)
|
|
|
|
|
const hours = date.getHours().toString().padStart(2, '0')
|
|
|
|
|
const minutes = date.getMinutes().toString().padStart(2, '0')
|
|
|
|
|
const seconds = date.getSeconds().toString().padStart(2, '0')
|
|
|
|
|
|
|
|
|
|
return `${hours}:${minutes}:${seconds}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const convertTimestampToDateTime = timestamp => {
|
|
|
|
|
const date = new Date(timestamp)
|
|
|
|
|
const year = date.getFullYear()
|
|
|
|
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
|
|
const day = String(date.getDate()).padStart(2, '0')
|
|
|
|
|
const hours = date.getHours().toString().padStart(2, '0')
|
|
|
|
|
const minutes = date.getMinutes().toString().padStart(2, '0')
|
|
|
|
|
const seconds = date.getSeconds().toString().padStart(2, '0')
|
|
|
|
|
|
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 16:09:27 +08:00
|
|
|
|
const handleCloseDialog = () => {
|
|
|
|
|
status.showDialog = false
|
|
|
|
|
status.selectedRow = null
|
|
|
|
|
}
|
2025-08-28 18:56:34 +08:00
|
|
|
|
/**
|
|
|
|
|
* 格式化单个报警项的函数
|
|
|
|
|
* @param {Object} item 原始报警数据项
|
|
|
|
|
* @returns {Object} 格式化后的报警数据项
|
|
|
|
|
*/
|
|
|
|
|
const formatAlarmItem = (item) => {
|
|
|
|
|
let newAlarmDescname = item.alarmDescname;
|
|
|
|
|
const dateTimeRegexInDesc = /于\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
|
|
|
|
|
if (newAlarmDescname) {
|
|
|
|
|
newAlarmDescname = newAlarmDescname.replace(dateTimeRegexInDesc, '');
|
|
|
|
|
}
|
|
|
|
|
let newAlarmLevel = item.alarmLevel;
|
|
|
|
|
if (typeof newAlarmLevel === 'string') {
|
|
|
|
|
newAlarmLevel = newAlarmLevel.replace('风险', '');
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
...item,
|
|
|
|
|
alarmTime: convertTimestampToDateTime(item.alarmTime), // 格式化 alarmTime
|
|
|
|
|
alarmDescname: newAlarmDescname,
|
|
|
|
|
alarmLevel: newAlarmLevel
|
|
|
|
|
};
|
|
|
|
|
};
|
2025-08-27 15:25:37 +08:00
|
|
|
|
const fetchRecentAlarms = async () => {
|
|
|
|
|
try {
|
2025-09-02 14:24:21 +08:00
|
|
|
|
isLoading.value = true
|
2025-09-02 14:39:25 +08:00
|
|
|
|
const res = await axiosInstance.get('/awjt/space/getRecentAlarms', {responseType: 'json', baseURL: '' });
|
2025-09-02 14:24:21 +08:00
|
|
|
|
if (res.state === true) {
|
2025-08-27 15:25:37 +08:00
|
|
|
|
let rawData = [];
|
2025-09-02 14:24:21 +08:00
|
|
|
|
if (Array.isArray(res.value.items)) {
|
|
|
|
|
rawData = res.value.items;
|
|
|
|
|
} else if (Array.isArray(res.value)) {
|
|
|
|
|
rawData = res.value;
|
2025-08-27 15:25:37 +08:00
|
|
|
|
}
|
2025-08-28 18:56:34 +08:00
|
|
|
|
option.dataset = rawData.map(formatAlarmItem);
|
2025-08-27 15:25:37 +08:00
|
|
|
|
} else {
|
2025-08-28 18:56:34 +08:00
|
|
|
|
console.warn('API返回数据格式不符合预期,将保留原有数据并尝试重新格式化:', res);
|
|
|
|
|
if (Array.isArray(option.dataset)) {
|
|
|
|
|
option.dataset = option.dataset.map(formatAlarmItem);
|
|
|
|
|
}
|
2025-08-27 15:25:37 +08:00
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
2025-08-28 18:56:34 +08:00
|
|
|
|
console.error('获取最近报警数据失败,将保留原有数据并尝试重新格式化:', error);
|
|
|
|
|
if (Array.isArray(option.dataset)) {
|
|
|
|
|
option.dataset = option.dataset.map(formatAlarmItem);
|
|
|
|
|
}
|
2025-08-27 15:25:37 +08:00
|
|
|
|
} finally {
|
|
|
|
|
isLoading.value = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let alarmTimer = null;
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
fetchRecentAlarms();
|
|
|
|
|
// 每5分钟执行一次
|
|
|
|
|
const interval = 5 * 60 * 1000;
|
|
|
|
|
alarmTimer = setInterval(fetchRecentAlarms, interval);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
clearInterval(alarmTimer);
|
|
|
|
|
});
|
|
|
|
|
|
2025-08-26 16:09:27 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.content_box {
|
2025-08-28 10:33:55 +08:00
|
|
|
|
z-index: -1;
|
|
|
|
|
|
2025-08-26 16:09:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.seamless {
|
2025-08-28 10:33:55 +08:00
|
|
|
|
z-index: 1;
|
|
|
|
|
height: 240px;
|
2025-08-28 18:56:34 +08:00
|
|
|
|
margin: 0px 10px 30px 10px;
|
2025-08-28 10:33:55 +08:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
2025-08-26 16:09:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail {
|
|
|
|
|
color: #fff;
|
|
|
|
|
position: relative;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.flex_column {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.flex {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.flex_wrap {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.flex_v {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2025-08-28 10:33:55 +08:00
|
|
|
|
// background-color: #0a0c1260;
|
2025-08-26 16:09:27 +08:00
|
|
|
|
border-bottom: 1.5px solid #123E54;
|
2025-08-28 10:33:55 +08:00
|
|
|
|
margin: 3px;
|
2025-08-26 16:09:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item_level_text {
|
|
|
|
|
width: fit-content;
|
2025-08-28 18:56:34 +08:00
|
|
|
|
min-width: 2em;
|
2025-08-26 16:09:27 +08:00
|
|
|
|
height: 18px;
|
|
|
|
|
color: #ff9191;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
background: #B10000;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
|
|
|
|
line-height: 18px;
|
|
|
|
|
padding: 2px 12px;
|
|
|
|
|
margin-right: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-27 15:25:37 +08:00
|
|
|
|
// .item_title_text {
|
|
|
|
|
// color: #b10000;
|
|
|
|
|
// font-size: 18px;
|
|
|
|
|
// }
|
2025-08-26 16:09:27 +08:00
|
|
|
|
.item_title_text {
|
|
|
|
|
color: #b10000;
|
|
|
|
|
font-size: 18px;
|
2025-08-27 15:25:37 +08:00
|
|
|
|
white-space: nowrap;
|
|
|
|
|
/* 禁止换行 */
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
/* 隐藏超出部分 */
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
/* 显示省略号 */
|
|
|
|
|
flex-grow: 1; // 允许标题占据剩余空间
|
2025-08-26 16:09:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.flex_c {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 17:36:59 +08:00
|
|
|
|
.levelOneIcon {
|
|
|
|
|
width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
background: url(/src/assets/images/chart/tables/list_scroll_1_icon.png) 50% 50% / contain no-repeat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.levelTwoIcon {
|
|
|
|
|
width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
background: url(/src/assets/images/chart/tables/list_scroll_2_icon.png) 50% 50% / contain no-repeat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.levelOtherIcon {
|
|
|
|
|
width: 18px;
|
|
|
|
|
height: 18px;
|
|
|
|
|
background: url(/src/assets/images/chart/tables/list_scroll_3_icon.png) 50% 50% / contain no-repeat;
|
|
|
|
|
}
|
2025-08-26 16:09:27 +08:00
|
|
|
|
|
|
|
|
|
.item_content {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item_header {
|
2025-08-28 19:45:19 +08:00
|
|
|
|
margin-left: 10px;
|
2025-08-26 16:09:27 +08:00
|
|
|
|
flex: 1;
|
2025-08-27 15:25:37 +08:00
|
|
|
|
width: fit-content;
|
|
|
|
|
max-width: 450px;
|
2025-08-26 16:09:27 +08:00
|
|
|
|
// height: 30px;
|
|
|
|
|
align-items: end;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
/* 禁止换行 */
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
/* 隐藏超出部分 */
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
/* 显示省略号 */
|
|
|
|
|
margin-top: 2px;
|
2025-08-27 15:25:37 +08:00
|
|
|
|
// background-color: #123E54;
|
2025-08-26 16:09:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item_footer {
|
|
|
|
|
margin-left: 20px;
|
|
|
|
|
display: flex;
|
|
|
|
|
width: 100%;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item_time {
|
2025-08-28 19:45:19 +08:00
|
|
|
|
// margin-right: 70px;
|
|
|
|
|
padding-right: 30px;
|
2025-08-26 16:09:27 +08:00
|
|
|
|
font-size: 18px;
|
|
|
|
|
color: #7E8990;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.item_dept {
|
|
|
|
|
color: #7E8990;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail-item {
|
|
|
|
|
padding: 8px 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cursor {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail-item label {
|
|
|
|
|
min-width: 90px;
|
|
|
|
|
color: rgb(145 187 242);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.detail-item span {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info {
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
width: fit-content;
|
|
|
|
|
margin: 0 16px 0 0;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info .sub-title {
|
|
|
|
|
padding: 0 0 0 24px;
|
2025-08-26 17:36:59 +08:00
|
|
|
|
background: url(./second_level.png) 0 50%/425px 31px no-repeat;
|
2025-08-26 16:09:27 +08:00
|
|
|
|
margin-right: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info .info-num {
|
|
|
|
|
color: rgb(34 211 238);
|
|
|
|
|
font-size: 1.875rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info .info-body {
|
|
|
|
|
padding-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.enterBtn {
|
|
|
|
|
color: rgb(62 200 244);
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 1.25rem;
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 87%;
|
|
|
|
|
top: -45px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<style lang="scss">
|
|
|
|
|
.custom-tab-modal>.n-card-header {
|
|
|
|
|
background-color: rgba(26, 56, 113, 1) !important;
|
|
|
|
|
background-image: linear-gradient(to right, rgba(8, 100, 177, 0.7), transparent) !important;
|
|
|
|
|
padding: 16px !important;
|
|
|
|
|
border: 2px solid rgba(62, 200, 244, 1);
|
|
|
|
|
border-bottom-width: 0px !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.custom-tab-modal>.n-card__content {
|
|
|
|
|
background-color: rgba(26, 56, 113, 1) !important;
|
|
|
|
|
border: 2px solid rgba(62, 200, 244, 1);
|
|
|
|
|
padding-right: 0;
|
|
|
|
|
padding-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
|
|
|
background-color: #2461db;
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.custom-tab-modal .timeLineClass>div:last-child {
|
|
|
|
|
padding-left: 8px;
|
2025-08-26 17:36:59 +08:00
|
|
|
|
background: url(./text_bg.png) 0 50%/100% 100% no-repeat;
|
2025-08-26 16:09:27 +08:00
|
|
|
|
}
|
|
|
|
|
</style>
|