Просмотр исходного кода

fix 设备测试界面显示添加

zhanghaijun лет назад: 2
Родитель
Сommit
2ca4dcc45e

+ 11 - 6
train-server/src/main/java/top/haijunit/train/controller/DeviceController.java

@@ -1,10 +1,10 @@
 package top.haijunit.train.controller;
 package top.haijunit.train.controller;
 
 
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
 import top.haijunit.common.domain.ResultData;
 import top.haijunit.common.domain.ResultData;
+import top.haijunit.train.device.domain.CommandEnum;
 import top.haijunit.train.device.entity.DeviceEntity;
 import top.haijunit.train.device.entity.DeviceEntity;
 import top.haijunit.train.device.service.DeviceService;
 import top.haijunit.train.device.service.DeviceService;
 
 
@@ -24,12 +24,17 @@ public class DeviceController {
 
 
     @GetMapping("/all")
     @GetMapping("/all")
     public ResultData<List<DeviceEntity>> device() {
     public ResultData<List<DeviceEntity>> device() {
-        return ResultData.success(deviceService.getAll());
+        return ResultData.success(deviceService.getDeviceAll());
+    }
+
+    @PostMapping("/add")
+    public ResultData<DeviceEntity> addDevice(@RequestBody @Validated DeviceEntity entity){
+        return ResultData.success(deviceService.addDevice(entity));
     }
     }
 
 
     @GetMapping("/elevator")
     @GetMapping("/elevator")
-    public ResultData<String> elevator() {
-        return ResultData.success("");
+    public ResultData<List<DeviceEntity>> elevator() {
+        return ResultData.success(deviceService.getDeviceByCommand(CommandEnum.ELEVATOR_HAND_COMMAND));
     }
     }
 
 
     @GetMapping("/platform/door")
     @GetMapping("/platform/door")

+ 7 - 0
train-server/src/main/java/top/haijunit/train/device/entity/DeviceEntity.java

@@ -4,10 +4,13 @@ import jakarta.persistence.Entity;
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
 import jakarta.persistence.Id;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
 import lombok.Data;
 import lombok.Data;
 import top.haijunit.train.device.domain.CommandEnum;
 import top.haijunit.train.device.domain.CommandEnum;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
+import java.lang.reflect.Member;
 
 
 /**
 /**
  * @author zhanghaijun
  * @author zhanghaijun
@@ -23,14 +26,18 @@ public class DeviceEntity implements Serializable {
     private Long id;
     private Long id;
 
 
     /** 资源类型 */
     /** 资源类型 */
+    @NotNull(message = "资源类型不能为空")
     private CommandEnum command;
     private CommandEnum command;
     /** 标识编码 */
     /** 标识编码 */
+    @NotEmpty(message="设备标识不能为空")
     private String code;
     private String code;
     /** 状态 */
     /** 状态 */
+    @NotNull(message = "资源状态不能为空")
     private Integer status;
     private Integer status;
     /** 其他信息 */
     /** 其他信息 */
     private String jsonStr;
     private String jsonStr;
     // 所属站台
     // 所属站台
+    @NotNull(message = "所属站台不能为空")
     private Long stationId;
     private Long stationId;
 
 
 }
 }

+ 4 - 0
train-server/src/main/java/top/haijunit/train/device/repository/DeviceRepository.java

@@ -5,6 +5,8 @@ import org.springframework.stereotype.Repository;
 import top.haijunit.train.device.domain.CommandEnum;
 import top.haijunit.train.device.domain.CommandEnum;
 import top.haijunit.train.device.entity.DeviceEntity;
 import top.haijunit.train.device.entity.DeviceEntity;
 
 
