文章接上文:进一步完善前端框架搭建及vue-konva依赖的使用(Vscode)-CSDN博客
4.功能完善
添加功能: 1.页面左侧图标栏目中的图标:当鼠标放置在每个图标上时,每个图标会显示出对应的名称。 2.图标属性中添加设置图标device_ID和名称device_name的设置操作。 根据以上代码,添加这两个功能我需要用到什么知识点(列举出),并给出对应的添加或修改的代码,注意一切都是在以上代码基础上修改操作,不得随便更改原本代码。
-
添加鼠标放置图标上的功能提示?
引入
<el-tooltip>
组件,<!-- 左侧图标栏 --> <div class="icon-sidebar"> <div class="sidebar-icon" v-for="(icon, index) in icons" :key="index" :draggable="icon.loaded" @dragstart="icon.loaded ? handleDragStart($event, icon) : null" :class="{ 'opacity-50': !icon.loaded }"> <el-tooltip class="item" effect="dark" :content="icon.name" placement="right"> <img :src="icon.src" :alt="icon.name" class="custom-icon" /> </el-tooltip> <div class="icon-label">{{ icon.name }}</div> <div v-if="!icon.loaded" class="loading-indicator">加载中...</div> </div> </div>
我将
<el-tooltip>
直接应用于每个图标的img
标签周围,并为每个el-tooltip
设置了对应的content
属性,这样当鼠标悬停在图标上方时,将会显示该图标的名称。使用前,请确保你的项目中已经正确安装并引入了 Element Plus 组件库,因为
<el-tooltip>
是 Element Plus 的一部分。如果还未安装,请通过 npm 或 yarn 安装:npm install element-plus --save # 或者 yarn add element-plus
-
如何实现: 1.自由缩放图标 2.右键菜单:设置 device_ID 与 device_name 3.保存布局,并进行导出 4.导入导出
✅ 目标功能列表
功能编号 功能名称 描述 1 自由缩放图标 支持在画布中对图标进行自由缩放 2 右键菜单:设置 device_id 与 device_name 点击图标后弹出输入框让用户填写设备信息 3 保存布局并导出 将当前图标位置和配置导出为 JSON 文件 4 导入布局 上传 JSON 文件还原图标布局 -
功能1:
<!-- 在 <v-layer> 内,已渲染完所有 <v-image> 后,添加: --> <v-transformer ref="transformerRef" :config="transformerConfig" /> import { ref, computed, watch } from 'vue' // 存每个 <v-image> 的组件引用 const iconRefs = ref([]) // 存 Transformer 组件引用 const transformerRef = ref(null) // 存放当前选中的索引数组 const selectedIndices = ref([]) // Konva 层,用于重绘 const layerRef = ref(null) // ① 给 v-image 挂 ref function setIconRef(el, index) { iconRefs.value[index] = el } // ② Transformer 配置 const transformerConfig = computed(() => ({ nodes: selectedIndices.value .map(i => iconRefs.value[i]?.getNode()) .filter(Boolean), enabledAnchors: ['top-left','top-right','bottom-left','bottom-right'], borderStroke: 'blue', anchorFill: 'white', anchorStroke: 'blue' })) // ③ 每次 config 变更,重新挂载节点并重绘 watch(transformerConfig, (cfg) => { const trNode = transformerRef.value?.getNode() const lNode = layerRef.value?.getNode() if (trNode && lNode) { trNode.nodes(cfg.nodes) lNode.batchDraw() } }) // ④ 变换结束后,同步数据 function handleTransformEnd(index) { const node = iconRefs.value[index]?.getNode() if (!node) return const w = node.width() * node.scaleX() const h = node.height() * node.scaleY() droppedIcons.value[index].x = node.x() droppedIcons.value[index].y = node.y() droppedIcons.value[index].width = w droppedIcons.value[index].height = h node.scaleX(1) node.scaleY(1) }
-
功能2
<!-- 原有 <el-dropdown> 内插入: --> <el-dropdown-item @click="openAttributeDialog"> <el-button class="menu-button" text>设置属性</el-button> </el-dropdown-item> <el-dropdown-item divided disabled style="pointer-events: auto;"> <div class="device-info-box"> <p>设备 ID: {{ currentDevice.device_ID || '-' }}</p> <p>设备名称: {{ currentDevice.device_name || '-' }}</p> </div> </el-dropdown-item> <!-- 弹窗对话框 --> <el-dialog v-model="showAttributeDialog" title="设置设备属性" width="30%"> <el-form label-position="left" label-width="80px" size="small"> <el-form-item label="设备 ID"> <el-input v-model="currentDevice.device_ID" placeholder="请输入设备ID"/> </el-form-item> <el-form-item label="设备名称"> <el-input v-model="currentDevice.device_name" placeholder="请输入设备名称"/> </el-form-item> </el-form> <template #footer> <el-button @click="showAttributeDialog = false">取消</el-button> <el-button type="primary" @click="saveDeviceData">确认</el-button> </template> </el-dialog> import { ref } from 'vue' // 当前右键所选图标的信息 const contextIconIndex = ref(null) const currentDevice = ref({ device_ID: '', device_name: '' }) const showAttributeDialog= ref(false) // ① 右键时同步当前数据 function onRightClick(e, index) { e.evt.preventDefault() contextIconIndex.value = index const icon = droppedIcons.value[index] currentDevice.value = { device_ID: icon.device_ID || '', device_name: icon.device_name || '' } showContextMenu.value = true // …已有定位逻辑 } // ② 弹窗打开 function openAttributeDialog() { if (contextIconIndex.value !== null) { const icon = droppedIcons.value[contextIconIndex.value] currentDevice.value = { device_ID: icon.device_ID || '', device_name: icon.device_name || '' } showAttributeDialog.value = true } } // ③ 保存回写 function saveDeviceData() { const idx = contextIconIndex.value if (idx !== null) { const icon = droppedIcons.value[idx] icon.device_ID = currentDevice.value.device_ID icon.device_name = currentDevice.value.device_name } showAttributeDialog.value = false }
-
功能3
-
功能4
-
-
实现:
功能模块 子功能 描述 技术建议 🧪 模拟传感器程序 数据模拟 模拟温度、湿度、火情等传感器数据 Python 脚本 / Node.js 网络发送 将数据定时打包并发送至本机某个端口(TCP/UDP) Socket 编程 配置可调 支持调整模拟频率、数值范围等 JSON 配置或命令行参数 🌐 后端服务 端口监听 后端监听指定端口,接收来自模拟程序的数据 Spring Boot / Node.js / django 数据解析 将接收到的字符串(如 JSON)解析为结构化数据 JSON解析器 数据推送 使用 WebSocket 将实时数据推送给前端 Spring Boot WebSocket / Socket.IO REST 查询接口 提供历史数据查询接口(可选) RESTful API 日志与错误处理 数据记录、连接失败、格式异常处理 日志组件如 Logback 🖥️ 前端展示页面 UI布局 仿图中仪表盘式页面布局,展示三种传感器数据与地图 Vue 3 + Element Plus 实时数据接收 建立 WebSocket 连接接收数据 WebSocket 客户端 动态仪表盘 温度、湿度、气压等采用仪表盘组件展示 ECharts / vue-echarts 地图定位 实时显示经纬度地图并标注位置 百度地图 / Leaflet.js 数据列表 显示实时数值列表和“报警记录”、“历史数据”按钮 Table 组件 报警提示 火情触发弹窗或高亮 动态提示框、颜色变化等 -
🧪 模拟传感器程序:
待更新
-
🌐 后端服务:
待更新
-
🖥️ 前端展示页面:
-
准备工作:
安装 命令 Element Plus npm install element-plus --save socket.io-client(第三方库) npm install socket.io-client ECharts 和 vue-echarts npm install echarts vue-echarts 百度地图 / Leaflet.js npm install vue-baidu-map/npm install leaflet -
在做完相应的准备工作后: 接下来我将大致讲解一下这四个板块如何注入和使用
-
Element Plus:
在main.js或者main.ts文件中直接全局注册该依赖: import { createApp } from 'vue' import App from './App.vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' const app = createApp(App) app.use(ElementPlus) app.mount('#app')
-
socket.io-client(第三方库)
对于基本的 WebSocket 功能,你可以直接使用浏览器内置的 WebSocket API。但如果你想要更高级的功能,比如自动重连等,可以考虑使用第三方库如 socket.io-client。 <template> <div> <h1>WebSocket Example</h1> <button @click="sendMessage">Send Message</button> <ul> <li v-for="(message, index) in messages" :key="index">{{ message }}</li> </ul> </div> </template> <script> import { io } from 'socket.io-client'; export default { data() { return { socket: null, messages: [], }; }, methods: { sendMessage() { if (this.socket) { this.socket.emit('chat message', 'Hello from client!'); } }, addMessage(message) { this.messages.push(message); } }, mounted() { // 创建一个 socket.io 实例并连接到服务器 this.socket = io('http://localhost:3000'); // 替换为你的服务器地址 // 监听 'connect' 事件 this.socket.on('connect', () => { console.log('Connected to server'); }); // 监听从服务器收到的 'chat message' 事件 this.socket.on('chat message', (msg) => { console.log('New message:', msg); this.addMessage(msg); }); }, beforeDestroy() { // 当组件销毁时断开连接 if (this.socket) { this.socket.disconnect(); } } }; </script>
-
ECharts 和 vue-echarts
<template> <v-chart :option="chartOption" /> </template> <script> import { ref, defineComponent } from 'vue'; import VChart from 'vue-echarts'; export default defineComponent({ components: { VChart, }, setup() { const chartOption = ref({ // 你的图表配置选项 }); return { chartOption, }; }, }); </script>
-
百度地图 / Leaflet.js
在项目中引入: import BaiduMap from 'vue-baidu-map' const app = createApp(App) app.use(BaiduMap, { ak: 'YOUR_BAIDU_MAP_API_KEY' // 替换为你的百度地图API密钥 }) 在项目中使用: import L from 'leaflet'; import 'leaflet/dist/leaflet.css'; // 引入样式文件 const map = L.map('map').setView([51.505, -0.09], 13); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors', }).addTo(map);
-
-
-