Files
svgeditor2.0/src/components/mt-preview-ypt/index.vue
2026-06-05 09:15:18 +08:00

931 lines
30 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
:style="{ backgroundColor: useData.display ? '' : canvas_cfg.color }"
@mousedown="onMouseDown"
@mousemove="onMouseMove"
@mouseup="onMouseUp"
@mouseleave="onMouseUp"
@wheel="onMouseWheel"
style="height: 100vh; overflow: hidden"
>
<div
v-loading="useData.loading"
:style="canvasStyle"
style="overflow: hidden"
: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">
<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>
-->
<!-- <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>
</div>
</div>
<!-- 弹框 -->
<iframeDia :steadyState="dataList" ref="iframeDiaRef" @lineListChange="indicator"></iframeDia>
</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'
import IframeDia from './iframeDia.vue'
import MQTT from '@/utils/mqtt'
const iframeDiaRef = ref<any>(null)
const dataList = ref([])
// 节流函数实现 (替代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,
scale: 1,
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`,
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',
backgroundRepeat: 'no-repeat',
marginLeft: (document.documentElement.clientWidth - canvas_cfg.value.width * canvas_cfg.value.scale) / 2 + 'px'
}))
// 鼠标按下事件 - 开始拖拽
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 = () => {
// 定义重置变换的函数
canvas_cfg.value.scale = useData.display ? 1 : document.documentElement.clientHeight / globalStore.canvasCfg.height
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) {
const rect = canvasAreaRef.value?.getBoundingClientRect()
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)
}
// 连接mqtt
await setMqtt()
// await sendTableData()
})
// mqtt推过来的 lineId
let keyList = ref<any>([])
// 实时数据表格
const tableData = ref()
const sendTableData = () => {
try {
// 类型检查,确保 tableData.value 是数组
if (!Array.isArray(tableData.value)) {
console.warn('tableData is not an array, current value:', tableData.value)
return
}
// 确保只传输可序列化的数据
const cleanData = tableData.value.map(item => ({
name: item.name,
date: item.timeId,
address: item.eventDesc
}))
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
if (type === 'RESET_EVENT') {
// 处理复位事件
handleResetEvent()
} else if (type === 'ANALYSIS_KEYS') {
// 处理 ANALYSIS_KEYS 消息
// 在处理新数据前,先清理现有的动态文字
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-'))
}
init()
}
// 对于其他类型的消息,我们仍然调用 init()
else if (type) {
init()
}
})
}
// 复位事件处理函数
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('执行复位操作完成')
}
//根据 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)
// console.log('传输设备 ID 列表:', deviceIds)
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
console.log(11111111111)
// 为图元添加点击事件
addClickEventsToElements()
// 首页初始化的时候
setTimeout(() => {
console.log(22222222222222)
done_json.value.forEach(item => {
//报警设备闪烁
if (findTransmissionDeviceIdsByKeyList(keyList.value).includes(item.id)) {
item.props.fill.val = 'red'
item.common_animations.val = 'flash'
} else {
item.common_animations.val = ''
}
})
}, 1000)
}
if (!useData.loading) {
if (exportJson == null) {
setDoneJson(useData.dataTree[0].kId)
} else {
executeOperations()
publish(useData.dataTree[0])
singlePublish(useData.dataTree[0])
}
} else {
// 如果不是true添加监听
const stopWatch = watch(
() => useData.loading,
newVal => {
if (newVal === false) {
// 当loading变为true时执行操作
if (exportJson == null) {
setDoneJson(useData.dataTree[0].kId)
} else {
executeOperations()
singlePublish(useData.dataTree[0])
publish(useData.dataTree[0])
}
// 执行后停止监听
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事件
const handleElementClick = (elementId: string, lineName: string) => {
iframeDiaRef.value.showNextCorner(elementId, lineName)
// 保存当前点击的设备ID
// indicator(['00B78D0171091', '00B78D0171092'])
// 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 // 可以发送整个元素数据
// },
// '*'
// )
// }
}
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(() => {
// 执行动态添加文本的操作
// const updatedDoneJson = addTextNextToTransport()
// 调用函数获取传输设备 ID
transmissionDeviceIds = findTransmissionDeviceIdsByKeyList(keyList.value)
// 重新设置导入的JSON以触发界面更新
setImportJson(savedExportJson.value)
}, 100)
}
const timer = ref()
const list: any = ref([])
// 连接mqtt
const mqttClient = ref()
const setMqtt = async () => {
mqttClient.value = new MQTT('/zl/TemperData/#')
// 设置消息接收回调
try {
await mqttClient.value.init()
// 订阅主题
await mqttClient.value.subscribe('/zl/rtData/#')
await mqttClient.value.subscribe('/zl/TemperData/#') //实时数据
await mqttClient.value.subscribe('/zl/csConfigRtData/#') //指标
// 设置消息接收回调
mqttClient.value.onMessage((subscribe: string, message: any) => {
const msg: any = uint8ArrayToObject(message)
// console.log('🚀 ~ setMqtt ~ msg:', msg)
if (subscribe.split('/')[2] === 'rtData') {
// 指标数据
// await setImportJson(savedExportJson.value)
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'
}
})
list.forEach((listValue: any) => {
if (listValue == item.lineId && item.type == 'svg') {
item.props.fill.val = '#ff0000'
// item.common_animations.val = 'flash'
}
})
})
}
}, 500)
}
if (subscribe.split('/')[2] === 'csConfigRtData') {
// 指标数据
dataList.value = JSON.parse(msg.message)
//console.log('🚀 ~ setMqtt ~ dataList:', dataList.value)
// keyList.value = JSON.parse(list.path).canvasCfg.lineList
} else if (subscribe.split('/')[2] === 'TemperData') {
// 表格数据
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()
}
})
} 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
)
}
}
const publish = async (list: any) => {
// console.log('🚀 ~ publish ~ list:', JSON.parse(list.path).canvasCfg.lineList)
if (mqttClient.value) {
// 发送消息
await mqttClient.value.publish(
'/zl/askCSConfigWarnData/' + storedSelectedId,
`[${JSON.parse(list.path).canvasCfg.lineList}]`
)
if (timer.value) {
clearInterval(timer.value)
timer.value = null
}
timer.value = setInterval(
() => {
mqttClient.value.publish(
'/zl/askCSConfigWarnData/' + storedSelectedId,
`[${JSON.parse(list.path).canvasCfg.lineList}]`
)
},
3 * 60 * 1000
)
}
}
// 绑定指标
const indicator = async (ids: string[]) => {
if (mqttClient.value) {
// await mqttClient.value.subscribe('zl/askCSConfigRtData/' + useData.dataTree[0].id)
// 发送消息
await mqttClient.value.publish('/zl/askCSConfigRtData/' + storedSelectedId, `[${ids}]`)
}
}
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) {
canvas_cfg.value.scale = 1
canvas_cfg.value.pan = {
x: 0,
y: 0
}
} 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;
}
/* .aaaa{
position: absolute;
right: 0;
top: 0px;
z-index: 111123;
height: 100px;
width: 100px;
background-color: #ccc;
} */
</style>