+import java.util.List;
+
 /**
 /**
  * @author zhanghaijun
  * @author zhanghaijun
  * @date 2023/11/24 11:36
  * @date 2023/11/24 11:36
@@ -14,4 +16,6 @@ import top.haijunit.train.device.entity.DeviceEntity;
 public interface DeviceRepository extends JpaRepository<DeviceEntity, Long> {
 public interface DeviceRepository extends JpaRepository<DeviceEntity, Long> {
 
 
     DeviceEntity findByCommandAndCode(CommandEnum command, String code);
     DeviceEntity findByCommandAndCode(CommandEnum command, String code);
+
+    List<DeviceEntity> findByCommand(CommandEnum command);
 }
 }

+ 20 - 8
train-server/src/main/java/top/haijunit/train/device/service/DeviceService.java

@@ -11,12 +11,7 @@ import org.springframework.stereotype.Service;
 import top.haijunit.common.exception.ServiceException;
 import top.haijunit.common.exception.ServiceException;
 import top.haijunit.train.device.domain.CommandEnum;
 import top.haijunit.train.device.domain.CommandEnum;
 import top.haijunit.train.device.entity.DeviceEntity;
 import top.haijunit.train.device.entity.DeviceEntity;
-import top.haijunit.train.device.entity.PlatformStopEntity;
-import top.haijunit.train.device.entity.StationAfcGateEntity;
-import top.haijunit.train.device.repository.DevicePlatformStopRepository;
 import top.haijunit.train.device.repository.DeviceRepository;
 import top.haijunit.train.device.repository.DeviceRepository;
-import top.haijunit.train.device.repository.DeviceStationAfcGateRepository;
-import top.haijunit.train.domain.constant.DirectionEnum;
 
 
 import java.util.List;
 import java.util.List;
 
 
@@ -33,10 +28,25 @@ public class DeviceService {
     private final DeviceRepository deviceRepository;
     private final DeviceRepository deviceRepository;
 
 
 
 
-    public List<DeviceEntity> getAll(){
+    public List<DeviceEntity> getDeviceAll() {
         return deviceRepository.findAll();
         return deviceRepository.findAll();
     }
     }
 
 
+    public List<DeviceEntity> getDeviceByCommand(CommandEnum command) {
+        return deviceRepository.findByCommand(command);
+    }
+
+    public DeviceEntity addDevice(DeviceEntity entity) {
+        DeviceEntity device = deviceRepository.findByCommandAndCode(entity.getCommand(), entity.getCode());
+        if (null != device) {
+            entity.setId(device.getId());
+        }
+        if (null == entity.getStationId() || entity.getStationId() == 0L) {
+            // 如果没有传车站Id,则默认是中间车站
+            entity.setStationId(104002L);
+        }
+        return deviceRepository.saveAndFlush(entity);
+    }
 
 
     public DeviceEntity updateDevice(String msgStr) {
     public DeviceEntity updateDevice(String msgStr) {
         JSONObject message = JSONUtil.parseObj(msgStr);
         JSONObject message = JSONUtil.parseObj(msgStr);
@@ -58,8 +68,8 @@ public class DeviceService {
         if (StrUtil.isBlank(command.getNameStr()) || StrUtil.isBlank(command.getStatusStr())) {
         if (StrUtil.isBlank(command.getNameStr()) || StrUtil.isBlank(command.getStatusStr())) {
             throw new ServiceException(500, String.format("无法获取对应的参数,cmd: %s, data: %s", command.getCode(), data));
             throw new ServiceException(500, String.format("无法获取对应的参数,cmd: %s, data: %s", command.getCode(), data));
         }
         }
-        String code = Convert.toStr(data.get(command.getNameStr()));
-        Integer status = Convert.toInt(data.get(command.getStatusStr()));
+        String code = Convert.toStr(data.get(command.getNameStr()), "");
+        Integer status = Convert.toInt(data.get(command.getStatusStr()), 0);
         if (StrUtil.isBlank(code) || null == status) {
         if (StrUtil.isBlank(code) || null == status) {
             throw new ServiceException(500, String.format("参数值为空,cmd: %s, data: %s", command.getCode(), data));
             throw new ServiceException(500, String.format("参数值为空,cmd: %s, data: %s", command.getCode(), data));
         }
         }
@@ -68,6 +78,8 @@ public class DeviceService {
             device = new DeviceEntity();
             device = new DeviceEntity();
             device.setCode(code);
             device.setCode(code);
             device.setCommand(command);
             device.setCommand(command);
+            // 默认是中间车站
+            device.setStationId(104002L);
         }
         }
         device.setStatus(status);
         device.setStatus(status);
         device.setJsonStr(getJsonStr(command, device.getJsonStr(), data));
         device.setJsonStr(getJsonStr(command, device.getJsonStr(), data));

+ 2 - 2
train-server/src/main/java/top/haijunit/train/listener/MessageEventListener.java

@@ -44,7 +44,7 @@ public class MessageEventListener implements ApplicationListener<MessageEvent> {
             if (entity == null) {
             if (entity == null) {
                 return;
                 return;
             }
             }
-            log.info("设备信息更新成功: {}", entity.getCode());
+            log.info("设备信息更新成功: {}, message: {}", entity.getCode(), message);
             switch (entity.getCommand()) {
             switch (entity.getCommand()) {
                 case AFC_GATE_MACHINE_STATUS:
                 case AFC_GATE_MACHINE_STATUS:
                     TrainWebSocketUtils.sendMessage(CommandEnum.AFC_GATE_MACHINE, getMessage(CommandEnum.AFC_GATE_MACHINE, entity));
                     TrainWebSocketUtils.sendMessage(CommandEnum.AFC_GATE_MACHINE, getMessage(CommandEnum.AFC_GATE_MACHINE, entity));
@@ -62,7 +62,7 @@ public class MessageEventListener implements ApplicationListener<MessageEvent> {
 
 
             }
             }
         } catch (RuntimeException exception) {
         } catch (RuntimeException exception) {
-            log.error("消息处理异常: {}", message);
+            log.error("消息处理异常: {},消息: {}", exception.getMessage(), message);
         }
         }
     }
     }
 
 

+ 2 - 1
train-server/src/main/java/top/haijunit/train/simulation/train/TrainComputerService.java

@@ -1,6 +1,7 @@
 package top.haijunit.train.simulation.train;
 package top.haijunit.train.simulation.train;
 
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.convert.Convert;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import cn.hutool.json.JSONUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
@@ -207,7 +208,7 @@ public class TrainComputerService {
             List<PlatformDoorEntity> doorList = platformDoorRepository.findByStationIdAndDirection(optional.get().getStationId(), train.getDirection());
             List<PlatformDoorEntity> doorList = platformDoorRepository.findByStationIdAndDirection(optional.get().getStationId(), train.getDirection());
             for (PlatformDoorEntity entity : doorList) {
             for (PlatformDoorEntity entity : doorList) {
                 JSONObject doorData = JSONUtil.createObj();
                 JSONObject doorData = JSONUtil.createObj();
-                doorData.set(CommandEnum.PLATFORM_DOOR_LOCK.getNameStr(), entity.getId());
+                doorData.set(CommandEnum.PLATFORM_DOOR_LOCK.getNameStr(), Convert.toStr(entity.getId(),""));
                 doorData.set(CommandEnum.PLATFORM_DOOR_LOCK.getStatusStr(), state);
                 doorData.set(CommandEnum.PLATFORM_DOOR_LOCK.getStatusStr(), state);
                 TrainWebSocketUtils.sendMessage(CommandEnum.PLATFORM_DOOR_LOCK, doorData);
                 TrainWebSocketUtils.sendMessage(CommandEnum.PLATFORM_DOOR_LOCK, doorData);
             }
             }

+ 1 - 1
train-server/src/main/java/top/haijunit/train/simulation/train/TrainSimulationHolder.java

@@ -34,7 +34,7 @@ public class TrainSimulationHolder {
     }
     }
 
 
     public static synchronized void addRouteList(Long trainId, LinkedList<SectionItem> SectionItemList) {
     public static synchronized void addRouteList(Long trainId, LinkedList<SectionItem> SectionItemList) {
-        TRAVEL_ROUTE_MAP.compute(trainId, (k, v) -> SectionItemList);
+        TRAVEL_ROUTE_MAP.compute(trainId, (k, v) -> new LinkedList<>(SectionItemList));
     }
     }
 
 
     public static LinkedList<SectionItem> getRouteList(Long trainId) {
     public static LinkedList<SectionItem> getRouteList(Long trainId) {

+ 34 - 15
train-server/src/main/resources/templates/webSocket.html

@@ -7,6 +7,7 @@
 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
 <script>
 <script>
     var socket;
     var socket;
+
     //打开WebSocket
     //打开WebSocket
     function openSocket() {
     function openSocket() {
         if (typeof (WebSocket) === "undefined") {
         if (typeof (WebSocket) === "undefined") {
@@ -30,8 +31,15 @@
             };
             };
             //获得消息事件
             //获得消息事件
             socket.onmessage = function (msg) {
             socket.onmessage = function (msg) {
-                console.log(msg.data);
-                //发现消息进入,开始处理前端触发逻辑
+                try {
+                    //发现消息进入,开始处理前端触发逻辑
+                    var data = JSON.parse(msg.data);
+                    if ([10101, 10102, 10103, 10104].indexOf(data.cmd) === -1) {
+                        console.log("收到消息:",msg.data);
+                    }
+                } catch (error) {
+                    console.log(error);
+                }
             };
             };
             //关闭事件
             //关闭事件
             socket.onclose = function () {
             socket.onclose = function () {
@@ -49,27 +57,38 @@
         if (typeof (WebSocket) === "undefined") {
         if (typeof (WebSocket) === "undefined") {
             console.log("您的浏览器不支持WebSocket");
             console.log("您的浏览器不支持WebSocket");
         } else {
         } else {
-            console.log("您的浏览器支持WebSocket");
-            console.log('{"toUID":"' + $("#toUID").val() + '","Msg":"' + $("#msg").val() + '"}');
-            socket.send('{"toUID":"' + $("#toUID").val() + '","Msg":"' + $("#msg").val() + '"}');
+            var data = $("#dataId").val();
+            console.log("发送数据:", data);
+            socket.send(data);
+        }
+    }
+
+    function closeSocket() {
+        if (typeof (WebSocket) === "undefined") {
+            console.log("您的浏览器不支持WebSocket");
+        } else {
+            console.log("WebSocket已经关闭");
+            if (socket != null) {
+                socket.close();
+                socket = null;
+            }
         }
         }
     }
     }
 </script>
 </script>
 <body>
 <body>
-<p>【uid】:
-<div><input id="uid" name="uid" type="text" value="1"></div>
-<p>【toUID】:
-<div><input id="toUID" name="toUID" type="text" value="2"></div>
-<p>【Msg】:
-<div><input id="msg" name="msg" type="text" value="hello WebSocket2"></div>
-<p>【第一步操作:】:
 <div>
 <div>
     <button onclick="openSocket()">开启socket</button>
     <button onclick="openSocket()">开启socket</button>
+    <button onclick="closeSocket()">关闭socket</button>
+</div>
+<div>
+    <label for="dataId">发送数据</label>
+    <input id="dataId" name="uid" type="text" value='{"cmd":20202,"data":{"tvmNo":"5","state":1}}' style="width: 500px;">
 </div>
 </div>
-<p>【第二步操作:】:
 <div>
 <div>
     <button onclick="sendMessage()">发送消息</button>
     <button onclick="sendMessage()">发送消息</button>
 </div>
 </div>
-</body>
-
+<!--<div>-->
+<!--    <label for="messageId">接受数据</label>-->
+<!--    <input id="messageId" name="msg" type="text" value="hello WebSocket2"></div>-->
+<!--</body>-->
 </html>
 </html>

+ 1 - 1
train-web/.editorconfig

@@ -7,7 +7,7 @@ indent_size = 2
 charset = utf-8
 charset = utf-8
 end_of_line = lf
 end_of_line = lf
 indent_style = space
 indent_style = space
-max_line_length = 260
+max_line_length = 280
 trim_trailing_whitespace = true
 trim_trailing_whitespace = true
 insert_final_newline = true
 insert_final_newline = true
 
 

+ 47 - 0
train-web/README.md

@@ -25,4 +25,51 @@
 
 
 ## 参考地址
 ## 参考地址
 
 
+- [VUE3](https://cn.vuejs.org/)
 - [canvas 进阶](https://juejin.cn/column/7244337505495056445)
 - [canvas 进阶](https://juejin.cn/column/7244337505495056445)
+
+## VUE3 参数模板
+
+- [TypeScript 与组合式 API](https://cn.vuejs.org/guide/typescript/composition-api.html)
+
+```vue
+<!-- 子组件 -->
+<template>
+  <h1>ts sample</h1>
+  <p>{{ msg }}</p>
+</template>
+<script setup lang="ts">
+// 第一种方式:分离模式
+interface Props {
+  msg?: string | number | boolean;
+  title?: string[];
+}
+withDefaults(defineProps<Props>(), {
+  msg: "hello",
+  title: () => ["one", "two"],
+});
+
+// 第二种方式:组合模式
+withDefaults(defineProps<{ msg?: string | number | boolean; title?: string }>(), {
+  msg: "hello vite",
+  title: "默认标题",
+});
+
+// 第三种方式:利用接口声明类型
+interface testType {
+  name: string;
+  age: number;
+}
+const props = withDefaults(
+  defineProps<{
+    text: testType;
+  }>(),
+  {
+    text: {
+      name: "flying_dark_feather",
+      age: 20,
+    },
+  }
+);
+</script>
+```

+ 19 - 19
train-web/package.json

@@ -9,31 +9,31 @@
     "preview": "vite preview"
     "preview": "vite preview"
   },
   },
   "dependencies": {
   "dependencies": {
-    "axios": "^1.4.0",
-    "element-plus": "^2.3.9",
+    "axios": "^1.6.5",
+    "element-plus": "^2.5.1",
     "nprogress": "^0.2.0",
     "nprogress": "^0.2.0",
-    "pinia": "^2.1.6",
-    "pinia-plugin-persistedstate": "^3.2.0",
-    "vue": "^3.3.4",
-    "vue-json-pretty": "^2.2.4",
-    "vue-router": "^4.2.4"
+    "pinia": "^2.1.7",
+    "pinia-plugin-persistedstate": "^3.2.1",
+    "vue": "^3.4.13",
+    "vue-json-pretty": "^2.3.0",
+    "vue-router": "^4.2.5"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@iconify-json/ep": "^1.1.12",
-    "@types/node": "^20.5.0",
-    "@types/nprogress": "^0.2.0",
+    "@iconify-json/ep": "^1.1.14",
+    "@types/node": "^20.11.0",
+    "@types/nprogress": "^0.2.3",
     "@vitejs/plugin-legacy": "^4.1.1",
     "@vitejs/plugin-legacy": "^4.1.1",
-    "@vitejs/plugin-vue": "^4.2.3",
-    "@vueuse/core": "^10.3.0",
+    "@vitejs/plugin-vue": "^4.6.2",
+    "@vueuse/core": "^10.7.1",
     "fast-glob": "3.2.11",
     "fast-glob": "3.2.11",
     "postcss-px-to-viewport": "^1.1.1",
     "postcss-px-to-viewport": "^1.1.1",
-    "sass": "^1.65.1",
-    "typescript": "^5.0.2",
-    "unplugin-auto-import": "^0.16.6",
-    "unplugin-icons": "^0.16.5",
-    "unplugin-vue-components": "^0.25.1",
-    "vite": "^4.4.5",
+    "sass": "^1.69.7",
+    "typescript": "^5.3.3",
+    "unplugin-auto-import": "^0.16.7",
+    "unplugin-icons": "^0.16.6",
+    "unplugin-vue-components": "^0.25.2",
+    "vite": "^4.5.1",
     "vite-plugin-svg-icons": "2.0.1",
     "vite-plugin-svg-icons": "2.0.1",
-    "vue-tsc": "^1.8.5"
+    "vue-tsc": "^1.8.27"
   }
   }
 }
 }

Разница между файлами не показана из-за своего большого размера
+ 1017 - 1004
train-web/pnpm-lock.yaml


+ 4 - 0
train-web/src/api/device.ts

@@ -5,3 +5,7 @@ import { DeviceItem } from '@/types/device'
 export function deviceAll(): AxiosPromise<Array<DeviceItem>> {
 export function deviceAll(): AxiosPromise<Array<DeviceItem>> {
   return request({ url: '/api/device/all', method: 'get' })
   return request({ url: '/api/device/all', method: 'get' })
 }
 }
+
+export function addDevice(data: DeviceItem): AxiosPromise<any> {
+  return request({ url: '/api/device/add', method: 'post', data })
+}

+ 1 - 1
train-web/src/components/Device.vue

@@ -24,7 +24,7 @@ interface PropsType {
 }
 }
 withDefaults(defineProps<PropsType>(), {
 withDefaults(defineProps<PropsType>(), {
   imgUrl: '',
   imgUrl: '',
-  devices: [] as DeviceItem[],
+  devices: () => [],
 });
 });
 </script>
 </script>
 <style scoped lang="scss">
 <style scoped lang="scss">

+ 109 - 0
train-web/src/pages/device/DeviceItem.vue

@@ -0,0 +1,109 @@
+<template>
+  <div class="device-box">
+    <div>{{ deviceType.aliasName || '未知的设备类型' }}</div>
+    <!-- 表单 -->
+    <el-form class="device-form" :inline="true" :model="formData" :rules="formRules">
+      <el-form-item label="设备编码" prop="code">
+        <el-input v-model="formData.code" placeholder="设备编码" clearable></el-input>
+      </el-form-item>
+      <el-form-item label="设备状态" prop="status">
+        <el-input v-model="formData.status" placeholder="设备状态" clearable></el-input>
+      </el-form-item>
+      <el-form-item label="设备状态" prop="status">
+        <el-select v-model="formData.status" placeholder="设备状态">
+          <el-option v-for="(label, value) in deviceType.statusOptions" :key="label" :label="label"
+            :value="value"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="其他信息" prop="jsonStr">
+        <el-input v-model="formData.jsonStr" placeholder="其他信息" clearable></el-input>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="addData">添加数据</el-button>
+      </el-form-item>
+    </el-form>
+    <!-- 表格 -->
+    <el-table class="device-table" :data="deviceList" style="width: 100%">
+      <el-table-column prop="id" label="唯一标识"></el-table-column>
+      <el-table-column prop="command" label="指令"></el-table-column>
+      <el-table-column prop="code" label="标识编码"></el-table-column>
+      <!-- <el-table-column prop="status" label="状态"></el-table-column> -->
+      <el-table-column prop="status" label="状态">
+        <template #default="scope">
+          <el-radio-group v-model="scope.row.status">
+            <el-radio v-for="(label, value) in deviceType.statusOptions" :key="value" :label="value">
+              {{ label }}
+            </el-radio>
+          </el-radio-group>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column prop="jsonStr" label="其他信息"></el-table-column> -->
+      <el-table-column prop="stationId" label="所属站台"></el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { DeviceItem, DeviceType } from "@/types/device";
+import { deviceAll, addDevice } from "@/api/device";
+
+// 状态选择
+const pops = withDefaults(defineProps<{ deviceType: DeviceType }>(), {
+  // deviceType: PLATFORM_PRESSING_STOP
+});
+
+// 表单
+const addData = async () => {
+  formData.command = pops.deviceType.command;
+  console.log(formData);
+  const result = await addDevice(formData);
+  console.log(result);
+}
+const formData = reactive<DeviceItem>({
+  id: '',
+  command: 0,
+  code: 0,
+  status: '0',
+  jsonStr: '',
+  stationId: 0,
+});
+const formRules = reactive({
+  code: [
+    { required: true, message: '请输入标识编码', trigger: 'blur' },
+  ],
+  status: [
+    { required: true, message: '请输入状态', trigger: 'blur' },
+    // { type: 'number', message: '状态必须为数字', trigger: 'blur' },
+  ],
+  jsonStr: [
+    {
+      validator: (_rule: any, value: any, callback: (error?: string | Error) => void) => {
+        try {
+          JSON.parse(value);
+          callback();
+        } catch (error) {
+          callback(new Error('必须是合法的 JSON 字符串'));
+        }
+      },
+      trigger: 'blur',
+    },
+  ]
+});
+
+// 表格
+const deviceList = ref<DeviceItem[]>([]);
+onMounted(async () => {
+  const response = await deviceAll();
+  deviceList.value = response.data;
+});
+</script>
+
+<style scoped lang="scss">
+.device-form .el-input {
+  --el-input-width: 220px;
+}
+
+.device-form .el-select {
+  --el-select-width: 220px;
+}
+</style>

+ 29 - 8
train-web/src/pages/device/DeviceTest.vue

@@ -1,13 +1,32 @@
 <template>
 <template>
   <div class="box">
   <div class="box">
     <el-tabs tab-position="left" style="height: 500px" class="demo-tabs">
     <el-tabs tab-position="left" style="height: 500px" class="demo-tabs">
-      <el-tab-pane v-for="(device, deviceIndex) in deviceList" :key="deviceIndex" :label="device.description">
-        {{ device.description }}
-        <el-table :data="getDevice(device)" stripe class="device-table">
-          <el-table-column prop="code" label="设备名称" />
-          <el-table-column prop="status" label="设备状态" width="180" />
-          <el-table-column prop="" label="操作" width="260" />
-        </el-table>
+      <el-tab-pane v-for="(device, deviceIndex) in deviceList" :key="deviceIndex" :label="device.aliasName">
+        <div style="display: flex;flex-direction: column;">
+          <div>{{ device.aliasName }}</div>
+          <!-- <el-form :model="formItem">
+            <el-form-item label="设备名称">
+              <el-input :model="formItem.aliasName" />
+            </el-form-item>
+            <el-form-item label="设备状态">
+              {{ '状态' }}
+            </el-form-item>
+          </el-form> -->
+
+          <hr />
+          <el-table :data="getDevice(device)" stripe class="device-table">
+            <el-table-column prop="code" label="设备名称" />
+            <el-table-column prop="status" label="设备状态" width="180" />
+            <el-table-column prop="" label="操作" width="260">
+              {{ device.statusOptions }}
+              <!-- <el-radio-group :model="device.statusOptions" label="label position">
+              <el-radio-button label="left">Left</el-radio-button>
+              <el-radio-button label="right">Right</el-radio-button>
+              <el-radio-button label="top">Top</el-radio-button>
+            </el-radio-group> -->
+            </el-table-column>
+          </el-table>
+        </div>
       </el-tab-pane>
       </el-tab-pane>
     </el-tabs>
     </el-tabs>
   </div>
   </div>
@@ -16,6 +35,9 @@
 import { DeviceType, DeviceItem, deviceTypeSocket as deviceList } from "@/types/device";
 import { DeviceType, DeviceItem, deviceTypeSocket as deviceList } from "@/types/device";
 import { deviceAll } from "@/api/device";
 import { deviceAll } from "@/api/device";
 
 
+// 添加设备
+// const formItem = ref<DeviceItem>();
+
 const deviceItemAll = ref<DeviceItem[]>([]);
 const deviceItemAll = ref<DeviceItem[]>([]);
 onMounted(async () => {
 onMounted(async () => {
   const res = await deviceAll();
   const res = await deviceAll();
@@ -23,7 +45,6 @@ onMounted(async () => {
 });
 });
 function getDevice(deviceType: DeviceType): DeviceItem[] {
 function getDevice(deviceType: DeviceType): DeviceItem[] {
   const list = deviceItemAll.value.filter((item: DeviceItem) => item.command === deviceType.command);
   const list = deviceItemAll.value.filter((item: DeviceItem) => item.command === deviceType.command);
-  console.log(deviceItemAll.value, list, deviceType.description);
   return list;
   return list;
 }
 }
 </script>
 </script>

+ 5 - 1
train-web/src/pages/device/index.vue

@@ -15,6 +15,9 @@
     <el-tab-pane label="测试设备" name="test">
     <el-tab-pane label="测试设备" name="test">
       <device-test></device-test>
       <device-test></device-test>
     </el-tab-pane>
     </el-tab-pane>
+    <!-- <el-tab-pane label="测试设备" name="testItem">
+      <deviec-item></deviec-item>
+    </el-tab-pane> -->
   </el-tabs>
   </el-tabs>
 </template>
 </template>
 <script lang="ts" setup>
 <script lang="ts" setup>
@@ -23,6 +26,7 @@ import DeviceWater from "./DeviceWater.vue";
 import DeviceDoor from "./DeviceDoor.vue";
 import DeviceDoor from "./DeviceDoor.vue";
 import DeviceElevator from "./DeviceElevator.vue";
 import DeviceElevator from "./DeviceElevator.vue";
 import DeviceTest from "./DeviceTest.vue"
 import DeviceTest from "./DeviceTest.vue"
+// import DeviecItem from "./DeviceItem.vue"
 
 
-const activeName = ref('test')
+const activeName = ref('testItem')
 </script>
 </script>

+ 10 - 0
train-web/src/types/auto-imports.d.ts

@@ -42,6 +42,7 @@ declare global {
   const h: typeof import('vue')['h']
   const h: typeof import('vue')['h']
   const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
   const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
   const inject: typeof import('vue')['inject']
   const inject: typeof import('vue')['inject']
+  const injectLocal: typeof import('@vueuse/core')['injectLocal']
   const isDefined: typeof import('@vueuse/core')['isDefined']
   const isDefined: typeof import('@vueuse/core')['isDefined']
   const isProxy: typeof import('vue')['isProxy']
   const isProxy: typeof import('vue')['isProxy']
   const isReactive: typeof import('vue')['isReactive']
   const isReactive: typeof import('vue')['isReactive']
@@ -76,6 +77,7 @@ declare global {
   const onUpdated: typeof import('vue')['onUpdated']
   const onUpdated: typeof import('vue')['onUpdated']
   const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
   const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
   const provide: typeof import('vue')['provide']
   const provide: typeof import('vue')['provide']
+  const provideLocal: typeof import('@vueuse/core')['provideLocal']
   const reactify: typeof import('@vueuse/core')['reactify']
   const reactify: typeof import('@vueuse/core')['reactify']
   const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
   const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
   const reactive: typeof import('vue')['reactive']
   const reactive: typeof import('vue')['reactive']
@@ -142,6 +144,7 @@ declare global {
   const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
   const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
   const useCached: typeof import('@vueuse/core')['useCached']
   const useCached: typeof import('@vueuse/core')['useCached']
   const useClipboard: typeof import('@vueuse/core')['useClipboard']
   const useClipboard: typeof import('@vueuse/core')['useClipboard']
+  const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
   const useCloned: typeof import('@vueuse/core')['useCloned']
   const useCloned: typeof import('@vueuse/core')['useCloned']
   const useColorMode: typeof import('@vueuse/core')['useColorMode']
   const useColorMode: typeof import('@vueuse/core')['useColorMode']
   const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
   const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
@@ -298,6 +301,7 @@ declare global {
 declare global {
 declare global {
   // @ts-ignore
   // @ts-ignore
   export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
   export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
+  import('vue')
 }
 }
 // for vue template auto import
 // for vue template auto import
 import { UnwrapRef } from 'vue'
 import { UnwrapRef } from 'vue'
@@ -340,6 +344,7 @@ declare module 'vue' {
     readonly h: UnwrapRef<typeof import('vue')['h']>
     readonly h: UnwrapRef<typeof import('vue')['h']>
     readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
     readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
     readonly inject: UnwrapRef<typeof import('vue')['inject']>
     readonly inject: UnwrapRef<typeof import('vue')['inject']>
+    readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']>
     readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
     readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
     readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
     readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
     readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
     readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
@@ -374,6 +379,7 @@ declare module 'vue' {
     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
     readonly provide: UnwrapRef<typeof import('vue')['provide']>
     readonly provide: UnwrapRef<typeof import('vue')['provide']>
+    readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
     readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
     readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
     readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
     readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
     readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
     readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
@@ -440,6 +446,7 @@ declare module 'vue' {
     readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
     readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
     readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
     readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
     readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
     readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
+    readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']>
     readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
     readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
     readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
     readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
     readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
     readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
@@ -632,6 +639,7 @@ declare module '@vue/runtime-core' {
     readonly h: UnwrapRef<typeof import('vue')['h']>
     readonly h: UnwrapRef<typeof import('vue')['h']>
     readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
     readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
     readonly inject: UnwrapRef<typeof import('vue')['inject']>
     readonly inject: UnwrapRef<typeof import('vue')['inject']>
+    readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']>
     readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
     readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
     readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
     readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
     readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
     readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
@@ -666,6 +674,7 @@ declare module '@vue/runtime-core' {
     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
     readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
     readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
     readonly provide: UnwrapRef<typeof import('vue')['provide']>
     readonly provide: UnwrapRef<typeof import('vue')['provide']>
+    readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
     readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
     readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
     readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
     readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
     readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
     readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
@@ -732,6 +741,7 @@ declare module '@vue/runtime-core' {
     readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
     readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
     readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
     readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
     readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
     readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
+    readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']>
     readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
     readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
     readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
     readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
     readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
     readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>

+ 2 - 2
train-web/src/types/device.ts

@@ -1,8 +1,8 @@
 export interface DeviceItem {
 export interface DeviceItem {
-  id: string; // 唯一标识
+  id?: string; // 唯一标识
   command: number; // 指令
   command: number; // 指令
   code: number; // 标识编码
   code: number; // 标识编码
-  status: number; // 状态
+  status: string; // 状态
   jsonStr: string; // 其他信息
   jsonStr: string; // 其他信息
   stationId: number; // 所属站台
   stationId: number; // 所属站台
 }
 }

+ 1 - 0
train-web/src/types/global.d.ts

@@ -0,0 +1 @@
+declare module "element-plus";

+ 5 - 0
train-web/src/types/typing.d.ts

@@ -0,0 +1,5 @@
+export default class SomeClass extends Vue {
+  public $refs!: {
+    scrollView: ScrollView
+  }
+}