first commit
This commit is contained in:
378
src/views/business/largeModel/realDialog.vue
Normal file
378
src/views/business/largeModel/realDialog.vue
Normal file
@@ -0,0 +1,378 @@
|
||||
<template>
|
||||
<div class="dialog">
|
||||
<div class="file">
|
||||
<el-image
|
||||
v-for="(i, index) in answers.previewImages"
|
||||
:key="index"
|
||||
:src="i.url"
|
||||
:zoom-rate="1.2"
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:scale="0.8"
|
||||
:preview-src-list="answers.previewImages.map(i=>i.url)"
|
||||
show-progress
|
||||
:initial-index="4"
|
||||
fit="object-fit"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="answers.query" class="user">
|
||||
{{ answers.query }}
|
||||
</div>
|
||||
<div v-if="answers.answer" class="ai-message">
|
||||
<img src="@/assets/images/largeModel/icon-robot.png" alt="">
|
||||
<div class="content" v-html="formattedAnswer(answers.answer)"></div>
|
||||
</div>
|
||||
<div class="loading" v-else>
|
||||
<span class="dot" v-for="i in 3" :key="i"></span>
|
||||
</div>
|
||||
</div>
|
||||
<el-image-viewer
|
||||
v-if="showPreview"
|
||||
:url-list="srcList"
|
||||
show-progress
|
||||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:scale="0.8"
|
||||
fit="object-fit"
|
||||
@close="showPreview = false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { marked } from 'marked'
|
||||
// import { useStore } from 'vuex'
|
||||
import GlobalMap from '@/components/map/js/GlobalMap.js'
|
||||
|
||||
// const store = useStore()
|
||||
const emit = defineEmits([ 'openVideo', 'openUav' ])
|
||||
const videoArr = ref([])
|
||||
const uavArr = ref([])
|
||||
const boatArr = ref([])
|
||||
const codes = ref([])
|
||||
const visible = ref(false)
|
||||
// 创建自定义渲染器
|
||||
const renderer = new marked.Renderer()
|
||||
const showPreview = ref(false)
|
||||
const srcList = ref([])
|
||||
|
||||
// 自定义链接渲染
|
||||
renderer.link = function(e) {
|
||||
if(e.href) {
|
||||
return e.href.split(' ').map(i => {
|
||||
return renderMedia(i)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染媒体元素的通用函数
|
||||
const renderMedia = (url, text) => {
|
||||
if (!url) {
|
||||
return ''
|
||||
}
|
||||
console.log(url, 'url')
|
||||
// 监控
|
||||
if (url.startsWith('{') && url.includes('rtsp_url')) {
|
||||
const videoNameMatch = JSON.parse(url).video_name
|
||||
const codeMatch = JSON.parse(url).video_code
|
||||
const previewUrlMatch = JSON.parse(url).rtsp_url
|
||||
const videoObj = {
|
||||
videoName: videoNameMatch,
|
||||
videoCode: codeMatch,
|
||||
previewUrl: previewUrlMatch
|
||||
}
|
||||
if (videoObj.videoCode) {
|
||||
const exists = videoArr.value.some(item =>
|
||||
item.videoCode === videoObj.videoCode && item.previewUrl === videoObj.previewUrl
|
||||
)
|
||||
let index
|
||||
if (!exists) {
|
||||
// 只有不存在时才添加
|
||||
index = videoArr.value.length
|
||||
videoArr.value.push(videoObj)
|
||||
} else {
|
||||
// 如果已存在,找到对应的索引
|
||||
index = videoArr.value.findIndex(item =>
|
||||
item.videoCode === videoObj.videoCode && item.previewUrl === videoObj.previewUrl
|
||||
)
|
||||
}
|
||||
// 创建全局事件来处理视频弹窗
|
||||
return `<div style="color:#1D91FE ;cursor:pointer" onclick="openVideoHandler('${index}')">
|
||||
${videoObj.videoName}
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
// 无人机
|
||||
if(url.startsWith('{') && url.includes('droneSn')) {
|
||||
if(JSON.parse(url).droneSn) {
|
||||
const droneSnMatch = JSON.parse(url).droneSn
|
||||
const gatewaySnMatch = JSON.parse(url).gatewaySn
|
||||
const videoNameMatch = JSON.parse(url).video_name
|
||||
const uavObj = {
|
||||
droneSn: droneSnMatch,
|
||||
gatewaySn: gatewaySnMatch,
|
||||
videoName: videoNameMatch
|
||||
}
|
||||
if (uavObj.videoName) {
|
||||
const exists = uavArr.value.some(item =>
|
||||
item.videoName === uavObj.videoName
|
||||
)
|
||||
let index
|
||||
if (!exists) {
|
||||
// 只有不存在时才添加
|
||||
index = uavArr.value.length
|
||||
uavArr.value.push(uavObj)
|
||||
} else {
|
||||
// 如果已存在,找到对应的索引
|
||||
index = uavArr.value.findIndex(item =>
|
||||
item.droneSn === uavObj.droneSn || item.gatewaySnMatch === uavObj.gatewaySnMatch
|
||||
)
|
||||
}
|
||||
// 创建全局事件来处理视频弹窗
|
||||
return `<div style="color:#1D91FE ;cursor:pointer" onclick="openUavHandler('${index}')">
|
||||
${uavObj.videoName}
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
}
|
||||
// 船舶智能追踪
|
||||
if(url.startsWith('{') && url.includes('mmsi')) {
|
||||
if(JSON.parse(url).mmsi) {
|
||||
const terminalCodeMatch = JSON.parse(url).mmsi
|
||||
const boatNameMatch = JSON.parse(url).boatName
|
||||
const sogMatch = JSON.parse(url).sog
|
||||
const cogMatch = JSON.parse(url).cog
|
||||
const longitudeMatch = JSON.parse(url).longitude
|
||||
const latitudeMatch = JSON.parse(url).latitude
|
||||
const boatObj = {
|
||||
terminalCode: terminalCodeMatch,
|
||||
boatName: boatNameMatch,
|
||||
sog: Number(sogMatch),
|
||||
cog: 360 - Number(cogMatch),
|
||||
angle: Number(cogMatch),
|
||||
longitude: longitudeMatch,
|
||||
latitude: latitudeMatch
|
||||
}
|
||||
if (boatObj.terminalCode) {
|
||||
const exists = boatArr.value.some(item =>
|
||||
item.terminalCode === boatObj.terminalCode
|
||||
)
|
||||
let index
|
||||
if (!exists) {
|
||||
// 只有不存在时才添加
|
||||
index = boatArr.value.length
|
||||
boatArr.value.push(boatObj)
|
||||
} else {
|
||||
// 如果已存在,找到对应的索引
|
||||
index = boatArr.value.findIndex(item =>
|
||||
item.terminalCode === boatObj.terminalCode || item.boatName === boatObj.boatName
|
||||
)
|
||||
}
|
||||
// 创建全局事件来处理视频弹窗
|
||||
return `<div style="color:#1D91FE ;cursor:pointer" onclick="openTrawlerHandler('${index}')">
|
||||
${boatObj.boatName}
|
||||
</div>`
|
||||
}
|
||||
}
|
||||
}
|
||||
// 检查是否为视频链接
|
||||
if (url.match(/\.(mp4|webm|ogg)(\?.*)?$/i)) {
|
||||
return `<div style="margin: 10px 0;">
|
||||
<video controls loop muted style="width: 260px; height: 150px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin: 10px 10px 10px 0;">
|
||||
<source src="${url}" type="video/${url.split('.').pop().split('?')[0]}">
|
||||
您的浏览器不支持视频播放。
|
||||
</video>
|
||||
</div>`
|
||||
}
|
||||
|
||||
// 检查是否为图片链接
|
||||
if (url.match(/\.(jpg|jpeg|png|gif|bmp|webp)(\?.*)?$/i)) {
|
||||
return `<img onclick="openImageHandle('${url}')" src="${url}" alt="${text || ''}" style="width: 260px; height: 150px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin: 10px 10px 10px 0;" />`
|
||||
}
|
||||
}
|
||||
// 监控视频查看
|
||||
const openVideoHandler = (index) => {
|
||||
const data = videoArr.value[index]
|
||||
// 雷云一体机一个视频
|
||||
if(data.videoName.indexOf('雷云') !== -1) {
|
||||
const videos = videoArr.value.filter(i => i.videoName.indexOf('雷云') !== -1).sort((a, b) => {
|
||||
const index1 = a.videoName[a.videoName.length - 1]
|
||||
const index2 = b.videoName[b.videoName.length - 1]
|
||||
return index1 - index2
|
||||
})
|
||||
|
||||
codes.value = [ videos[0].videoCode ]
|
||||
}else{
|
||||
if(data.videoName.indexOf('球机') !== -1) {
|
||||
const videos = videoArr.value.filter(i => i.videoName.indexOf('球机') !== -1).sort((a, b) => {
|
||||
const index1 = a.videoName[a.videoName.length - 1]
|
||||
const index2 = b.videoName[b.videoName.length - 1]
|
||||
return index1 - index2
|
||||
})
|
||||
codes.value = videos.map((i, index) => {
|
||||
return i.previewUrl
|
||||
})
|
||||
}else{
|
||||
codes.value = [ data.videoCode ]
|
||||
}
|
||||
}
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
visible.value = true
|
||||
emit('openVideo', { codes: codes.value, visible: visible.value })
|
||||
// 再次延迟后触发窗口大小调整事件(模拟resize)
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(new Event('resize'))
|
||||
}, 100)
|
||||
}, 50)
|
||||
})
|
||||
}
|
||||
// 预览图片
|
||||
const openImageHandle = (url) => {
|
||||
srcList.value = [ url ]
|
||||
showPreview.value = true
|
||||
}
|
||||
// 开启无人机控制
|
||||
const openUavHandler = (index) => {
|
||||
const data = uavArr.value[index]
|
||||
// store.commit('mapStore/updateUavData', { ...data })
|
||||
emit('openUav', { ...data })
|
||||
}
|
||||
// 渔船追踪
|
||||
const openTrawlerHandler = (index) => {
|
||||
const data = boatArr.value[index]
|
||||
// store.commit('mapStore/updateWindowInfo', {
|
||||
// visible: true,
|
||||
// type: '_trawler_dynamic',
|
||||
// data: { ...data }
|
||||
// })
|
||||
GlobalMap.instance.map.animateTo(
|
||||
{
|
||||
zoom: 20,
|
||||
center: [ data.longitude, data.latitude ]
|
||||
},
|
||||
{
|
||||
duration: 1000 * 0.5,
|
||||
easing: 'out'
|
||||
}
|
||||
)
|
||||
}
|
||||
// 配置 marked 选项并应用自定义渲染器
|
||||
marked.setOptions({
|
||||
renderer: renderer,
|
||||
gfm: true,
|
||||
breaks: true,
|
||||
smartypants: false
|
||||
})
|
||||
const props = defineProps({
|
||||
answers: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
const formattedAnswer = (answer) => {
|
||||
if (!answer) {
|
||||
return ''
|
||||
}
|
||||
const processedText = answer.replace(/{[^}]+}/g, (match) => {
|
||||
return renderMedia(match)
|
||||
})
|
||||
return marked.parse(processedText)
|
||||
}
|
||||
onMounted(() => {
|
||||
videoArr.value = []
|
||||
window.openVideoHandler = openVideoHandler
|
||||
window.openImageHandle = openImageHandle
|
||||
// 无人机控制方法
|
||||
window.openUavHandler = openUavHandler
|
||||
// 渔船追踪
|
||||
window.openTrawlerHandler = openTrawlerHandler
|
||||
})
|
||||
onUnmounted(() => {
|
||||
delete window.openVideoHandler
|
||||
delete window.openImageHandle
|
||||
delete window.openUavHandler
|
||||
delete window.openTrawlerHandler
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dialog {
|
||||
height: calc(100% - 50px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.user,.file{
|
||||
color: #E1F1FA;
|
||||
max-width: calc(100% - 88px);
|
||||
padding: 6px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
width: fit-content;
|
||||
margin-left: auto;
|
||||
|
||||
background: rgba(89,175,255,0.15);
|
||||
border-radius: 3px 3px 3px 3px;
|
||||
}
|
||||
.file{
|
||||
background: none;
|
||||
padding-right: 0;
|
||||
.el-image{
|
||||
width:100px;
|
||||
height: 100px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.ai-message {
|
||||
color: #E1F1FA;
|
||||
border-radius: 22px;
|
||||
max-width: calc(100% - 88px);
|
||||
padding: 10px 0;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
width: fit-content;
|
||||
margin-right: auto;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
img{
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.content{
|
||||
margin-bottom: 10px;
|
||||
background: rgba(89,175,255,0.15);
|
||||
border-radius: 3px 3px 3px 3px;
|
||||
padding: 6px;
|
||||
}
|
||||
}
|
||||
.loading {
|
||||
color: #0C1533;
|
||||
border-radius: 22px;
|
||||
max-width: calc(100% - 88px);
|
||||
padding: 10px 16px;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
width: fit-content;
|
||||
margin-right: auto;
|
||||
text-align: left;
|
||||
}
|
||||
.dot {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background-color: #ccc;
|
||||
border-radius: 50%;
|
||||
margin: 0 2px;
|
||||
animation: dot-animation 1.5s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes dot-animation {
|
||||
0%, 100% { opacity: 0.5; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user