Files
svgeditor2.0/src/components/mt-preview-ypt/index.vue

931 lines
30 KiB
Vue
Raw Normal View History

2025-10-22 09:09:46 +08:00
<template>
<div
:style="{ backgroundColor: useData.display ? '' : canvas_cfg.color }"
2025-10-22 09:49:14 +08:00
@mousedown="onMouseDown"
@mousemove="onMouseMove"
@mouseup="onMouseUp"
@mouseleave="onMouseUp"
@wheel="onMouseWheel"
style="height: 100vh; overflow: hidden"
2025-10-22 09:09:46 +08:00
>
2025-10-22 11:42:14 +08:00
<div
v-loading="useData.loading"
:style="canvasStyle"
style="overflow: hidden"
2025-10-22 11:42:14 +08:00
:class="`canvasArea ${isDragging ? 'cursor-grabbing' : mtPreviewProps.canDrag ? 'cursor-grab' : ''} `"
>
<!-- <el-button type="primary" class="backBtn" @click="onBack" v-if="!useData.display">返回</el-button> -->
<!-- 缩放控制按钮 (默认注释需要时可开启) -->
<!-- <div class="zoom-controls">
2025-10-22 09:09:46 +08:00
<el-button icon="ZoomIn" size="small" @click="zoomIn"></el-button>
<el-button icon="ZoomOut" size="small" @click="zoomOut"></el-button>
<el-button icon="RefreshLeft" size="small" @click="resetTransform"></el-button>
</div>
-->
2025-10-22 11:42:14 +08:00
<!-- <el-scrollbar ref="elScrollbarRef" class="w-1/1 h-1/1" @scroll="onScroll" > -->
<div ref="canvasAreaRef">
<render-core
v-model:done-json="done_json"
:canvas-cfg="canvas_cfg"
:grid-cfg="grid_cfg"
:show-ghost-dom="false"
:canvas-dom="canvasAreaRef"
:global-lock="false"
:preivew-mode="true"
:show-popover="mtPreviewProps.showPopover"
@setDoneJson="setDoneJson"
@element-click="handleElementClick"
></render-core>
</div>
<drag-canvas
ref="dragCanvasRef"
:scale-ratio="canvas_cfg.scale"
@drag-canvas-mouse-down="dragCanvasMouseDown"
@drag-canvas-mouse-move="dragCanvasMouseMove"
@drag-canvas-mouse-up="dragCanvasMouseUp"
></drag-canvas>
2025-10-22 09:09:46 +08:00
</div>
</div>
2025-12-09 08:41:11 +08:00
<!-- 弹框 -->
<iframeDia :steadyState="dataList" ref="iframeDiaRef" @lineListChange="indicator"></iframeDia>
2025-10-22 09:09:46 +08:00
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, watch, computed, onUnmounted, nextTick } from 'vue'
import RenderCore from '@/components/mt-edit/components/render-core/index.vue'
import type { IExportJson, IExportDoneJson } from '../mt-edit/components/types'
import { useExportJsonToDoneJson } from '../mt-edit/composables'
import type { IDoneJson } from '../mt-edit/store/types'
import { getItemAttr, previewCompareVal, setItemAttr } from '../mt-edit/utils'
import { ElScrollbar, ElMessage, ElMessageBox, ElButton } from 'element-plus'
import DragCanvas from '@/components/mt-edit/components/drag-canvas/index.vue'
import { useDataStore } from '@/stores/menuList'
import { globalStore } from '../mt-edit/store/global'
import type { IDoneJsonEventList } from '../mt-edit/store/types'
2025-12-05 10:55:28 +08:00
import IframeDia from './iframeDia.vue'
2025-10-22 09:09:46 +08:00
import MQTT from '@/utils/mqtt'
2025-12-05 11:22:36 +08:00
const iframeDiaRef = ref<any>(null)
2025-12-09 13:22:04 +08:00
2025-12-05 11:22:36 +08:00
const dataList = ref([])
2025-10-22 09:09:46 +08:00
// 节流函数实现 (替代lodash减少依赖)
const throttle = (func: (...args: any[]) => void, wait: number) => {
let lastTime = 0
return (...args: any[]) => {
const now = Date.now()
if (now - lastTime >= wait) {
func.apply(this, args)
lastTime = now
}
}
}
type MtPreviewProps = {
exportJson?: IExportJson
canZoom?: boolean
canDrag?: boolean
showPopover?: boolean
}
const mtPreviewProps = withDefaults(defineProps<MtPreviewProps>(), {
canDrag: true,
canZoom: true,
showPopover: true
})
const emits = defineEmits(['onEventCallBack'])
const useData = useDataStore()
const canvasAreaRef = ref<HTMLDivElement | null>(null)
const savedExportJson = ref<IExportJson>()
// 画布配置 - 扩展支持平移
const canvas_cfg = ref({
// width: 1920,
// height: 1080,
width: globalStore.canvasCfg.width,
height: globalStore.canvasCfg.height,
2025-10-22 09:49:14 +08:00
scale: 1,
2025-10-22 09:09:46 +08:00
color: '',
img: '',
guide: true,
adsorp: true,
adsorp_diff: 3,
transform_origin: {
x: 0,
y: 0
},
drag_offset: {
x: 0,
y: 0
},
// 平移属性
pan: {
x: 0,
y: 0
}
})
const grid_cfg = ref({
enabled: true,
align: true,
size: 10
})
const done_json = ref<IDoneJson[]>([])
const elScrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
const dragCanvasRef = ref<InstanceType<typeof DragCanvas>>()
const scroll_info = reactive({
begin_left: 0,
begin_top: 0,
left: 0,
top: 0
})
// 拖拽状态变量
const isDragging = ref(false)
const startPos = ref({ x: 0, y: 0 })
// 非响应式临时变量,减少响应式更新频率
const tempPan = { x: 0, y: 0 }
let animationFrameId: number | null = null
// 计算画布样式
const canvasStyle = computed(() => ({
transform: `translate(${canvas_cfg.value.pan.x}px, ${canvas_cfg.value.pan.y}px) scale(${canvas_cfg.value.scale})`,
transformOrigin: '0 0',
// width: `100vw`,
// height: `100vh`,
2025-10-22 11:42:14 +08:00
2025-10-22 09:09:46 +08:00
width: canvas_cfg.value.width + 'px',
height: canvas_cfg.value.height + 'px',
backgroundColor: useData.display ? '' : canvas_cfg.value.color,
backgroundImage: canvas_cfg.value.img ? `url(${canvas_cfg.value.img})` : 'none',
backgroundSize: '100% 100%',
backgroundPosition: 'center',
2025-10-22 11:42:14 +08:00
backgroundRepeat: 'no-repeat',
marginLeft: (document.documentElement.clientWidth - canvas_cfg.value.width * canvas_cfg.value.scale) / 2 + 'px'
2025-10-22 09:09:46 +08:00
}))
// 鼠标按下事件 - 开始拖拽
const onMouseDown = (e: MouseEvent) => {
if (mtPreviewProps.canDrag && e.button === 0) {
// 仅响应左键
e.preventDefault()
e.stopPropagation()
isDragging.value = true
startPos.value = {
x: e.clientX - canvas_cfg.value.pan.x,
y: e.clientY - canvas_cfg.value.pan.y
}
// 初始化临时位置
tempPan.x = canvas_cfg.value.pan.x
tempPan.y = canvas_cfg.value.pan.y
// 隐藏默认拖拽行为
if (canvasAreaRef.value) {
canvasAreaRef.value.style.userSelect = 'none'
}
// 启动动画帧同步
if (!animationFrameId) {
const syncPosition = () => {
if (isDragging.value) {
canvas_cfg.value.pan.x = tempPan.x
canvas_cfg.value.pan.y = tempPan.y
animationFrameId = requestAnimationFrame(syncPosition)
} else {
animationFrameId = null
}
}
animationFrameId = requestAnimationFrame(syncPosition)
}
}
}
// 节流处理鼠标移动事件 (16ms约等于60fps)
const throttledMouseMove = throttle((e: MouseEvent) => {
if (isDragging.value && mtPreviewProps.canDrag) {
e.preventDefault()
e.stopPropagation()
// 只更新临时变量,通过动画帧同步到响应式对象
tempPan.x = e.clientX - startPos.value.x
tempPan.y = e.clientY - startPos.value.y
}
}, 16)
// 鼠标移动事件 - 处理拖拽
const onMouseMove = throttledMouseMove
// 鼠标释放事件 - 结束拖拽
const onMouseUp = (e: MouseEvent) => {
if (isDragging.value) {
isDragging.value = false
// 恢复选择功能
if (canvasAreaRef.value) {
canvasAreaRef.value.style.userSelect = ''
}
// 取消动画帧
if (animationFrameId) {
cancelAnimationFrame(animationFrameId)
animationFrameId = null
}
}
}
// 缩放控制函数
const zoomIn = () => {
if (mtPreviewProps.canZoom) {
canvas_cfg.value.scale = Math.min(canvas_cfg.value.scale + 0.1, 3) // 最大放大到3倍
}
}
const zoomOut = () => {
if (mtPreviewProps.canZoom) {
canvas_cfg.value.scale = Math.max(canvas_cfg.value.scale - 0.1, 0.3) // 最小缩小到0.3倍
}
}
// 重置变换
const resetTransform = () => {
// 定义重置变换的函数
2025-10-22 09:49:14 +08:00
canvas_cfg.value.scale = useData.display ? 1 : document.documentElement.clientHeight / globalStore.canvasCfg.height
2025-10-22 09:09:46 +08:00
canvas_cfg.value.pan = { x: 0, y: 0 }
tempPan.x = 0
tempPan.y = 0
}
// 鼠标滚轮事件 - 缩放
const onMouseWheel = (e: WheelEvent) => {
if (mtPreviewProps.canZoom) {
e.preventDefault()
e.stopPropagation()
// 计算缩放因子
const delta = e.deltaY < 0 ? 0.1 : -0.1
const newScale = Math.max(0.3, Math.min(3, canvas_cfg.value.scale + delta))
// 如果缩放中心是鼠标位置,计算新的平移值以保持视觉中心
if (canvasAreaRef.value) {
2025-11-03 10:36:11 +08:00
const rect = canvasAreaRef.value?.getBoundingClientRect()
2025-10-22 09:09:46 +08:00
const mouseX = e.clientX - rect.left
const mouseY = e.clientY - rect.top
// 计算缩放前后的鼠标位置差异,调整平移量
const scaleRatio = newScale / canvas_cfg.value.scale
canvas_cfg.value.pan.x = mouseX - (mouseX - canvas_cfg.value.pan.x) * scaleRatio
canvas_cfg.value.pan.y = mouseY - (mouseY - canvas_cfg.value.pan.y) * scaleRatio
// 同步到临时变量
tempPan.x = canvas_cfg.value.pan.x
tempPan.y = canvas_cfg.value.pan.y
// 应用新缩放值
canvas_cfg.value.scale = newScale
}
}
}
const dragCanvasMouseDown = () => {
scroll_info.begin_left = scroll_info.left
scroll_info.begin_top = scroll_info.top
}
const dragCanvasMouseMove = (move_x: number, move_y: number) => {
let new_left = scroll_info.begin_left - move_x
let new_top = scroll_info.begin_top - move_y
elScrollbarRef.value?.setScrollLeft(new_left)
elScrollbarRef.value?.setScrollTop(new_top)
}
/**
* 画布拖动结束事件
*/
const dragCanvasMouseUp = () => {}
const setItemAttrByID = (id: string, key: string, val: any) => {
return setItemAttr(id, key, val, done_json.value)
}
const setItemAttrs = (info: { id: string; key: string; val: any }[]) => {
info.forEach(f => {
setItemAttr(f.id, f.key, f.val, done_json.value)
})
}
const getItemAttrByID = (id: string, key: string, val: any) => {
return getItemAttr(id, key, done_json.value)
}
const setItemAttrByIDAsync = (id: string, key: string, val: any) => {
// 通过改变属性的事件去设置值时 需要转换成宏任务 不然多个事件判断会有问题
setTimeout(() => {
setItemAttrByID(id, key, val)
}, 0)
}
;(window as any).$mtElMessage = ElMessage
;(window as any).$mtElMessageBox = ElMessageBox
;(window as any).$setItemAttrByID = (id: string, key: string, val: any) => setItemAttrByIDAsync(id, key, val)
;(window as any).$getItemAttrByID = getItemAttrByID
;(window as any).$previewCompareVal = previewCompareVal
;(window as any).$mtEventCallBack = (type: string, item_id: string, ...args: any[]) =>
emits('onEventCallBack', type, item_id, ...args)
onMounted(async () => {
// 启动消息监听 iframe传过来的参数
receiveMessage()
if (mtPreviewProps.exportJson) {
await setImportJson(mtPreviewProps.exportJson)
}
2025-12-09 13:22:04 +08:00
// 连接mqtt
2025-12-05 10:55:28 +08:00
await setMqtt()
2025-12-09 13:22:04 +08:00
// await sendTableData()
2025-10-22 09:09:46 +08:00
})
2025-12-09 13:22:04 +08:00
// mqtt推过来的 lineId
2025-12-05 10:55:28 +08:00
let keyList = ref<any>([])
2025-12-09 13:22:04 +08:00
// 实时数据表格
const tableData = ref()
2025-12-05 10:55:28 +08:00
2025-10-22 09:09:46 +08:00
const sendTableData = () => {
try {
2025-12-09 13:22:04 +08:00
// 类型检查,确保 tableData.value 是数组
if (!Array.isArray(tableData.value)) {
console.warn('tableData is not an array, current value:', tableData.value)
return
}
2025-10-22 09:09:46 +08:00
// 确保只传输可序列化的数据
2025-12-09 13:22:04 +08:00
const cleanData = tableData.value.map(item => ({
2025-10-22 09:09:46 +08:00
name: item.name,
2025-12-09 13:22:04 +08:00
date: item.timeId,
address: item.eventDesc
2025-10-22 09:09:46 +08:00
}))
window.parent.postMessage(
{
action: 'securityDetailData',
data: cleanData
},
'*'
)
} catch (error) {
console.error('数据传输失败:', error)
}
}
let transmissionDeviceIds: string[] = []
let eventListAll = ref<any>([])
const receiveMessage = () => {
// 在 iframe 内的页面中
window.addEventListener('message', function (event) {
// 验证消息来源(在生产环境中应该验证 origin
// if (event.origin !== 'trusted-origin') return;
const { type, payload } = event.data
2025-12-09 13:22:04 +08:00
if (type === 'RESET_EVENT') {
// 处理复位事件
handleResetEvent()
} else if (type === 'ANALYSIS_KEYS') {
// 处理 ANALYSIS_KEYS 消息
2025-10-22 09:09:46 +08:00
// 在处理新数据前,先清理现有的动态文字
if (savedExportJson.value) {
savedExportJson.value.json =
savedExportJson.value.json?.filter(item => !item.id?.startsWith('auto-text-')) || []
done_json.value = done_json.value.filter(item => !item.id?.startsWith('auto-text-'))
}
2025-12-09 13:22:04 +08:00
init()
}
// 对于其他类型的消息,我们仍然调用 init()
else if (type) {
init()
2025-10-22 09:09:46 +08:00
}
})
}
2025-12-09 13:22:04 +08:00
// 复位事件处理函数
const handleResetEvent = () => {
// 清空或重置 表格数据
// if (tableData.value && Array.isArray(tableData.value)) {
// tableData.value = []
// } else {
// // 确保 tableData 是一个空数组
// tableData.value = []
// }
// 接线图数据
keyList.value = []
setTimeout(() => {
// 表格数据
sendTableData()
// 接线图数据
if (savedExportJson.value) {
setImportJson(savedExportJson.value)
}
}, 100)
console.log('执行复位操作完成')
}
2025-10-22 09:09:46 +08:00
//根据 lineId 查找传输设备 ID 的函数
const findTransmissionDeviceIdsByKeyList = (array: any) => {
if (!savedExportJson.value?.json) return []
const deviceIds = savedExportJson.value.json.filter(item => array.includes(item.lineId ?? '')).map(item => item.id)
2025-12-05 10:55:28 +08:00
// console.log('传输设备 ID 列表:', deviceIds)
2025-10-22 09:09:46 +08:00
return deviceIds
}
// 为每个图元动态添加点击事件
const addClickEventsToElements = () => {
if (savedExportJson.value && savedExportJson.value.json) {
savedExportJson.value.json.forEach(item => {
// 检查是否已经有点击事件
const hasClickEvent = item.events && item.events.some(event => event.type === 'click')
if (!hasClickEvent) {
// 创建点击事件对象
const clickEvent: IDoneJsonEventList = {
id: generateRandomId(), // 生成随机ID
type: 'click', // 明确指定为字面量类型
action: 'customCode', // 自定义操作
jump_to: '',
change_attr: [],
custom_code: '', // 可以在这里添加自定义代码
trigger_rule: {
value: null
}
}
// 如果图元还没有events数组则创建一个
if (!item.events) {
item.events = []
}
// 添加点击事件
item.events.push(clickEvent)
}
})
}
}
// 生成随机ID的辅助函数
const generateRandomId = () => {
return Math.random().toString(36).substr(2, 10)
}
const setImportJson = (exportJson: IExportJson) => {
// 保存exportJson供后续使用
savedExportJson.value = exportJson
// 定义要执行的操作函数
const executeOperations = () => {
const { canvasCfg, gridCfg, importDoneJson } = useExportJsonToDoneJson(savedExportJson.value)
// 保留现有的平移和缩放设置
const currentPan = canvas_cfg.value.pan
const currentScale = canvas_cfg.value.scale
canvas_cfg.value = {
...canvasCfg,
pan: currentPan,
scale: currentScale
}
grid_cfg.value = gridCfg
done_json.value = importDoneJson
2026-01-13 15:20:17 +08:00
console.log(11111111111)
2025-10-22 09:09:46 +08:00
// 为图元添加点击事件
addClickEventsToElements()
// 首页初始化的时候
setTimeout(() => {
2026-01-13 15:20:17 +08:00
console.log(22222222222222)
2025-10-22 09:09:46 +08:00
done_json.value.forEach(item => {
//报警设备闪烁
2025-12-09 13:22:04 +08:00
if (findTransmissionDeviceIdsByKeyList(keyList.value).includes(item.id)) {
item.props.fill.val = 'red'
2025-10-22 09:09:46 +08:00
item.common_animations.val = 'flash'
} else {
item.common_animations.val = ''
}
})
}, 1000)
}
if (!useData.loading) {
if (exportJson == null) {
setDoneJson(useData.dataTree[0].kId)
} else {
executeOperations()
2026-01-13 15:20:17 +08:00
publish(useData.dataTree[0])
singlePublish(useData.dataTree[0])
2025-10-22 09:09:46 +08:00
}
} else {
// 如果不是true添加监听
const stopWatch = watch(
() => useData.loading,
newVal => {
if (newVal === false) {
// 当loading变为true时执行操作
if (exportJson == null) {
setDoneJson(useData.dataTree[0].kId)
} else {
executeOperations()
2026-01-13 15:20:17 +08:00
singlePublish(useData.dataTree[0])
publish(useData.dataTree[0])
2025-10-22 09:09:46 +08:00
}
// 执行后停止监听
stopWatch()
}
}
)
}
return true
}
// 添加一个新的 ref 来存储当前点击的设备ID
const currentClickedElementId = ref<string | null>(null)
// 图纸的kId
let storedSelectedId = ''
storedSelectedId = localStorage.getItem('selectedId') || ''
// 当前点击的元素lineId 通过mt-edit/render-core/index.vue传过来的click事件
2025-12-05 10:55:28 +08:00
const handleElementClick = (elementId: string, lineName: string) => {
2025-12-05 14:10:29 +08:00
iframeDiaRef.value.showNextCorner(elementId, lineName)
2025-10-22 09:09:46 +08:00
// 保存当前点击的设备ID
2025-12-05 14:10:29 +08:00
// indicator(['00B78D0171091', '00B78D0171092'])
2025-12-05 10:55:28 +08:00
// currentClickedElementId.value = elementId
// const item = done_json.value.find(item => item.lineId === elementId)
// if (item && item.events && item.events.some(event => event.type === 'click')) {
// // 发送设备ID到父级iframe
// window.parent.postMessage(
// {
// action: 'coreClick',
// coreId: elementId.toString(), // 确保是字符串
// selectedId: storedSelectedId // 新增的字段
// // elementData: item // 可以发送整个元素数据
// },
// '*'
// )
// }
2025-10-22 09:09:46 +08:00
}
const searchDevicesConnect = (transmissionDeviceIds: string[]) => {
// 确保 savedExportJson.value 存在
if (!savedExportJson.value?.json) {
console.warn('savedExportJson.value 或 json 未定义')
return []
}
// 查找所有连线元素
const lineElements = savedExportJson.value.json.filter(item => item.type === 'sys-line' && item.props?.bind_anchors)
// 查找所有开关元素
const switchElements = savedExportJson.value.json.filter(item => item.title?.includes('开关'))
// 存储连接线的ID
const connectedLineIds: string[] = []
// 存储找到的开关ID
const switchIds: string[] = []
// 首先找出所有传输设备连接的开关
for (const deviceId of transmissionDeviceIds) {
for (const line of lineElements) {
const bindAnchors = line.props.bind_anchors as { start?: { id: string }; end?: { id: string } } | undefined
if (!bindAnchors) continue
const startId = bindAnchors.start?.id
const endId = bindAnchors.end?.id
// 检查连线是否连接传输设备和开关
if (startId === deviceId && switchElements.some(s => s.id === endId)) {
if (endId && !switchIds.includes(endId)) {
switchIds.push(endId)
}
if (!connectedLineIds.includes(line.id!)) {
connectedLineIds.push(line.id!)
}
} else if (endId === deviceId && switchElements.some(s => s.id === startId)) {
if (startId && !switchIds.includes(startId)) {
switchIds.push(startId)
}
if (!connectedLineIds.includes(line.id!)) {
connectedLineIds.push(line.id!)
}
}
}
}
// 然后找出开关之间的连线
for (const line of lineElements) {
const bindAnchors = line.props.bind_anchors as { start?: { id: string }; end?: { id: string } } | undefined
if (!bindAnchors) continue
const startId = bindAnchors.start?.id
const endId = bindAnchors.end?.id
// 检查连线是否连接两个开关
if (startId && endId && switchIds.includes(startId) && switchIds.includes(endId)) {
if (!connectedLineIds.includes(line.id!)) {
connectedLineIds.push(line.id!)
}
}
}
// console.log('连接的连线ID列表:', connectedLineIds)
return connectedLineIds
}
// 预览时候绑定指标等的点击事件
const setDoneJson = async (kId: string) => {
const filteredItems = useData.dataTree.filter(item => item.kId == kId)
if (filteredItems.length === 0) {
console.error(`No item found with kId: ${kId}`)
return
}
const item = filteredItems[0]
// 根据传过来的kId找到所在的id
const foundId = item.id
if (foundId) {
// 将当前选中的行数据存储到本地存储
localStorage.setItem('selectedId', foundId)
}
storedSelectedId = localStorage.getItem('selectedId') || ''
if (!item.path) {
console.error(`Item with kId: ${kId} does not have a path property`)
return
}
setImportJson(JSON.parse(item.path))
}
const init = async () => {
setTimeout(() => {
2025-12-05 10:55:28 +08:00
// 执行动态添加文本的操作
// const updatedDoneJson = addTextNextToTransport()
2025-10-22 09:09:46 +08:00
// 调用函数获取传输设备 ID
transmissionDeviceIds = findTransmissionDeviceIdsByKeyList(keyList.value)
2025-12-05 10:55:28 +08:00
2025-10-22 09:09:46 +08:00
// 重新设置导入的JSON以触发界面更新
setImportJson(savedExportJson.value)
}, 100)
}
const timer = ref()
const list: any = ref([])
2025-10-22 09:09:46 +08:00
// 连接mqtt
const mqttClient = ref()
const setMqtt = async () => {
2025-12-05 10:55:28 +08:00
mqttClient.value = new MQTT('/zl/TemperData/#')
2025-10-22 09:09:46 +08:00
// 设置消息接收回调
try {
await mqttClient.value.init()
// 订阅主题
await mqttClient.value.subscribe('/zl/rtData/#')
2025-12-05 10:55:28 +08:00
await mqttClient.value.subscribe('/zl/TemperData/#') //实时数据
await mqttClient.value.subscribe('/zl/csConfigRtData/#') //指标
2025-10-22 09:09:46 +08:00
// 设置消息接收回调
mqttClient.value.onMessage((subscribe: string, message: any) => {
const msg: any = uint8ArrayToObject(message)
2026-06-05 09:15:18 +08:00
// console.log('🚀 ~ setMqtt ~ msg:', msg)
if (subscribe.split('/')[2] === 'rtData') {
// 指标数据
// await setImportJson(savedExportJson.value)
2026-06-05 09:15:18 +08:00
const list = [
...new Set(msg.filter((item: any) => item.devStatus === 1).map((item: any) => item.lineId))
]
setTimeout(() => {
if (done_json.value) {
done_json.value?.forEach(item => {
msg.forEach((msgValue: any) => {
if (item.id == msgValue.id) {
const unit = item?.unit && Array.isArray(item.unit) ? item.unit[0] : ''
item.props.text.val = item.props.text.val.replace(
/#{3}/g,
msgValue.value == 3.1415926 ? '暂无数据' : msgValue.value + unit
) //'B相负载电流-CP95:31'
}
})
2026-06-05 09:15:18 +08:00
list.forEach((listValue: any) => {
if (listValue == item.lineId && item.type == 'svg') {
item.props.fill.val = '#ff0000'
// item.common_animations.val = 'flash'
}
})
})
}
2026-01-13 15:20:17 +08:00
}, 500)
}
2025-12-05 14:10:29 +08:00
if (subscribe.split('/')[2] === 'csConfigRtData') {
2025-12-05 10:55:28 +08:00
// 指标数据
2025-12-05 11:22:36 +08:00
dataList.value = JSON.parse(msg.message)
2025-12-09 13:22:04 +08:00
//console.log('🚀 ~ setMqtt ~ dataList:', dataList.value)
// keyList.value = JSON.parse(list.path).canvasCfg.lineList
} else if (subscribe.split('/')[2] === 'TemperData') {
2025-12-09 13:22:04 +08:00
// 表格数据
tableData.value = []
tableData.value = JSON.parse(msg.message)
//console.log('🚀 ~ setMqtt ~ tableData:', tableData.value)
// 闪烁点
// if (Array.isArray(tableData.value) && tableData.value.length > 0) {
// // 提取所有的 id 并去重(使用 Set 方式,性能更好)
// const uniqueIds = [
// ...new Set(
// tableData.value
// .filter(item => item.id) // 确保 id 存在
// .map(item => item.id) // 提取 id
// )
// ]
// keyList.value = uniqueIds
// console.log('提取的唯一ID列表:', keyList.value)
// }
sendTableData()
2025-12-05 10:55:28 +08:00
}
2025-10-22 09:09:46 +08:00
})
} catch (error) {
console.error('MQTT 初始化失败:', error)
}
}
const singlePublish = async (id: string) => {
if (mqttClient.value) {
await mqttClient.value.subscribe('zl/askRtData/' + storedSelectedId)
// 发送消息
await mqttClient.value.publish('/zl/askRtData/' + storedSelectedId, '{}')
if (timer.value) {
clearInterval(timer.value)
timer.value = null
}
timer.value = setInterval(
() => {
mqttClient.value.publish('/zl/askRtData/' + storedSelectedId, '{}')
},
3 * 60 * 1000
)
}
}
2025-12-09 08:41:11 +08:00
const publish = async (list: any) => {
2026-06-05 09:15:18 +08:00
// console.log('🚀 ~ publish ~ list:', JSON.parse(list.path).canvasCfg.lineList)
2025-12-09 08:41:11 +08:00
if (mqttClient.value) {
// 发送消息
await mqttClient.value.publish(
2025-12-09 13:22:04 +08:00
'/zl/askCSConfigWarnData/' + storedSelectedId,
2025-12-09 08:41:11 +08:00
`[${JSON.parse(list.path).canvasCfg.lineList}]`
)
if (timer.value) {
clearInterval(timer.value)
timer.value = null
}
timer.value = setInterval(
() => {
mqttClient.value.publish(
2025-12-09 13:22:04 +08:00
'/zl/askCSConfigWarnData/' + storedSelectedId,
2025-12-09 08:41:11 +08:00
`[${JSON.parse(list.path).canvasCfg.lineList}]`
)
},
3 * 60 * 1000
)
}
2025-12-05 10:55:28 +08:00
}
// 绑定指标
const indicator = async (ids: string[]) => {
2025-10-22 09:09:46 +08:00
if (mqttClient.value) {
2025-12-05 10:55:28 +08:00
// await mqttClient.value.subscribe('zl/askCSConfigRtData/' + useData.dataTree[0].id)
2025-10-22 09:09:46 +08:00
// 发送消息
2025-12-09 13:22:04 +08:00
await mqttClient.value.publish('/zl/askCSConfigRtData/' + storedSelectedId, `[${ids}]`)
2025-10-22 09:09:46 +08:00
}
}
2025-12-05 10:55:28 +08:00
2025-10-22 09:09:46 +08:00
onUnmounted(() => {
if (timer.value) {
clearInterval(timer.value)
timer.value = null
}
})
// 方法1: 使用 mqtt消息转换
function uint8ArrayToObject(uint8Array: Uint8Array) {
try {
// 将 Uint8Array 解码为字符串
const decoder = new TextDecoder('utf-8')
const jsonString = decoder.decode(uint8Array)
// 将 JSON 字符串解析为对象
return JSON.parse(jsonString)
} catch (error) {
console.error('转换失败:', error)
return null
}
}
const onBack = () => {
window.close()
}
watch(
() => useData.display,
newVal => {
if (newVal) {
2025-10-22 09:49:14 +08:00
canvas_cfg.value.scale = 1
2025-10-22 09:09:46 +08:00
canvas_cfg.value.pan = {
x: 0,
2025-10-22 09:49:14 +08:00
y: 0
2025-10-22 09:09:46 +08:00
}
} else {
canvas_cfg.value.scale = document.documentElement.clientHeight / globalStore.canvasCfg.height
}
},
{ immediate: true }
)
defineExpose({
setItemAttrByID,
setImportJson,
setItemAttrs,
zoomIn,
zoomOut,
resetTransform
})
</script>
<style scoped>
.canvasArea {
position: relative;
/* 移除过渡效果,避免拖拽延迟 */
will-change: transform;
/* 提示浏览器优化transform属性 */
}
.backBtn {
position: absolute;
top: 20px;
right: 10px;
z-index: 2;
}
.zoom-controls {
position: fixed;
top: 20px;
left: 20px;
z-index: 1;
display: flex;
gap: 5px;
}
.cursor-grab {
cursor: grab;
}
.cursor-grabbing {
cursor: grabbing;
}
.el-table {
/* --el-table-border-color: #0a73ff;
--el-table-row-hover-bg-color: #3d4862;
--el-table-header-bg-color: #2a3b62;
--el-table-bg-color: #343849c7; */
--el-table-border-color: #0a73ff;
--el-table-row-hover-bg-color: #0a73ff20;
--el-table-header-bg-color: #0a73ff40;
--el-table-bg-color: #ffffff00;
text-align: center;
color: #ffffff;
}
:deep(.el-table tr) {
/* background-color: #242936; */
background-color: #00000090 !important;
}
:deep(.el-table .cell) {
color: #ffffff;
text-align: center;
}
:deep(.el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell) {
/* background-color: #303b54; */
background-color: #5aa1ff29;
}
2025-12-05 11:22:36 +08:00
/* .aaaa{
position: absolute;
right: 0;
top: 0px;
z-index: 111123;
height: 100px;
width: 100px;
background-color: #ccc;
} */
2025-10-22 09:09:46 +08:00
</style>