Ver Fonte

Merge branch 'dev' of http://110.41.163.243:9980/kd-aiot/kd-aiot-frontend-eam into dev

yusheng há 5 meses atrás
pai
commit
89513dc350

+ 131 - 0
src/api/ledgerAssets/robot.js

@@ -0,0 +1,131 @@
+import request from '@/utils/request'
+
+// 获得设备管理分页
+export async function getDevicePage(params) {
+  const res = await request.get('/main/basic/device/pageRobert', { params })
+  if (res.data.code == 0) {
+    return res.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 获得其他设备管理列表
+export async function getOtherDeviceList(params) {
+  const res = await request.get('/isp/device/listOtherDevice', { params })
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 获得其他未绑定的设备列表
+export async function getNonLoadDeviceList(params) {
+  const res = await request.get('/isp/device/listOtherNonLoadDevice', { params })
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 获得设备管理列表
+export async function getDeviceList(params) {
+  const res = await request.get('/isp/device/list', { params })
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 获得设备管理
+export async function getDevice(id) {
+  const res = await request.get('/isp/device/get?id=' + id)
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 摄像头截图
+export async function catchPic(code) {
+  const res = await request.get({
+    url: '/isp/camera/catchPicBin?cameraCode=' + code,
+    responseType: 'blob',
+    returnType: true
+  })
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 摄像头开关灯
+export async function playControl(params) {
+  const res = await request.get('/isp/camera/playControl', { params })
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 创建设备管理
+export async function createDevice(data) {
+  const res = await request.post('/isp/device/create', data)
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 更新设备管理
+export async function updateDevice(data) {
+  const res = await request.put('/isp/device/update', data)
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 删除设备管理
+export async function deletedDevice(id) {
+  const res = await request.delete('/isp/device/delete?ids=' + id)
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 导出设备管理 Excel
+export async function exportDevice(params) {
+  const res = await request.download('/isp/device/export-excel', { params })
+  if (res) {
+    return res
+  }
+  return Promise.reject(new Error(res.message))
+}
+
+// 机器人运动控制
+export async function robotCommandControl(data) {
+  const res = await request.post('/isp/robot/Command/CommandControl', data)
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 机器人运动暂停
+export async function robotStop(data) {
+  const res = await request.post('/isp/robot/Command/Stop', data)
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}
+
+// 机器人状态查询
+export async function GetWorkState(data) {
+  const res = await request.post('/isp/robot/Command/GetWorkState', data)
+  if (res.data.code == 0) {
+    return res.data.data
+  }
+  return Promise.reject(new Error(res.data.message))
+}

+ 6 - 1
src/enum/dict.js

@@ -47,7 +47,12 @@ export default {
   设备管理状态: 'activity_status',
   摄像机类型: 'isp_camera_type',
   摄像头品牌: 'camera_brand',
-  NVR设备类型: 'device_type_nvr'
+  NVR设备类型:'device_type_nvr',
+  机器人品牌: 'isp_robot_brand',
+  机器人类型: 'isp_robot_type',
+  设备管理设备类型: 'isp_device_type',
+  设备品牌: 'device_brand'
+
 };
 
 export const numberList = ['patrol_cycle'];

+ 6 - 6
src/views/ledgerAssets/camera/index.vue

@@ -119,7 +119,7 @@
             headerAlign: 'center',
             align: 'center',
             showOverflowTooltip: true,
-            width: 150
+            width: 180
           },
           {
             prop: 'name',
@@ -135,7 +135,7 @@
             headerAlign: 'center',
             align: 'center',
             showOverflowTooltip: true,
-            width: 150
+            width: 180
           },
           {
             prop: 'modelName',
@@ -143,7 +143,7 @@
             headerAlign: 'center',
             align: 'center',
             showOverflowTooltip: true,
-            width: 150
+            width: 180
           },
           {
             prop: 'classifyName',
@@ -151,7 +151,7 @@
             headerAlign: 'center',
             align: 'center',
             showOverflowTooltip: true,
-            width: 150
+            width: 180
           },
           // {
           //   prop: 'loadDeviceName',
@@ -166,14 +166,14 @@
             label: '摄像机状态',
             align: 'center',
             showOverflowTooltip: true,
-            width: 100,
+            width: 120,
             slot: 'activityStatus'
           },
           {
             columnKey: 'action',
             fixed: 'right',
             label: '操作',
-            width: 150,
+            width: 160,
             align: 'center',
             resizable: false,
             slot: 'action',

+ 221 - 0
src/views/ledgerAssets/robot/components/detail.vue

@@ -0,0 +1,221 @@
+<template>
+  <ele-modal
+    :visible.sync="visible"
+    title="机器人详情"
+    width="1100px"
+    append-to-body
+    @close="cancel"
+  >
+    <el-tabs type="border-card" @tab-change="handleTabChange">
+      <el-tab-pane label="基础信息">
+        <el-form ref="form" :model="form" disabled label-width="120px" v-loading="loading">
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="机器人编号">
+                <span>{{ form.deviceCode }}</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="机器人类型">
+                <span>{{ form.classifyName }}</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="机器人名称">
+                <span>{{ form.name }}</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="品牌">
+                <span>{{ form.brandName }}</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="型号">
+                <span>{{ form.modelName }}</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="排序号">
+                <span>{{ form.deviceSort }}</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="状态">
+                <span :style="{ color: form.activityStatus == 1 ? '#0F83FF' : 'red' }">
+                  {{ form.activityStatus == 1 ? '启用' : '停用' }}
+                </span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+          <div class="tit">连接配置</div>
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="IP">
+                <span>{{ form.extraInfo?.ip }}</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="端口">
+                <span>{{ form.extraInfo?.port }}</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="账号">
+                <span>{{ form.extraInfo?.account }}</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="密码">
+                <span>{{ form.extraInfo?.password }}</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+          <div class="tit">固定负载</div>
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="可见光摄像机">
+                <span>{{ form.kjgLoadDeviceId }}</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="红外摄像机">
+                <span>{{ form.hwLoadDeviceId }}</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+          <div class="tit">其他负载</div>
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="①">
+                <span>{{ form.loadOtherDevice1 }}</span>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="②">
+                <span>{{ form.loadOtherDevice2 }}</span>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </el-tab-pane>
+      <el-tab-pane label="预置位信息">
+        <presetInfo ref="presetInfoRef" :deviceId="form.id" />
+      </el-tab-pane>
+    </el-tabs>
+
+    <template #footer>
+      <el-button @click="cancel">关 闭</el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+import presetInfo from './presetInfo/index.vue'
+import * as RobotApi from '@/api/ledgerAssets/robot'
+
+export default {
+  name: 'RobotDetail',
+  components: {
+    presetInfo
+  },
+  data() {
+    return {
+      visible: false,
+      loading: false,
+      form: {
+        name: undefined,
+        deviceCode: undefined,
+        classifyName: undefined,
+        brandName: undefined,
+        modelName: undefined,
+        deviceSort: undefined,
+        activityStatus: undefined,
+        kjgLoadDeviceId: undefined,
+        hwLoadDeviceId: undefined,
+        loadOtherDevice1: undefined,
+        loadOtherDevice2: undefined,
+        extraInfo: {
+          ip: undefined,
+          port: undefined,
+          account: undefined,
+          password: undefined
+        }
+      }
+    }
+  },
+  methods: {
+    /** 打开弹窗 */
+    open(id) {
+      this.visible = true
+      this.loading = true
+
+      RobotApi.getDevice(id)
+        .then((res) => {
+          this.form = res
+        })
+        .catch((e) => {
+          this.$message.error(e.message)
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+
+    /** 取消 */
+    cancel() {
+      this.resetForm()
+      this.visible = false
+    },
+
+    /** Tab切换 */
+    handleTabChange(index) {
+      if (index == 1) {
+        this.$refs.presetInfoRef?.preSetSuccess()
+      }
+    },
+
+    /** 重置表单 */
+    resetForm() {
+      this.form = {
+        name: undefined,
+        deviceCode: undefined,
+        classifyName: undefined,
+        brandName: undefined,
+        modelName: undefined,
+        deviceSort: undefined,
+        activityStatus: undefined,
+        kjgLoadDeviceId: undefined,
+        hwLoadDeviceId: undefined,
+        loadOtherDevice1: undefined,
+        loadOtherDevice2: undefined,
+        extraInfo: {
+          ip: undefined,
+          port: undefined,
+          account: undefined,
+          password: undefined
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.tit {
+  margin-left: 10px;
+  font-weight: bold;
+  border-left: 3px solid #9266f9;
+  padding-left: 10px;
+  margin-bottom: 14px;
+}
+</style>

+ 522 - 0
src/views/ledgerAssets/robot/components/formModel.vue

@@ -0,0 +1,522 @@
+<template>
+  <ele-modal
+    :visible.sync="visible"
+    :title="title"
+    width="1100px"
+    append-to-body
+    @close="cancel"
+  >
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="120px"
+      class="create-form"
+      v-loading="loading"
+    >
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="所属场站" prop="stationName">
+            <el-input
+              v-model="form.stationName"
+              placeholder="请输入"
+              :maxlength="20"
+              class="!w-100%"
+              disabled
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="所属区域" prop="areaId">
+            <el-tree-select
+              v-model="form.areaId"
+              :data="areaTree"
+              :props="defaultProps"
+              check-strictly
+              default-expand-all
+              placeholder="请选择"
+              value-key="id"
+              @change="areaIdChange"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="机器人类型" prop="classifyCode">
+            <DictSelection
+              dictName="机器人类型"
+              v-model="form.classifyCode"
+              @itemChange="classifyCodeChange"
+            ></DictSelection>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="机器人名称" prop="name">
+            <el-input v-model="form.name" placeholder="请输入" :maxlength="20" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="机器人编号" prop="deviceCode">
+            <el-input
+              v-model="form.deviceCode"
+              placeholder="请输入"
+              :maxlength="50"
+              class="!w-100%"
+              disabled
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="品牌" prop="brandCode">
+            <DictSelection
+              dictName="机器人品牌"
+              v-model="form.brandCode"
+              @itemChange="brandCodeChange"
+            ></DictSelection>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="型号" prop="modelName">
+            <el-input
+              v-model="form.modelName"
+              placeholder="请输入"
+              :maxlength="20"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="排序号" prop="deviceSort">
+            <el-input-number
+              v-model="form.deviceSort"
+              :min="1"
+              :max="99999"
+              class="!w-100%"
+              controls-position="right"
+              placeholder="请输入"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="状态" prop="activityStatus">
+            <el-radio-group v-model="form.activityStatus" class="ml-4" style="flex-wrap: nowrap">
+              <el-radio :label="1">启用</el-radio>
+              <el-radio :label="0">停用</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <div class="tit">连接配置</div>
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="IP" prop="extraInfo.ip">
+            <el-input v-model="form.extraInfo.ip" placeholder="请输入" :maxlength="20" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="端口" prop="extraInfo.port">
+            <el-input-number
+              v-model="form.extraInfo.port"
+              :min="1"
+              :max="65535"
+              :maxlength="5"
+              controls-position="right"
+              placeholder="请输入"
+              class="!w-100%"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="账号" prop="extraInfo.account">
+            <el-input v-model="form.extraInfo.account" placeholder="请输入" :maxlength="30" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="密码" prop="extraInfo.password">
+            <el-input v-model="form.extraInfo.password" placeholder="请输入" :maxlength="30" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <div class="tit">固定负载</div>
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="可见光摄像机">
+            <el-select
+              v-model="form.kjgLoadDeviceId"
+              placeholder="请选择"
+              clearable
+              class="!w-100%"
+            >
+              <el-option
+                v-for="(item, index) in cameraTypeFzKjgList"
+                :key="index"
+                :label="item.name"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="红外摄像机">
+            <el-select
+              v-model="form.hwLoadDeviceId"
+              placeholder="请选择"
+              clearable
+              class="!w-100%"
+            >
+              <el-option
+                v-for="(item, index) in cameraTypeFzHwList"
+                :key="index"
+                :label="item.name"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <div class="tit">其他负载</div>
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="①">
+            <el-select
+              v-model="form.loadOtherDevice1"
+              @change="handleDevice($event, 2)"
+              placeholder="请选择"
+              clearable
+              class="!w-100%"
+            >
+              <el-option
+                v-for="(item, index) in deviceOtherList"
+                :key="index"
+                :label="item.name"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="②">
+            <el-select
+              v-model="form.loadOtherDevice2"
+              @change="handleDevice($event, 3)"
+              placeholder="请选择"
+              clearable
+              class="!w-100%"
+            >
+              <el-option
+                v-for="(item, index) in deviceOtherList"
+                :key="index"
+                :label="item.name"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <template #footer>
+      <el-button @click="cancel">取消</el-button>
+      <el-button type="primary" @click="save" :loading="saving">
+        确定
+      </el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+import dictMixins from '@/mixins/dictMixins';
+import * as RobotApi from '@/api/ledgerAssets/robot';
+import * as AreaApi from '@/api/basic/area';
+import { getCode } from '@/api/ruleManagement/matter';
+// import { getStationData } from '@/utils/auth';
+
+const defForm = {
+  name: '',
+  deviceCode: '',
+  classifyCode: '',
+  classifyName: '',
+  modelId: '',
+  modelName: '',
+  brandCode: '',
+  brandName: '',
+  stationName: '',
+  stationId: '',
+  areaId: '',
+  areaCode: '',
+  areaName: '',
+  deviceSort: 100,
+  activityStatus: 1,
+  kjgLoadDeviceId: undefined,
+  hwLoadDeviceId: undefined,
+  loadOtherDevice1: undefined,
+  loadOtherDevice2: undefined,
+  delayTime: '',
+  listLoad: [],
+  extraInfo: {
+    ip: '',
+    port: 8080,
+    account: '',
+    password: ''
+  }
+}
+
+export default {
+  name: 'RobotForm',
+  mixins: [dictMixins],
+  data() {
+    return {
+      visible: false,
+      title: '',
+      loading: false,
+      saving: false,
+      type: '',
+      areaTree: [],
+      cameraTypeFzKjgList: [],
+      cameraTypeFzHwList: [],
+      deviceOtherList: [],
+      defaultProps: {
+        children: 'children',
+        label: 'name'
+      },
+      form: { ...defForm },
+      rules: {
+        name: [{ required: true, message: '机器人名称不能为空', trigger: 'blur' }],
+        deviceCode: [{ required: true, message: '机器人编号不能为空', trigger: 'blur' }],
+        classifyCode: [{ required: true, message: '机器人类型不能为空', trigger: 'change' }],
+        deviceSort: [{ required: true, message: '排序号不能为空', trigger: 'change' }],
+        stationName: [{ required: true, message: '所属场站不能为空', trigger: 'change' }],
+        areaId: [{ required: true, message: '所属区域不能为空', trigger: 'change' }],
+        extraInfo: {
+          ip: [{ required: true, message: 'IP不能为空', trigger: 'blur' }],
+          port: [{ required: true, message: '端口不能为空', trigger: 'blur' }],
+          account: [{ required: true, message: '账号不能为空', trigger: 'blur' }],
+          password: [{ required: true, message: '密码不能为空', trigger: 'blur' }]
+        }
+      }
+    }
+  },
+  created() {
+    this.requestDict('机器人品牌');
+    this.requestDict('机器人类型');
+  },
+  methods: {
+    brandCodeChange(e) {
+      this.form.brandName = e.dictValue;
+    },
+    classifyCodeChange(e) {
+      this.form.classifyName = e.dictValue;
+    },
+
+    /** 选择关联设备事件 */
+    handleDevice(val, idx) {
+      if (idx == 2) {
+        if (val == this.form.loadOtherDevice2) {
+          this.form.loadOtherDevice2 = '';
+        }
+      } else if (idx == 3) {
+        if (val == this.form.loadOtherDevice1) {
+          this.form.loadOtherDevice1 = '';
+        }
+      }
+    },
+
+    /** 根据关联类型查询对应设备 */
+    async loadDeviceData() {
+      this.cameraTypeFzKjgList = [];
+      this.cameraTypeFzHwList = [];
+      this.deviceOtherList = [];
+      const allList = await RobotApi.getNonLoadDeviceList({});
+      if (allList && allList.length > 0) {
+        for (let obj of allList) {
+          if (!obj.loadId) {
+            if (obj.classifyCode == 'camera_type_fz_kjg') {
+              this.cameraTypeFzKjgList.push(obj);
+            } else if (obj.classifyCode == 'camera_type_fz_hw') {
+              this.cameraTypeFzHwList.push(obj);
+            } else {
+              this.deviceOtherList.push(obj);
+            }
+          }
+        }
+      }
+    },
+
+    /** 获得区域树 */
+    async getAreaTree(stationId) {
+      const data = await AreaApi.getAreaList({ stationId: stationId });
+      this.areaTree = this.$util.toTreeData({
+        data: data.list,
+        idField: 'id',
+        parentIdField: 'parentId'
+      });
+    },
+
+    /** 区域Change事件 */
+    areaIdChange(e) {
+      this.processingAreaId(e, this.areaTree);
+    },
+
+    processingAreaId(id, data) {
+      for (let i = 0; i < data.length; i++) {
+        if (data[i].id == id) {
+          this.form.areaCode = data[i].areaCode;
+          this.form.areaName = data[i].name;
+          return;
+        } else {
+          if (data[i].children && data[i].children.length) {
+            this.processingAreaId(id, data[i].children);
+          }
+        }
+      }
+    },
+
+    /** 打开弹窗 */
+    async open(type, id, station) {
+      this.type = type;
+      this.title = type === 'create' ? '新增机器人' : '编辑机器人';
+      this.form = { ...defForm };
+      this.areaTree = [];
+
+    //   await this.loadDeviceData();
+
+    //   if (getStationData()) {
+    //     this.form.stationName = getStationData().stationName;
+    //     this.form.stationId = getStationData().stationId || getStationData().id;
+    //   }
+      if (station) {
+        this.form.stationName = station.stationName;
+        this.form.stationId = station.stationId || station.id;
+        await this.getAreaTree(this.form.stationId);
+        if (station.stationId) {
+          this.form.areaCode = station.areaCode;
+          this.form.areaName = station.areaName;
+          this.form.areaId = station.id;
+        }
+      }
+
+      if (id) {
+        this.loading = true;
+        try {
+          const res = await RobotApi.getDevice(id);
+          // 排障,避免异常
+          if (!res.extraInfo) {
+            res.extraInfo = { ip: undefined, port: 8080, account: undefined, password: undefined };
+          }
+          // 处理负载设备
+          if (res.listLoad && res.listLoad.length > 0) {
+            for (let device of res.listLoad) {
+              let currLoadDeviceType = device.loadDeviceType,
+                currDeviceId = device.loadDeviceId,
+                loadDeviceName = device.loadDeviceName;
+              let pushObj = { name: loadDeviceName, id: currDeviceId };
+              if (currLoadDeviceType == 'camera_type_fz_kjg') {
+                res.kjgLoadDeviceId = currDeviceId;
+                this.cameraTypeFzKjgList.push(pushObj);
+              } else if (currLoadDeviceType == 'camera_type_fz_hw') {
+                res.hwLoadDeviceId = currDeviceId;
+                this.cameraTypeFzHwList.push(pushObj);
+              } else {
+                if (!res.loadOtherDevice1) {
+                  res.loadOtherDevice1 = currDeviceId;
+                } else {
+                  res.loadOtherDevice2 = currDeviceId;
+                }
+                this.deviceOtherList.push(pushObj);
+              }
+            }
+          } else {
+            res.kjgLoadDeviceId = undefined;
+            res.hwLoadDeviceId = undefined;
+            res.loadOtherDevice1 = undefined;
+            res.loadOtherDevice2 = undefined;
+            res.listLoad = [];
+          }
+          this.form = res;
+          if (this.form.stationId) {
+            await this.getAreaTree(this.form.stationId);
+          }
+        } catch (e) {
+          this.$message.error(e.message);
+        } finally {
+          this.loading = false;
+        }
+      } else {
+        this.form.deviceCode = await getCode('robot_code');
+      }
+
+      this.visible = true;
+    },
+
+    /** 取消 */
+    cancel() {
+      this.form = { ...defForm };
+      this.visible = false;
+    },
+
+    /** 保存 */
+    save() {
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          this.saving = true;
+          const data = { ...this.form };
+
+          // 构建负载列表
+          data.listLoad = [];
+          if (data.kjgLoadDeviceId) {
+            data.listLoad.push({ loadDeviceId: data.kjgLoadDeviceId });
+          }
+          if (data.hwLoadDeviceId) {
+            data.listLoad.push({ loadDeviceId: data.hwLoadDeviceId });
+          }
+          if (data.loadOtherDevice1) {
+            data.listLoad.push({ loadDeviceId: data.loadOtherDevice1 });
+          }
+          if (data.loadOtherDevice2) {
+            data.listLoad.push({ loadDeviceId: data.loadOtherDevice2 });
+          }
+
+          const action =
+            this.type === 'create' ? RobotApi.createDevice(data) : RobotApi.updateDevice(data);
+
+          action
+            .then(() => {
+              this.$message.success(this.type === 'create' ? '新增成功' : '修改成功');
+              this.cancel();
+              this.$emit('success');
+            })
+            .catch((e) => {
+              this.$message.error(e.message);
+            })
+            .finally(() => {
+              this.saving = false;
+            });
+        }
+      });
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.tit {
+  margin-left: 10px;
+  font-weight: bold;
+  border-left: 3px solid #9266f9;
+  padding-left: 10px;
+  margin-bottom: 14px;
+}
+</style>

+ 133 - 0
src/views/ledgerAssets/robot/components/presetInfo/detail.vue

@@ -0,0 +1,133 @@
+<template>
+  <ele-modal
+    :visible.sync="visible"
+    title="预置位详情"
+    width="50%"
+    append-to-body
+    @close="cancel"
+  >
+    <el-form ref="form" :model="form" label-width="120px" v-loading="loading" disabled>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="预置位名称">
+            <span>{{ form.pointName }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="16">
+          <el-form-item label="行车距离">
+            <el-progress :percentage="dirv">
+              <el-button text>3500</el-button>
+            </el-progress>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item>
+            <span>{{ form.dirv }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="16">
+          <el-form-item label="升降距离">
+            <el-progress :percentage="lift">
+              <el-button text>3500</el-button>
+            </el-progress>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item>
+            <span>{{ form.lift }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="排序号">
+            <span>{{ form.indexNo }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <template #footer>
+      <el-button @click="cancel">关闭</el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+import * as DevicePointApi from '@/api/ledgerAssets/device/presetInfo'
+
+const defForm = {
+  pointName: '',
+  dirv: 0,
+  lift: 0,
+  indexNo: 0
+}
+
+export default {
+  name: 'RobotPresetInfoDetail',
+  data() {
+    return {
+      visible: false,
+      loading: false,
+      dirv: 0,
+      lift: 0,
+      form: { ...defForm }
+    }
+  },
+  methods: {
+    /** 打开弹窗 */
+    open(id) {
+      this.visible = true
+      this.loading = true
+
+      DevicePointApi.getDevicePoint(id)
+        .then((res) => {
+          const pointJson = JSON.parse(res.pointJson || '{}')
+          this.form = {
+            ...res,
+            dirv: pointJson.xcjl || 0,
+            lift: pointJson.sjjl || 0
+          }
+          this.dirv = Math.abs(Math.round((pointJson.xcjl || 0) / 35))
+          this.lift = Math.abs(Math.round((pointJson.sjjl || 0) / 35))
+        })
+        .catch((e) => {
+          this.$message.error(e.message)
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+
+    /** 取消 */
+    cancel() {
+      this.resetForm()
+      this.visible = false
+    },
+
+    /** 重置表单 */
+    resetForm() {
+      this.form = { ...defForm }
+      this.dirv = 0
+      this.lift = 0
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.el-col-4 {
+  .el-form-item {
+    :deep(.el-form-item__content) {
+      margin-left: 20px !important;
+    }
+  }
+}
+</style>

+ 226 - 0
src/views/ledgerAssets/robot/components/presetInfo/formModel.vue

@@ -0,0 +1,226 @@
+<template>
+  <ele-modal
+    :visible.sync="visible"
+    :title="title"
+    width="50%"
+    append-to-body
+    @close="cancel"
+  >
+    <el-form ref="form" :model="form" :rules="rules" label-width="120px" v-loading="loading">
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="预置位名称" prop="pointName">
+            <el-input v-model="form.pointName" placeholder="请输入" :maxlength="20" class="!w-100%" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="16">
+          <el-form-item label="行车距离">
+            <el-progress :percentage="dirv">
+              <el-button text>3500</el-button>
+            </el-progress>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item prop="dirv">
+            <el-input
+              v-model="form.dirv"
+              @input="progressUpdate($event, 'dirv')"
+              placeholder="请输入"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="16">
+          <el-form-item label="升降距离">
+            <el-progress :percentage="lift">
+              <el-button text>3500</el-button>
+            </el-progress>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item prop="lift">
+            <el-input
+              v-model="form.lift"
+              @input="progressUpdate($event, 'lift')"
+              placeholder="请输入"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="排序号" prop="indexNo">
+            <el-input-number
+              v-model="form.indexNo"
+              class="!w-100%"
+              placeholder="请输入"
+              :min="1"
+              :max="99999"
+              controls-position="right"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <template #footer>
+      <el-button @click="cancel">取消</el-button>
+      <el-button type="primary" @click="save" :loading="saving">确定</el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+import * as DevicePointApi from '@/api/ledgerAssets/device/presetInfo'
+
+const defForm = {
+  pointName: '',
+  dirv: 2000,
+  lift: 2000,
+  indexNo: 100
+}
+
+export default {
+  name: 'RobotPresetInfoForm',
+  props: {
+    dId: ''
+  },
+  data() {
+    return {
+      visible: false,
+      title: '',
+      loading: false,
+      saving: false,
+      type: '',
+      dirv: 57,
+      lift: 57,
+      form: { ...defForm },
+      rules: {
+        pointName: [{ required: true, message: '预置位名称不能为空', trigger: 'blur' }],
+        dirv: [{ required: true, message: '行车距离不能为空', trigger: 'blur' }],
+        lift: [{ required: true, message: '升降距离不能为空', trigger: 'blur' }],
+        indexNo: [{ required: true, message: '排序号不能为空', trigger: 'change' }]
+      }
+    }
+  },
+  methods: {
+    /** 进度更新 */
+    progressUpdate(value, type) {
+      let num = parseInt(value) || 0
+      if (num > 3500) {
+        num = 3500
+        this.form[type] = num
+      } else if (num < -3500) {
+        num = -3500
+        this.form[type] = num
+      }
+      this[type] = Math.abs(Math.round(num / 35))
+    },
+
+    /** 打开弹窗 */
+    open(type, id) {
+      this.type = type
+      this.title = type === 'create' ? '新增预置位' : '编辑预置位'
+      this.resetForm()
+
+      if (id) {
+        this.loading = true
+        DevicePointApi.getDevicePoint(id)
+          .then((res) => {
+            const pointJson = JSON.parse(res.pointJson || '{}')
+            this.form = {
+              ...res,
+              dirv: pointJson.xcjl || 0,
+              lift: pointJson.sjjl || 0
+            }
+            this.progressUpdate(this.form.dirv, 'dirv')
+            this.progressUpdate(this.form.lift, 'lift')
+          })
+          .catch((e) => {
+            this.$message.error(e.message)
+          })
+          .finally(() => {
+            this.loading = false
+          })
+      }
+
+      this.visible = true
+    },
+
+    /** 取消 */
+    cancel() {
+      this.resetForm()
+      this.visible = false
+    },
+
+    /** 重置表单 */
+    resetForm() {
+      this.form = { ...defForm }
+      this.dirv = 57
+      this.lift = 57
+      this.$refs.form?.resetFields()
+    },
+
+    /** 保存 */
+    save() {
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          this.saving = true
+          const data = {
+            ...this.form,
+            deviceId: this.dId,
+            pointJson: JSON.stringify({
+              xcjl: this.form.dirv,
+              sjjl: this.form.lift
+            })
+          }
+          delete data.dirv
+          delete data.lift
+
+          const action =
+            this.type === 'create'
+              ? DevicePointApi.createDevicePoint(data)
+              : DevicePointApi.updateDevicePoint(data)
+
+          action
+            .then(() => {
+              this.$message.success(this.type === 'create' ? '新增成功' : '修改成功')
+              this.cancel()
+              this.$emit('success')
+            })
+            .catch((e) => {
+              this.$message.error(e.message)
+            })
+            .finally(() => {
+              this.saving = false
+            })
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.el-col-4 {
+  .el-form-item {
+    :deep(.el-form-item__content) {
+      margin-left: 20px !important;
+    }
+  }
+}
+
+.tit {
+  margin-left: 10px;
+  font-weight: bold;
+  border-left: 3px solid #9266f9;
+  padding-left: 10px;
+  margin-bottom: 14px;
+}
+</style>

+ 170 - 0
src/views/ledgerAssets/robot/components/presetInfo/index.vue

@@ -0,0 +1,170 @@
+<template>
+  <div class="ele-body">
+    <el-card shadow="never">
+      <ele-pro-table
+        ref="table"
+        :columns="columns"
+        :datasource="datasource"
+        :selection.sync="selection"
+      >
+        <!-- 表头工具栏 -->
+        <template v-slot:toolbar>
+          <el-button
+            type="danger"
+            icon="el-icon-delete"
+            class="ele-btn-icon"
+            v-if="selection.length"
+            @click="handleBatchDelete"
+          >
+            批量删除
+          </el-button>
+        </template>
+
+        <!-- 操作列 -->
+        <template v-slot:action="{ row }">
+          <el-link
+            type="primary"
+            :underline="false"
+            icon="el-icon-delete"
+            :style="{ color: delColor }"
+            @click="handleDelete(row.pointId)"
+          >
+            删除
+          </el-link>
+        </template>
+      </ele-pro-table>
+    </el-card>
+
+    <!-- 新增编辑弹窗 -->
+    <formModel ref="formRef" :dId="deviceId" @success="reload" />
+  </div>
+</template>
+
+<script>
+import * as DevicePointApi from '@/api/ledgerAssets/device/presetInfo'
+import formModel from './formModel.vue'
+
+export default {
+  name: 'RobotPresetInfo',
+  components: {
+    formModel
+  },
+  props: {
+    deviceId: ''
+  },
+  data() {
+    return {
+      delColor: '#F56C6C',
+      selection: [],
+      // 表格列配置
+      columns: [
+        {
+          width: 45,
+          type: 'selection',
+          columnKey: 'selection',
+          align: 'center',
+          fixed: 'left'
+        },
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 60,
+          align: 'center'
+        },
+        {
+          prop: 'pointName',
+          label: '预置位名称',
+          align: 'left',
+          minWidth: 200,
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'indexNo',
+          label: '排序号',
+          align: 'center',
+          width: 100
+        },
+        {
+          columnKey: 'action',
+          fixed: 'right',
+          label: '操作',
+          width: 80,
+          align: 'center',
+          slot: 'action'
+        }
+      ]
+    }
+  },
+  methods: {
+    /* 表格数据源 */
+    datasource({ page, limit }) {
+      return DevicePointApi.getDevicePointPage({
+        pageNo: page,
+        pageSize: limit,
+        deviceId: this.deviceId
+      }).then((res) => {
+        return {
+          list: res.list.map((item) => ({
+            ...item,
+            pointJson: JSON.parse(item.pointJson || '{}')
+          })),
+          total: res.total
+        }
+      })
+    },
+
+    /* 刷新表格 */
+    reload() {
+      this.$refs.table.reload()
+    },
+
+    /** 预置位初始化 */
+    preSetSuccess() {
+      this.reload()
+    },
+
+    /** 打开表单 */
+    openForm(type, id) {
+      this.$refs.formRef.open(type, id)
+    },
+
+    /** 删除按钮操作 */
+    handleDelete(id) {
+      this.$confirm('是否确认删除该预置位?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          await DevicePointApi.deleteDevicePoint(id)
+          this.$message.success('删除成功')
+          this.reload()
+        })
+        .catch(() => {})
+    },
+
+    /** 批量删除按钮操作 */
+    handleBatchDelete() {
+      if (this.selection.length === 0) {
+        this.$message.warning('请选择要删除的数据项')
+        return
+      }
+      const ids = this.selection.map((item) => item.pointId)
+      this.$confirm('是否确认删除选中的预置位?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          await DevicePointApi.batchDeleteDevicePoint(ids)
+          this.$message.success('删除成功')
+          this.reload()
+        })
+        .catch(() => {})
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 44 - 0
src/views/ledgerAssets/robot/components/robot-search.vue

@@ -0,0 +1,44 @@
+<!-- 搜索表单 -->
+<template>
+  <seekPage :seekList="seekList" :formLength="4" @search="search"></seekPage>
+</template>
+
+<script>
+export default {
+  name: 'RobotSearch',
+  components: {},
+  data() {
+    return {}
+  },
+  computed: {
+    seekList() {
+      return [
+        {
+          label: '机器人名称:',
+          value: 'name',
+          type: 'input',
+          placeholder: '请输入'
+        },
+        {
+          label: '设备品牌:',
+          value: 'brandCode',
+          type: 'DictSelection',
+          dictName: '机器人品牌'
+        },
+        {
+          label: '设备状态:',
+          value: 'activityStatus',
+          type: 'DictSelection',
+          dictName: '设备管理状态'
+        }
+      ]
+    }
+  },
+  methods: {
+    /* 搜索 */
+    search(e) {
+      this.$emit('search', { ...e })
+    }
+  }
+}
+</script>

+ 296 - 0
src/views/ledgerAssets/robot/index.vue

@@ -0,0 +1,296 @@
+<template>
+  <div class="ele-body">
+    <!-- <el-row :gutter="20">
+      <el-col :span="4" :xs="24">
+        <el-card shadow="never" class="h-1/1">
+          <StationTree
+            ref="treeRef"
+            @node-click="handleDeptNodeClick"
+            @nodeChange="handleDeptNodeClick"
+            :isCheckbox="true"
+            :isArea="true"
+          />
+        </el-card>
+      </el-col>
+      <el-col :span="20" :xs="24"> -->
+        <el-card shadow="never">
+          <!-- 搜索表单 -->
+          <robot-search ref="searchRef" @search="reload" />
+          <!-- 数据表格 -->
+          <ele-pro-table
+            ref="table"
+            :columns="columns"
+            :datasource="datasource"
+            :selection.sync="selection"
+            :cache-key="cacheKeyUrl"
+            :response="{
+              dataName: 'data.records',
+              countName: 'data.total'
+            }"
+          >
+            <!-- 表头工具栏 -->
+            <template v-slot:toolbar>
+              <el-button
+                type="primary"
+                icon="el-icon-plus"
+                class="ele-btn-icon"
+                v-if="$hasPermission('ledgerAssets:robot:create')"
+                @click="openForm('create')"
+              >
+                新建
+              </el-button>
+              <el-button
+                type="danger"
+                icon="el-icon-delete"
+                class="ele-btn-icon"
+                v-if="
+                  $hasPermission('ledgerAssets:robot:delete') && selection.length
+                "
+                @click="handleBatchDelete"
+              >
+                批量删除
+              </el-button>
+            </template>
+
+            <!-- 设备状态列 -->
+            <template v-slot:activityStatus="{ row }">
+              <el-switch
+                v-model="row.activityStatus"
+                :active-value="1"
+                :inactive-value="0"
+                @change="handleStatusChange(row)"
+              />
+            </template>
+
+            <!-- 操作列 -->
+            <template v-slot:action="{ row }">
+              <el-link
+                type="primary"
+                :underline="false"
+                icon="el-icon-edit"
+                v-if="$hasPermission('ledgerAssets:robot:update')"
+                @click="openForm('update', row.id)"
+              >
+                编辑
+              </el-link>
+              <el-link
+                type="primary"
+                :underline="false"
+                icon="el-icon-delete"
+                :style="{ color: delColor }"
+                v-if="$hasPermission('ledgerAssets:robot:delete')"
+                @click="handleDelete(row.id)"
+              >
+                删除
+              </el-link>
+            </template>
+          </ele-pro-table>
+        </el-card>
+      <!-- </el-col>
+    </el-row> -->
+
+    <!-- 详情弹窗 -->
+    <detail ref="detailFormRef" />
+
+    <!-- 新增编辑弹窗 -->
+    <formModel ref="formModelRef" @success="reload" />
+  </div>
+</template>
+
+<script>
+import * as RobotApi from '@/api/ledgerAssets/robot';
+import formModel from './components/formModel.vue';
+import robotSearch from './components/robot-search.vue';
+import detail from './components/detail.vue';
+// import StationTree from '@/views/isp/ispLine/area/stationTree.vue';
+
+export default {
+  name: 'LedgerRobot',
+  components: {
+    formModel,
+    robotSearch,
+    detail,
+    // StationTree
+  },
+  data() {
+    return {
+      cacheKeyUrl: 'ledgerRobotTable',
+      delColor: '#F56C6C',
+      selection: [],
+      stationData: {},
+      // 表格列配置
+      columns: [
+        {
+          width: 45,
+          type: 'selection',
+          columnKey: 'selection',
+          align: 'center',
+          fixed: 'left'
+        },
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true,
+          fixed: 'left'
+        },
+        {
+          prop: 'deviceCode',
+          label: '机器人编号',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 150
+        },
+        {
+          prop: 'name',
+          label: '机器人名称',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 200
+        },
+        {
+          prop: 'brandName',
+          label: '设备品牌',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 150
+        },
+        {
+          prop: 'modelName',
+          label: '设备型号',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 150
+        },
+        {
+          prop: 'kjgName',
+          label: '可见光摄像机',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 150
+        },
+        {
+          prop: 'hwName',
+          label: '红外摄影机',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 150
+        },
+        {
+          prop: 'activityStatus',
+          label: '设备状态',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 100,
+          slot: 'activityStatus'
+        },
+        {
+          columnKey: 'action',
+          fixed: 'right',
+          label: '操作',
+          width: 150,
+          align: 'center',
+          resizable: false,
+          slot: 'action',
+          showOverflowTooltip: true
+        }
+      ],
+      loading: false
+    };
+  },
+  methods: {
+    /* 表格数据源 */
+    async datasource({ page, limit, where }) {
+      return await RobotApi.getDevicePage({
+        pageNo: page,
+        pageSize: limit,
+        // stationId: this.stationData.stationId || this.stationData.id || undefined,
+        // areaId: this.stationData.stationId ? this.stationData.id : undefined,
+        ...where
+      });
+    },
+
+    /* 场站树节点点击 */
+    handleDeptNodeClick(data) {
+      this.stationData = data;
+      this.reload();
+    },
+
+    /* 刷新表格 */
+    reload(where) {
+      this.$refs.table.reload({ page: 1, where });
+    },
+
+    /** 添加/修改操作 */
+    openForm(type, id) {
+      this.$refs.formModelRef.open(type, id, this.stationData);
+    },
+
+    /** 状态切换 */
+    handleStatusChange(row) {
+      const text = row.activityStatus === 1 ? '启用' : '停用';
+      this.$confirm('确认要' + text + '机器人"' + row.name + '"吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          row.extInfo = row.extInfo || '{}';
+          await RobotApi.updateDevice(row);
+          this.$message.success(text + '成功');
+          this.reload();
+        })
+        .catch(() => {
+          row.activityStatus = row.activityStatus === 1 ? 0 : 1;
+        });
+    },
+
+    /** 删除按钮操作 */
+    handleDelete(id) {
+      this.$confirm('是否确认删除该数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          await RobotApi.deletedDevice(id);
+          this.$message.success('删除成功');
+          this.reload();
+        })
+        .catch(() => {});
+    },
+
+    /** 批量删除按钮操作 */
+    handleBatchDelete() {
+      if (this.selection.length === 0) {
+        this.$message.warning('请选择要删除的数据项');
+        return;
+      }
+      this.$confirm('是否确认删除选中的数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          const ids = this.selection.map((item) => item.id);
+          for (const id of ids) {
+            await RobotApi.deletedDevice(id);
+          }
+          this.$message.success('删除成功');
+          this.reload();
+        })
+        .catch(() => {});
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 167 - 0
src/views/ledgerAssets/subDevice/components/detail.vue

@@ -0,0 +1,167 @@
+<template>
+  <ele-modal
+    :visible.sync="visible"
+    title="附属设备详情"
+    width="1100px"
+    append-to-body
+    @close="cancel"
+  >
+    <el-form ref="form" :model="form" disabled label-width="120px" v-loading="loading">
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="设备编号">
+            <span>{{ form.deviceCode }}</span>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="设备类型">
+            <span>{{ form.classifyName }}</span>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="设备名称">
+            <span>{{ form.name }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="设备品牌">
+            <span>{{ form.brandName }}</span>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="设备型号">
+            <span>{{ form.modelName }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="排序号">
+            <span>{{ form.deviceSort }}</span>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="状态">
+            <span :style="{ color: form.activityStatus == 1 ? '#0F83FF' : 'red' }">
+              {{ form.activityStatus == 1 ? '启用' : '停用' }}
+            </span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <div class="tit">连接配置</div>
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="IP">
+            <span>{{ form.extraInfo?.ip }}</span>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="端口">
+            <span>{{ form.extraInfo?.port }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="账号">
+            <span>{{ form.extraInfo?.account }}</span>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="密码">
+            <span>{{ form.extraInfo?.password }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <template #footer>
+      <el-button @click="cancel">关 闭</el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+import * as DeviceApi from '@/api/ledgerAssets/device'
+
+export default {
+  name: 'SubDeviceDetail',
+  data() {
+    return {
+      visible: false,
+      loading: false,
+      form: {
+        name: undefined,
+        deviceCode: undefined,
+        classifyName: undefined,
+        brandName: undefined,
+        modelName: undefined,
+        deviceSort: undefined,
+        activityStatus: undefined,
+        extraInfo: {
+          ip: undefined,
+          port: undefined,
+          account: undefined,
+          password: undefined
+        }
+      }
+    }
+  },
+  methods: {
+    /** 打开弹窗 */
+    open(id) {
+      this.visible = true
+      this.loading = true
+
+      DeviceApi.getDevice(id)
+        .then((res) => {
+          this.form = res
+        })
+        .catch((e) => {
+          this.$message.error(e.message)
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+
+    /** 取消 */
+    cancel() {
+      this.resetForm()
+      this.visible = false
+    },
+
+    /** 重置表单 */
+    resetForm() {
+      this.form = {
+        name: undefined,
+        deviceCode: undefined,
+        classifyName: undefined,
+        brandName: undefined,
+        modelName: undefined,
+        deviceSort: undefined,
+        activityStatus: undefined,
+        extraInfo: {
+          ip: undefined,
+          port: undefined,
+          account: undefined,
+          password: undefined
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.tit {
+  margin-left: 10px;
+  font-weight: bold;
+  border-left: 3px solid #9266f9;
+  padding-left: 10px;
+  margin-bottom: 14px;
+}
+</style>

+ 349 - 0
src/views/ledgerAssets/subDevice/components/formModel.vue

@@ -0,0 +1,349 @@
+<template>
+  <ele-modal
+    :visible.sync="visible"
+    :title="title"
+    width="1100px"
+    append-to-body
+    @close="cancel"
+  >
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="120px"
+      class="create-form"
+      v-loading="loading"
+    >
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="所属场站" prop="stationName">
+            <el-input
+              v-model="form.stationName"
+              placeholder="请输入"
+              :maxlength="20"
+              class="!w-100%"
+              disabled
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="所属区域" prop="areaId">
+            <el-tree-select
+              v-model="form.areaId"
+              :data="areaTree"
+              :props="defaultProps"
+              check-strictly
+              default-expand-all
+              placeholder="请选择"
+              value-key="id"
+              @change="areaIdChange"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="设备编号" prop="deviceCode">
+            <el-input
+              v-model="form.deviceCode"
+              placeholder="请输入"
+              :maxlength="50"
+              class="!w-100%"
+              disabled
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="设备类型" prop="classifyCode">
+            <DictSelection
+              dictName="设备管理设备类型"
+              v-model="form.classifyCode"
+              @itemChange="classifyCodeChange"
+            ></DictSelection>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="设备名称" prop="name">
+            <el-input v-model="form.name" placeholder="请输入" :maxlength="20" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="品牌" prop="brandCode">
+            <DictSelection
+              dictName="设备品牌"
+              v-model="form.brandCode"
+              @itemChange="brandCodeChange"
+            ></DictSelection>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="型号" prop="modelName">
+            <el-input
+              v-model="form.modelName"
+              placeholder="请输入"
+              :maxlength="20"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="排序号" prop="deviceSort">
+            <el-input-number
+              v-model="form.deviceSort"
+              :min="1"
+              :max="99999"
+              class="!w-100%"
+              controls-position="right"
+              placeholder="请输入"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="状态" prop="activityStatus">
+            <el-radio-group v-model="form.activityStatus" class="ml-4" style="flex-wrap: nowrap">
+              <el-radio :label="1">启用</el-radio>
+              <el-radio :label="0">停用</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <div class="tit">连接配置</div>
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="IP" prop="extraInfo.ip">
+            <el-input v-model="form.extraInfo.ip" placeholder="请输入" :maxlength="20" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="端口" prop="extraInfo.port">
+            <el-input-number
+              v-model="form.extraInfo.port"
+              :min="1"
+              :max="65535"
+              :maxlength="5"
+              controls-position="right"
+              placeholder="请输入"
+              class="!w-100%"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row :gutter="15">
+        <el-col :span="8">
+          <el-form-item label="账号" prop="extraInfo.account">
+            <el-input v-model="form.extraInfo.account" placeholder="请输入" :maxlength="30" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="密码" prop="extraInfo.password">
+            <el-input v-model="form.extraInfo.password" placeholder="请输入" :maxlength="30" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <template #footer>
+      <el-button @click="cancel">取消</el-button>
+      <el-button type="primary" @click="save" :loading="saving">
+        确定
+      </el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+import dictMixins from '@/mixins/dictMixins';
+import * as DeviceApi from '@/api/ledgerAssets/device';
+import * as AreaApi from '@/api/basic/area';
+import { getCode } from '@/api/ruleManagement/matter';
+// import { getStationData } from '@/utils/auth';
+
+const defForm = {
+  name: '',
+  deviceCode: '',
+  classifyCode: '',
+  classifyName: '',
+  modelId: '',
+  modelName: '',
+  brandCode: '',
+  brandName: '',
+  stationName: '',
+  stationId: '',
+  areaId: '',
+  areaCode: '',
+  areaName: '',
+  deviceSort: 100,
+  activityStatus: 1,
+  listLoad: [],
+  extraInfo: {
+    ip: '',
+    port: 9000,
+    account: '',
+    password: ''
+  }
+}
+
+export default {
+  name: 'SubDeviceForm',
+  mixins: [dictMixins],
+  data() {
+    return {
+      visible: false,
+      title: '',
+      loading: false,
+      saving: false,
+      type: '',
+      areaTree: [],
+      defaultProps: {
+        children: 'children',
+        label: 'name'
+      },
+      form: { ...defForm },
+      rules: {
+        name: [{ required: true, message: '设备名称不能为空', trigger: 'blur' }],
+        deviceCode: [{ required: true, message: '设备编号不能为空', trigger: 'blur' }],
+        classifyCode: [{ required: true, message: '设备类型不能为空', trigger: 'change' }],
+        deviceSort: [{ required: true, message: '排序号不能为空', trigger: 'change' }],
+        stationName: [{ required: true, message: '所属场站不能为空', trigger: 'change' }],
+        areaId: [{ required: true, message: '所属区域不能为空', trigger: 'change' }]
+      }
+    }
+  },
+  created() {
+    this.requestDict('设备品牌');
+    this.requestDict('设备类型');
+  },
+  methods: {
+    brandCodeChange(e) {
+      this.form.brandName = e.dictValue;
+    },
+    classifyCodeChange(e) {
+      this.form.classifyName = e.dictValue;
+    },
+
+    /** 获得区域树 */
+    async getAreaTree(stationId) {
+      const data = await AreaApi.getAreaList({ stationId: stationId });
+      this.areaTree = this.$util.toTreeData({
+        data: data.list,
+        idField: 'id',
+        parentIdField: 'parentId'
+      });
+    },
+
+    /** 区域Change事件 */
+    areaIdChange(e) {
+      this.processingAreaId(e, this.areaTree);
+    },
+
+    processingAreaId(id, data) {
+      for (let i = 0; i < data.length; i++) {
+        if (data[i].id == id) {
+          this.form.areaCode = data[i].areaCode;
+          this.form.areaName = data[i].name;
+          return;
+        } else {
+          if (data[i].children && data[i].children.length) {
+            this.processingAreaId(id, data[i].children);
+          }
+        }
+      }
+    },
+
+    /** 打开弹窗 */
+    async open(type, id, station) {
+      this.type = type;
+      this.title = type === 'create' ? '新增附属设备' : '编辑附属设备';
+      this.form = { ...defForm };
+      this.areaTree = [];
+
+    //   if (getStationData()) {
+    //     this.form.stationName = getStationData().stationName;
+    //     this.form.stationId = getStationData().stationId || getStationData().id;
+    //   }
+      if (station) {
+        this.form.stationName = station.stationName;
+        this.form.stationId = station.stationId || station.id;
+        await this.getAreaTree(this.form.stationId);
+        if (station.stationId) {
+          this.form.areaCode = station.areaCode;
+          this.form.areaName = station.areaName;
+          this.form.areaId = station.id;
+        }
+      }
+
+      if (id) {
+        this.loading = true;
+        try {
+          const res = await DeviceApi.getDevice(id);
+          if (!res.extraInfo) {
+            res.extraInfo = { ip: undefined, port: 9000, account: undefined, password: undefined };
+          }
+          this.form = res;
+          if (this.form.stationId) {
+            await this.getAreaTree(this.form.stationId);
+          }
+        } catch (e) {
+          this.$message.error(e.message);
+        } finally {
+          this.loading = false;
+        }
+      } else {
+        this.form.deviceCode = await getCode('affiliate_device_code');
+      }
+
+      this.visible = true;
+    },
+
+    /** 取消 */
+    cancel() {
+      this.form = { ...defForm };
+      this.visible = false;
+    },
+
+    /** 保存 */
+    save() {
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          this.saving = true;
+          const data = { ...this.form };
+
+          const action =
+            this.type === 'create' ? DeviceApi.createDevice(data) : DeviceApi.updateDevice(data);
+
+          action
+            .then(() => {
+              this.$message.success(this.type === 'create' ? '新增成功' : '修改成功');
+              this.cancel();
+              this.$emit('success');
+            })
+            .catch((e) => {
+              this.$message.error(e.message);
+            })
+            .finally(() => {
+              this.saving = false;
+            });
+        }
+      });
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.tit {
+  margin-left: 10px;
+  font-weight: bold;
+  border-left: 3px solid #9266f9;
+  padding-left: 10px;
+  margin-bottom: 14px;
+}
+</style>

+ 133 - 0
src/views/ledgerAssets/subDevice/components/presetInfo/detail.vue

@@ -0,0 +1,133 @@
+<template>
+  <ele-modal
+    :visible.sync="visible"
+    title="预置位详情"
+    width="50%"
+    append-to-body
+    @close="cancel"
+  >
+    <el-form ref="form" :model="form" label-width="120px" v-loading="loading" disabled>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="预置位名称">
+            <span>{{ form.pointName }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="16">
+          <el-form-item label="行车距离">
+            <el-progress :percentage="dirv">
+              <el-button text>3500</el-button>
+            </el-progress>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item>
+            <span>{{ form.dirv }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="16">
+          <el-form-item label="升降距离">
+            <el-progress :percentage="lift">
+              <el-button text>3500</el-button>
+            </el-progress>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item>
+            <span>{{ form.lift }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="排序号">
+            <span>{{ form.indexNo }}</span>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <template #footer>
+      <el-button @click="cancel">关闭</el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+import * as DevicePointApi from '@/api/ledgerAssets/device/presetInfo'
+
+const defForm = {
+  pointName: '',
+  dirv: 0,
+  lift: 0,
+  indexNo: 0
+}
+
+export default {
+  name: 'SubDevicePresetInfoDetail',
+  data() {
+    return {
+      visible: false,
+      loading: false,
+      dirv: 0,
+      lift: 0,
+      form: { ...defForm }
+    }
+  },
+  methods: {
+    /** 打开弹窗 */
+    open(id) {
+      this.visible = true
+      this.loading = true
+
+      DevicePointApi.getDevicePoint(id)
+        .then((res) => {
+          const pointJson = JSON.parse(res.pointJson || '{}')
+          this.form = {
+            ...res,
+            dirv: pointJson.xcjl || 0,
+            lift: pointJson.sjjl || 0
+          }
+          this.dirv = Math.abs(Math.round((pointJson.xcjl || 0) / 35))
+          this.lift = Math.abs(Math.round((pointJson.sjjl || 0) / 35))
+        })
+        .catch((e) => {
+          this.$message.error(e.message)
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+
+    /** 取消 */
+    cancel() {
+      this.resetForm()
+      this.visible = false
+    },
+
+    /** 重置表单 */
+    resetForm() {
+      this.form = { ...defForm }
+      this.dirv = 0
+      this.lift = 0
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.el-col-4 {
+  .el-form-item {
+    :deep(.el-form-item__content) {
+      margin-left: 20px !important;
+    }
+  }
+}
+</style>

+ 226 - 0
src/views/ledgerAssets/subDevice/components/presetInfo/formModel.vue

@@ -0,0 +1,226 @@
+<template>
+  <ele-modal
+    :visible.sync="visible"
+    :title="title"
+    width="50%"
+    append-to-body
+    @close="cancel"
+  >
+    <el-form ref="form" :model="form" :rules="rules" label-width="120px" v-loading="loading">
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="预置位名称" prop="pointName">
+            <el-input v-model="form.pointName" placeholder="请输入" :maxlength="20" class="!w-100%" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="16">
+          <el-form-item label="行车距离">
+            <el-progress :percentage="dirv">
+              <el-button text>3500</el-button>
+            </el-progress>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item prop="dirv">
+            <el-input
+              v-model="form.dirv"
+              @input="progressUpdate($event, 'dirv')"
+              placeholder="请输入"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="16">
+          <el-form-item label="升降距离">
+            <el-progress :percentage="lift">
+              <el-button text>3500</el-button>
+            </el-progress>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item prop="lift">
+            <el-input
+              v-model="form.lift"
+              @input="progressUpdate($event, 'lift')"
+              placeholder="请输入"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="排序号" prop="indexNo">
+            <el-input-number
+              v-model="form.indexNo"
+              class="!w-100%"
+              placeholder="请输入"
+              :min="1"
+              :max="99999"
+              controls-position="right"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <template #footer>
+      <el-button @click="cancel">取消</el-button>
+      <el-button type="primary" @click="save" :loading="saving">确定</el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+import * as DevicePointApi from '@/api/ledgerAssets/device/presetInfo'
+
+const defForm = {
+  pointName: '',
+  dirv: 2000,
+  lift: 2000,
+  indexNo: 100
+}
+
+export default {
+  name: 'SubDevicePresetInfoForm',
+  props: {
+    dId: ''
+  },
+  data() {
+    return {
+      visible: false,
+      title: '',
+      loading: false,
+      saving: false,
+      type: '',
+      dirv: 57,
+      lift: 57,
+      form: { ...defForm },
+      rules: {
+        pointName: [{ required: true, message: '预置位名称不能为空', trigger: 'blur' }],
+        dirv: [{ required: true, message: '行车距离不能为空', trigger: 'blur' }],
+        lift: [{ required: true, message: '升降距离不能为空', trigger: 'blur' }],
+        indexNo: [{ required: true, message: '排序号不能为空', trigger: 'change' }]
+      }
+    }
+  },
+  methods: {
+    /** 进度更新 */
+    progressUpdate(value, type) {
+      let num = parseInt(value) || 0
+      if (num > 3500) {
+        num = 3500
+        this.form[type] = num
+      } else if (num < -3500) {
+        num = -3500
+        this.form[type] = num
+      }
+      this[type] = Math.abs(Math.round(num / 35))
+    },
+
+    /** 打开弹窗 */
+    open(type, id) {
+      this.type = type
+      this.title = type === 'create' ? '新增预置位' : '编辑预置位'
+      this.resetForm()
+
+      if (id) {
+        this.loading = true
+        DevicePointApi.getDevicePoint(id)
+          .then((res) => {
+            const pointJson = JSON.parse(res.pointJson || '{}')
+            this.form = {
+              ...res,
+              dirv: pointJson.xcjl || 0,
+              lift: pointJson.sjjl || 0
+            }
+            this.progressUpdate(this.form.dirv, 'dirv')
+            this.progressUpdate(this.form.lift, 'lift')
+          })
+          .catch((e) => {
+            this.$message.error(e.message)
+          })
+          .finally(() => {
+            this.loading = false
+          })
+      }
+
+      this.visible = true
+    },
+
+    /** 取消 */
+    cancel() {
+      this.resetForm()
+      this.visible = false
+    },
+
+    /** 重置表单 */
+    resetForm() {
+      this.form = { ...defForm }
+      this.dirv = 57
+      this.lift = 57
+      this.$refs.form?.resetFields()
+    },
+
+    /** 保存 */
+    save() {
+      this.$refs.form.validate((valid) => {
+        if (valid) {
+          this.saving = true
+          const data = {
+            ...this.form,
+            deviceId: this.dId,
+            pointJson: JSON.stringify({
+              xcjl: this.form.dirv,
+              sjjl: this.form.lift
+            })
+          }
+          delete data.dirv
+          delete data.lift
+
+          const action =
+            this.type === 'create'
+              ? DevicePointApi.createDevicePoint(data)
+              : DevicePointApi.updateDevicePoint(data)
+
+          action
+            .then(() => {
+              this.$message.success(this.type === 'create' ? '新增成功' : '修改成功')
+              this.cancel()
+              this.$emit('success')
+            })
+            .catch((e) => {
+              this.$message.error(e.message)
+            })
+            .finally(() => {
+              this.saving = false
+            })
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.el-col-4 {
+  .el-form-item {
+    :deep(.el-form-item__content) {
+      margin-left: 20px !important;
+    }
+  }
+}
+
+.tit {
+  margin-left: 10px;
+  font-weight: bold;
+  border-left: 3px solid #9266f9;
+  padding-left: 10px;
+  margin-bottom: 14px;
+}
+</style>

+ 193 - 0
src/views/ledgerAssets/subDevice/components/presetInfo/index.vue

@@ -0,0 +1,193 @@
+<template>
+  <div class="ele-body">
+    <el-card shadow="never">
+      <ele-pro-table
+        ref="table"
+        :columns="columns"
+        :datasource="datasource"
+        :selection.sync="selection"
+      >
+        <!-- 表头工具栏 -->
+        <template v-slot:toolbar>
+          <el-button
+            type="primary"
+            icon="el-icon-plus"
+            class="ele-btn-icon"
+            @click="openForm('create')"
+          >
+            新建
+          </el-button>
+          <el-button
+            type="danger"
+            icon="el-icon-delete"
+            class="ele-btn-icon"
+            v-if="selection.length"
+            @click="handleBatchDelete"
+          >
+            批量删除
+          </el-button>
+        </template>
+
+        <!-- 操作列 -->
+        <template v-slot:action="{ row }">
+          <el-link
+            type="primary"
+            :underline="false"
+            icon="el-icon-edit"
+            @click="openForm('update', row.pointId)"
+          >
+            编辑
+          </el-link>
+          <el-link
+            type="primary"
+            :underline="false"
+            icon="el-icon-delete"
+            :style="{ color: delColor }"
+            @click="handleDelete(row.pointId)"
+          >
+            删除
+          </el-link>
+        </template>
+      </ele-pro-table>
+    </el-card>
+
+    <!-- 新增编辑弹窗 -->
+    <formModel ref="formRef" :dId="deviceId" @success="reload" />
+  </div>
+</template>
+
+<script>
+import * as DevicePointApi from '@/api/ledgerAssets/device/presetInfo'
+import formModel from './formModel.vue'
+
+export default {
+  name: 'SubDevicePresetInfo',
+  components: {
+    formModel
+  },
+  props: {
+    deviceId: ''
+  },
+  data() {
+    return {
+      delColor: '#F56C6C',
+      selection: [],
+      // 表格列配置
+      columns: [
+        {
+          width: 45,
+          type: 'selection',
+          columnKey: 'selection',
+          align: 'center',
+          fixed: 'left'
+        },
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 60,
+          align: 'center'
+        },
+        {
+          prop: 'pointName',
+          label: '预置位名称',
+          align: 'left',
+          minWidth: 200,
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'pointJson',
+          label: '预置位信息(单位:m)',
+          align: 'center',
+          width: 200,
+          showOverflowTooltip: true
+        },
+        {
+          prop: 'indexNo',
+          label: '排序号',
+          align: 'center',
+          width: 100
+        },
+        {
+          columnKey: 'action',
+          fixed: 'right',
+          label: '操作',
+          width: 150,
+          align: 'center',
+          slot: 'action'
+        }
+      ]
+    }
+  },
+  methods: {
+    /* 表格数据源 */
+    datasource({ page, limit }) {
+      return DevicePointApi.getDevicePointPage({
+        pageNo: page,
+        pageSize: limit,
+        deviceId: this.deviceId
+      }).then((res) => {
+        return {
+          list: res.list.map((item) => ({
+            ...item,
+            pointJson: JSON.parse(item.pointJson || '{}')
+          })),
+          total: res.total
+        }
+      })
+    },
+
+    /* 刷新表格 */
+    reload() {
+      this.$refs.table.reload()
+    },
+
+    /** 预置位初始化 */
+    preSetSuccess() {
+      this.reload()
+    },
+
+    /** 打开表单 */
+    openForm(type, id) {
+      this.$refs.formRef.open(type, id)
+    },
+
+    /** 删除按钮操作 */
+    handleDelete(id) {
+      this.$confirm('是否确认删除该预置位?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          await DevicePointApi.deleteDevicePoint(id)
+          this.$message.success('删除成功')
+          this.reload()
+        })
+        .catch(() => {})
+    },
+
+    /** 批量删除按钮操作 */
+    handleBatchDelete() {
+      if (this.selection.length === 0) {
+        this.$message.warning('请选择要删除的数据项')
+        return
+      }
+      const ids = this.selection.map((item) => item.pointId)
+      this.$confirm('是否确认删除选中的预置位?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          await DevicePointApi.batchDeleteDevicePoint(ids)
+          this.$message.success('删除成功')
+          this.reload()
+        })
+        .catch(() => {})
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 50 - 0
src/views/ledgerAssets/subDevice/components/subdevice-search.vue

@@ -0,0 +1,50 @@
+<!-- 搜索表单 -->
+<template>
+  <seekPage :seekList="seekList" :formLength="4" @search="search"></seekPage>
+</template>
+
+<script>
+export default {
+  name: 'SubDeviceSearch',
+  components: {},
+  data() {
+    return {}
+  },
+  computed: {
+    seekList() {
+      return [
+        {
+          label: '设备名称:',
+          value: 'name',
+          type: 'input',
+          placeholder: '请输入'
+        },
+        {
+          label: '设备品牌:',
+          value: 'brandCode',
+          type: 'DictSelection',
+          dictName: '设备品牌'
+        },
+        {
+          label: '设备类型:',
+          value: 'classifyCode',
+          type: 'DictSelection',
+          dictName: '设备类型'
+        },
+        {
+          label: '设备状态:',
+          value: 'activityStatus',
+          type: 'DictSelection',
+          dictName: '设备管理状态'
+        }
+      ]
+    }
+  },
+  methods: {
+    /* 搜索 */
+    search(e) {
+      this.$emit('search', { ...e })
+    }
+  }
+}
+</script>

+ 296 - 0
src/views/ledgerAssets/subDevice/index.vue

@@ -0,0 +1,296 @@
+<template>
+  <div class="ele-body">
+    <!-- <el-row :gutter="20">
+      <el-col :span="4" :xs="24">
+        <el-card shadow="never" class="h-1/1">
+          <StationTree
+            ref="treeRef"
+            @node-click="handleDeptNodeClick"
+            @nodeChange="handleDeptNodeClick"
+            :isCheckbox="true"
+            :isArea="true"
+          />
+        </el-card>
+      </el-col>
+      <el-col :span="20" :xs="24"> -->
+        <el-card shadow="never">
+          <!-- 搜索表单 -->
+          <subdevice-search ref="searchRef" @search="reload" />
+          <!-- 数据表格 -->
+          <ele-pro-table
+            ref="table"
+            :columns="columns"
+            :datasource="datasource"
+            :selection.sync="selection"
+            :cache-key="cacheKeyUrl"
+            :response="{
+              dataName: 'data.records',
+              countName: 'data.total'
+            }"
+          >
+            <!-- 表头工具栏 -->
+            <template v-slot:toolbar>
+              <el-button
+                type="primary"
+                icon="el-icon-plus"
+                class="ele-btn-icon"
+                v-if="$hasPermission('ledgerAssets:subDevice:create')"
+                @click="openForm('create')"
+              >
+                新建
+              </el-button>
+              <el-button
+                type="danger"
+                icon="el-icon-delete"
+                class="ele-btn-icon"
+                v-if="
+                  $hasPermission('ledgerAssets:subDevice:delete') && selection.length
+                "
+                @click="handleBatchDelete"
+              >
+                批量删除
+              </el-button>
+            </template>
+
+            <!-- 设备状态列 -->
+            <template v-slot:activityStatus="{ row }">
+              <el-switch
+                v-model="row.activityStatus"
+                :active-value="1"
+                :inactive-value="0"
+                @change="handleStatusChange(row)"
+              />
+            </template>
+
+            <!-- 操作列 -->
+            <template v-slot:action="{ row }">
+              <el-link
+                type="primary"
+                :underline="false"
+                icon="el-icon-edit"
+                v-if="$hasPermission('ledgerAssets:subDevice:update')"
+                @click="openForm('update', row.id)"
+              >
+                编辑
+              </el-link>
+              <el-link
+                type="primary"
+                :underline="false"
+                icon="el-icon-delete"
+                :style="{ color: delColor }"
+                v-if="$hasPermission('ledgerAssets:subDevice:delete')"
+                @click="handleDelete(row.id)"
+              >
+                删除
+              </el-link>
+            </template>
+          </ele-pro-table>
+        </el-card>
+      <!-- </el-col>
+    </el-row> -->
+
+    <!-- 详情弹窗 -->
+    <detail ref="detailFormRef" />
+
+    <!-- 新增编辑弹窗 -->
+    <formModel ref="formModelRef" @success="reload" />
+  </div>
+</template>
+
+<script>
+import * as DeviceApi from '@/api/ledgerAssets/device';
+import formModel from './components/formModel.vue';
+import subdeviceSearch from './components/subdevice-search.vue';
+import detail from './components/detail.vue';
+// import StationTree from '@/views/isp/ispLine/area/stationTree.vue';
+
+export default {
+  name: 'LedgerSubDevice',
+  components: {
+    formModel,
+    subdeviceSearch,
+    detail,
+    // StationTree
+  },
+  data() {
+    return {
+      cacheKeyUrl: 'ledgerSubDeviceTable',
+      delColor: '#F56C6C',
+      selection: [],
+      stationData: {},
+      // 表格列配置
+      columns: [
+        {
+          width: 45,
+          type: 'selection',
+          columnKey: 'selection',
+          align: 'center',
+          fixed: 'left'
+        },
+        {
+          columnKey: 'index',
+          label: '序号',
+          type: 'index',
+          width: 55,
+          align: 'center',
+          showOverflowTooltip: true,
+          fixed: 'left'
+        },
+        {
+          prop: 'deviceCode',
+          label: '设备编号',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 150
+        },
+        {
+          prop: 'name',
+          label: '设备名称',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          minWidth: 200
+        },
+        {
+          prop: 'brandName',
+          label: '设备品牌',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 150
+        },
+        {
+          prop: 'modelName',
+          label: '设备型号',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 150
+        },
+        {
+          prop: 'classifyName',
+          label: '设备类型',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 150
+        },
+        {
+          prop: 'loadDeviceName',
+          label: '关联设备',
+          headerAlign: 'center',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 150
+        },
+        {
+          prop: 'activityStatus',
+          label: '设备状态',
+          align: 'center',
+          showOverflowTooltip: true,
+          width: 100,
+          slot: 'activityStatus'
+        },
+        {
+          columnKey: 'action',
+          fixed: 'right',
+          label: '操作',
+          width: 150,
+          align: 'center',
+          resizable: false,
+          slot: 'action',
+          showOverflowTooltip: true
+        }
+      ],
+      loading: false
+    };
+  },
+  methods: {
+    /* 表格数据源 */
+    async datasource({ page, limit, where }) {
+      return await DeviceApi.getDevicePage({
+        pageNo: page,
+        pageSize: limit,
+        stationId: this.stationData.stationId || this.stationData.id || undefined,
+        areaId: this.stationData.stationId ? this.stationData.id : undefined,
+        ...where
+      });
+    },
+
+    /* 场站树节点点击 */
+    handleDeptNodeClick(data) {
+      this.stationData = data;
+      this.reload();
+    },
+
+    /* 刷新表格 */
+    reload(where) {
+      this.$refs.table.reload({ page: 1, where });
+    },
+
+    /** 添加/修改操作 */
+    openForm(type, id) {
+      this.$refs.formModelRef.open(type, id, this.stationData);
+    },
+
+    /** 状态切换 */
+    handleStatusChange(row) {
+      const text = row.activityStatus === 1 ? '启用' : '停用';
+      this.$confirm('确认要' + text + '设备"' + row.name + '"吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          row.extInfo = row.extInfo || '{}';
+          await DeviceApi.updateDevice(row);
+          this.$message.success(text + '成功');
+          this.reload();
+        })
+        .catch(() => {
+          row.activityStatus = row.activityStatus === 1 ? 0 : 1;
+        });
+    },
+
+    /** 删除按钮操作 */
+    handleDelete(id) {
+      this.$confirm('是否确认删除该数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          await DeviceApi.deleteDevice(id);
+          this.$message.success('删除成功');
+          this.reload();
+        })
+        .catch(() => {});
+    },
+
+    /** 批量删除按钮操作 */
+    handleBatchDelete() {
+      if (this.selection.length === 0) {
+        this.$message.warning('请选择要删除的数据项');
+        return;
+      }
+      this.$confirm('是否确认删除选中的数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          const ids = this.selection.map((item) => item.id);
+          for (const id of ids) {
+            await DeviceApi.deleteDevice(id);
+          }
+          this.$message.success('删除成功');
+          this.reload();
+        })
+        .catch(() => {});
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped></style>