《前端图形界面功能增强:从图标缩放到设备属性配置》

文章接上文:进一步完善前端框架搭建及vue-konva依赖的使用(Vscode)-CSDN博客

4.功能完善

添加功能: 1.页面左侧图标栏目中的图标:当鼠标放置在每个图标上时,每个图标会显示出对应的名称。 2.图标属性中添加设置图标device_ID和名称device_name的设置操作。 根据以上代码,添加这两个功能我需要用到什么知识点(列举出),并给出对应的添加或修改的代码,注意一切都是在以上代码基础上修改操作,不得随便更改原本代码。

  1. 添加鼠标放置图标上的功能提示?

    引入<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
  2. 如何实现: 1.自由缩放图标 2.右键菜单:设置 device_ID 与 device_name 3.保存布局,并进行导出 4.导入导出

    ✅ 目标功能列表

    功能编号功能名称描述
    1自由缩放图标支持在画布中对图标进行自由缩放
    2右键菜单:设置 device_id 与 device_name点击图标后弹出输入框让用户填写设备信息
    3保存布局并导出将当前图标位置和配置导出为 JSON 文件
    4导入布局上传 JSON 文件还原图标布局
    1. 功能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. 功能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. 功能3

    4. 功能4

  3. 实现:

    功能模块子功能描述技术建议
    🧪 模拟传感器程序数据模拟模拟温度、湿度、火情等传感器数据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 组件
    报警提示火情触发弹窗或高亮动态提示框、颜色变化等
    1. 🧪 模拟传感器程序:

      ​待更新
    2. 🌐 后端服务:

      ​待更新
    3. 🖥️ 前端展示页面:

      1. 准备工作:

        安装命令
        Element Plusnpm install element-plus --save
        socket.io-client(第三方库)npm install socket.io-client
        ECharts 和 vue-echartsnpm install echarts vue-echarts
        百度地图 / Leaflet.jsnpm install vue-baidu-map/npm install leaflet
      2. 在做完相应的准备工作后: 接下来我将大致讲解一下这四个板块如何注入和使用

        1. 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')
        2. 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>
        3. 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>
        4. 百度地图 / 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: '&copy; OpenStreetMap contributors',
          }).addTo(map);
          ​
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值