新增:对话列表新增关键词高亮
- 新增:对话列表新增关键词高亮 - 新增:对话列表中含有关键词的对话使用淡红色背景 - 修改:优化次级信息配色 - 修改:优化隐患结果页右侧边栏滚动条效果 - 修改:优化隐患结果页左侧边栏滚动条效果 - 修改:视频背景改为黑色
This commit is contained in:
parent
14bc8ffbf7
commit
b477db4bd2
|
|
@ -0,0 +1,2 @@
|
|||
拍一下,
|
||||
现场整改,
|
||||
|
|
|
@ -41,6 +41,7 @@ declare module 'vue' {
|
|||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElSegmented: typeof import('element-plus/es')['ElSegmented']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSelectV2: typeof import('element-plus/es')['ElSelectV2']
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { CaretRight, Location } from '@element-plus/icons-vue'
|
||||
import { ref, shallowRef, watch } from 'vue'
|
||||
import { computed, ref, shallowRef, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
|
|
@ -15,6 +15,10 @@ const props = defineProps({
|
|||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
keywords: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['click', 'play'])
|
||||
|
|
@ -28,6 +32,22 @@ function handlePlayClick(e: Event, item: any[], index: number) {
|
|||
emit('play', item, index)
|
||||
}
|
||||
|
||||
const keywordList = computed(() => props.keywords as string[])
|
||||
|
||||
function highlightText(text: string): string {
|
||||
const kws = keywordList.value
|
||||
if (!kws.length || !text)
|
||||
return text
|
||||
let result = text
|
||||
for (const kw of kws) {
|
||||
if (!kw)
|
||||
continue
|
||||
const regex = new RegExp(`(${kw})`, 'gi')
|
||||
result = result.replace(regex, '<span class="keyword-highlight">$1</span>')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const dataCache = shallowRef<[number, string, string, string][]>([])
|
||||
const indexMap = ref(new Map<number, number[]>())
|
||||
const sortedTimes = ref<number[]>([])
|
||||
|
|
@ -37,6 +57,7 @@ watch(() => props.data, (newData) => {
|
|||
dataCache.value = arr
|
||||
const map = new Map<number, number[]>()
|
||||
const times: number[] = []
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const t = (arr[i][0] * 10) | 0
|
||||
if (!map.has(t)) {
|
||||
|
|
@ -51,6 +72,30 @@ watch(() => props.data, (newData) => {
|
|||
}, { immediate: true })
|
||||
|
||||
const currentHighlight = ref(-1)
|
||||
const keywordItemSet = ref(new Set<number>())
|
||||
|
||||
watch(keywordList, () => {
|
||||
const arr = dataCache.value
|
||||
const kws = keywordList.value
|
||||
keywordItemSet.value = new Set<number>()
|
||||
|
||||
if (!kws.length || !arr.length)
|
||||
return
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i][3]) {
|
||||
for (const kw of kws) {
|
||||
if (!kw)
|
||||
continue
|
||||
const regex = new RegExp(kw, 'i')
|
||||
if (regex.test(arr[i][3])) {
|
||||
keywordItemSet.value.add(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => props.currentTime, (t) => {
|
||||
const time = (t * 10) | 0
|
||||
|
|
@ -85,22 +130,26 @@ watch(() => props.currentTime, (t) => {
|
|||
<!-- 滚动列表区域 -->
|
||||
<el-row style="flex: 1; overflow-y: auto;">
|
||||
<el-col>
|
||||
<el-row v-for="(item, index) in props.data" :key="index" class="message-item" :class="{ highlighted: currentHighlight === index }">
|
||||
<el-row v-for="(item, index) in props.data" :key="index" class="message-item" :class="{ 'highlighted': currentHighlight === index, 'keyword-highlighted': keywordItemSet.has(index) }">
|
||||
<div class="message-content" @click="handleItemClick(item as any[], index)">
|
||||
<div class="flex flex-col items-start">
|
||||
<div class="flex flex-row gap-2">
|
||||
<el-text class="item-text" type="info" size="small">
|
||||
<el-text class="item-text info-text" type="info" size="small">
|
||||
{{ (item as any[])[1] }}
|
||||
</el-text>
|
||||
<el-text class="item-text" type="info" size="small">
|
||||
<el-text class="item-text info-text" type="info" size="small">
|
||||
{{ (item as any[])[2] }}
|
||||
</el-text>
|
||||
</div>
|
||||
<el-text class="item-text">
|
||||
{{ (item as any[])[3] }}
|
||||
<div class="item-text">
|
||||
<el-text>
|
||||
<template #default>
|
||||
<span v-html="highlightText((item as any[])[3])" />
|
||||
</template>
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hover-actions">
|
||||
<el-tooltip content="定位" placement="top" effect="light">
|
||||
<el-button
|
||||
|
|
@ -150,6 +199,10 @@ watch(() => props.currentTime, (t) => {
|
|||
background-color: var(--ep-color-primary-light-8) !important;
|
||||
}
|
||||
|
||||
.message-item.keyword-highlighted {
|
||||
background-color: var(--ep-color-danger-light-9);
|
||||
}
|
||||
|
||||
.message-content {
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
|
|
@ -171,31 +224,16 @@ watch(() => props.currentTime, (t) => {
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.message-btn {
|
||||
width: 100%;
|
||||
padding: 4px 0.75rem;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
height: auto;
|
||||
min-height: 28px;
|
||||
line-height: normal;
|
||||
:deep(.keyword-highlight) {
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.message-title {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.item-text.ep-text {
|
||||
align-self: start;
|
||||
line-height: 1.5;
|
||||
.info-text {
|
||||
color: var(--ep-color-info-light-3) !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ const resultData = ref<ResultData>({
|
|||
})
|
||||
|
||||
const videoRef = ref<HTMLVideoElement>()
|
||||
const keywords = ref<string[]>([])
|
||||
const data = ref<DataFormat>({
|
||||
隐患列表: [],
|
||||
隐患范围字典: {},
|
||||
|
|
@ -322,7 +323,16 @@ function getFieldValue(field: typeof hazardFields[0]) {
|
|||
return field.transform ? field.transform(value) : (value || '无')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await fetch('/data/keyword.csv')
|
||||
const text = await res.text()
|
||||
keywords.value = text.split('\n').map(k => k.trim().replace(/,$/, '')).filter(k => k)
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Failed to load keywords:', e)
|
||||
}
|
||||
|
||||
// 从路由参数中获取数据
|
||||
const vidFile = router.currentRoute.value.query.vid_file as string
|
||||
if (vidFile) {
|
||||
|
|
@ -394,9 +404,11 @@ onMounted(() => {
|
|||
<el-container style="flex: 1; min-height: 0;">
|
||||
<el-aside width="200px" style="border-right: 1px solid var(--ep-border-color);">
|
||||
<!-- <el-row class="flex flex-col" style="border-bottom: 1px solid var(--ep-border-color);"> -->
|
||||
<el-scrollbar>
|
||||
<el-row class="flex flex-col">
|
||||
<ItemList title="隐患" :data="data.隐患列表" @click="handleHazardClick" />
|
||||
</el-row>
|
||||
</el-scrollbar>
|
||||
<!-- <el-row style="flex: 1; overflow-y: auto;">
|
||||
<ItemList title="物体列表" :data="data.物体列表" />
|
||||
</el-row> -->
|
||||
|
|
@ -427,6 +439,7 @@ onMounted(() => {
|
|||
</el-container>
|
||||
<el-aside style="width: 300px;">
|
||||
<!-- <el-row style="border-bottom: 1px solid var(--ep-border-color);"> -->
|
||||
<el-scrollbar>
|
||||
<el-row class="px-3 py-2">
|
||||
<el-col v-if="selectedHazard >= 0">
|
||||
<template v-for="group in groupedFields" :key="group[0].group">
|
||||
|
|
@ -464,11 +477,12 @@ onMounted(() => {
|
|||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="data.对话列表.length > 0" style="flex: 1; overflow-y: auto; border-top: 1px solid var(--ep-border-color);">
|
||||
<el-row v-if="data.对话列表.length > 0" style="height: calc(100% - 400px); flex: auto; border-top: 1px solid var(--ep-border-color);">
|
||||
<SubtitleList
|
||||
title="对话"
|
||||
:data="data.对话列表"
|
||||
:current-time="videoCurrentTime"
|
||||
:keywords="keywords"
|
||||
@click="(item: any[]) => handleJumpToTimePoint(Number(item[0]))"
|
||||
@play="(item: any[]) => handlePlayAndSeek(item)"
|
||||
/>
|
||||
|
|
@ -478,6 +492,7 @@ onMounted(() => {
|
|||
查看报告
|
||||
</el-button>
|
||||
</el-row> -->
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
</el-container>
|
||||
</el-container>
|
||||
|
|
@ -488,6 +503,7 @@ onMounted(() => {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
background-color: var(--ep-color-black);
|
||||
}
|
||||
.vid_box video {
|
||||
width: 100%;
|
||||
|
|
|
|||
Loading…
Reference in New Issue