V4修复: jsx改用onInit+columns+render 组件自含对话框

This commit is contained in:
2026-05-17 13:42:39 +08:00
parent b0a3141c79
commit 715c42fd4d
5 changed files with 106 additions and 78 deletions

View File

@@ -1,72 +1,33 @@
/** /**
* base_device 页面操作列扩展 * base_device 页面操作列扩展
* 按 DeviceGroup 动态渲染对应的操作按钮组件 * 在 onInit 中动态注入自定义操作列,按 DeviceGroup 渲染对应按钮组件
* 框架升级不覆盖此文件extension 目录受保护) * 对话框由各按钮组件内部自行管理
*/ */
import { h } from 'vue'
import VideoDeviceActions from '@/views/warehouse/device_manager/base_device/components/VideoDeviceActions.vue' import VideoDeviceActions from '@/views/warehouse/device_manager/base_device/components/VideoDeviceActions.vue'
import IoTDeviceActions from '@/views/warehouse/device_manager/base_device/components/IoTDeviceActions.vue' import IoTDeviceActions from '@/views/warehouse/device_manager/base_device/components/IoTDeviceActions.vue'
import AccessDeviceActions from '@/views/warehouse/device_manager/base_device/components/AccessDeviceActions.vue'
import BarrierDeviceActions from '@/views/warehouse/device_manager/base_device/components/BarrierDeviceActions.vue'
import AlarmDeviceActions from '@/views/warehouse/device_manager/base_device/components/AlarmDeviceActions.vue'
import DeviceLivePreview from '@/views/warehouse/device_manager/base_device/components/DeviceLivePreview.vue'
import PtzControlPanel from '@/views/warehouse/device_manager/base_device/components/PtzControlPanel.vue'
import RealtimeDataPanel from '@/views/warehouse/device_manager/base_device/components/RealtimeDataPanel.vue'
import DeviceControlPanel from '@/views/warehouse/device_manager/base_device/components/DeviceControlPanel.vue'
import DeviceEditDialog from '@/views/warehouse/device_manager/base_device/components/DeviceEditDialog.vue'
import MapBindingPanel from '@/views/warehouse/device_manager/base_device/components/MapBindingPanel.vue'
// 按 DeviceGroup 映射操作组件
const actionMap = { const actionMap = {
'视频设备': 'VideoDeviceActions', '视频设备': VideoDeviceActions,
'IoT设备': 'IoTDeviceActions', 'IoT设备': IoTDeviceActions,
'门禁设备': 'AccessDeviceActions',
'道闸设备': 'BarrierDeviceActions',
'报警设备': 'AlarmDeviceActions',
} }
export default { export default {
components: { onInit() {
VideoDeviceActions, IoTDeviceActions, AccessDeviceActions, BarrierDeviceActions, AlarmDeviceActions, const gcol = this.columns.find(c => c.field === 'DeviceGroup')
DeviceLivePreview, PtzControlPanel, RealtimeDataPanel, DeviceControlPanel, DeviceEditDialog, MapBindingPanel if (gcol) gcol.hidden = false
}, this.columns.splice(0, 0, {
field: '_actions',
data() { title: '操作',
return { type: 'string',
currentDevice: null, width: 300,
dialogs: { preview: false, ptz: false, realtime: false, control: false, edit: false, map: false } fixed: 'right',
} align: 'left',
}, render: (h, { row }) => {
const comp = actionMap[row.DeviceGroup]
// 替换框架操作列 if (!comp) return null
slots: { return h(comp, { row })
'col-action': (h, { row }) => { }
const compName = actionMap[row.deviceGroup] })
if (!compName) return null
// 渲染自定义按钮组,绑定事件
return h(compName, {
props: { row },
on: {
preview: () => this.openDialog('preview', row),
ptz: () => this.openDialog('ptz', row),
playback: () => console.log('playback', row),
snapshot: () => console.log('snapshot', row),
syncChannels: () => console.log('syncChannels', row),
realtime: () => this.openDialog('realtime', row),
control: () => this.openDialog('control', row),
refresh: () => this.$emit('refresh'),
alarms: () => console.log('alarms', row),
}
})
}
},
methods: {
openDialog(name, device) {
this.currentDevice = device
this.dialogs[name] = true
},
onRefresh() {
this.$emit('refresh')
}
} }
} }

View File

@@ -1,16 +1,20 @@
<template> <template>
<div style="display:flex;gap:4px;flex-wrap:wrap"> <div style="display:flex;gap:4px;flex-wrap:wrap">
<el-button size="small" type="primary" @click="$emit('realtime')">实时数据</el-button> <el-button size="small" type="primary" @click="realtimeVisible=true">实时数据</el-button>
<el-button v-if="isControl" size="small" @click="$emit('control')">设备控制</el-button> <el-button v-if="isControl" size="small" @click="controlVisible=true">设备控制</el-button>
<el-button size="small" @click="$emit('refresh')">刷新</el-button> <el-button size="small" @click="$emit('refresh')">刷新</el-button>
<el-button size="small" type="warning" @click="$emit('alarms')">查看告警</el-button> <el-button size="small" type="warning" @click="$emit('alarms')">查看告警</el-button>
<RealtimeDataPanel v-model="realtimeVisible" :device="row" />
<DeviceControlPanel v-model="controlVisible" :device="row" />
</div> </div>
</template> </template>
<script setup> <script setup>
import { computed } from 'vue' import { ref, computed } from 'vue'
import RealtimeDataPanel from './RealtimeDataPanel.vue'
import DeviceControlPanel from './DeviceControlPanel.vue'
const props = defineProps({ row: { type: Object, default: () => ({}) } }) const props = defineProps({ row: { type: Object, default: () => ({}) } })
defineEmits(['realtime','control','refresh','alarms']) defineEmits(['refresh','alarms'])
// 通过 ExtraData 判断是否为控制点 const realtimeVisible = ref(false), controlVisible = ref(false)
const isControl = computed(() => { const isControl = computed(() => {
try { return JSON.parse(props.row?.extraData || '{}')?.isControlPoint === true } catch { return false } try { return JSON.parse(props.row?.extraData || '{}')?.isControlPoint === true } catch { return false }
}) })

View File

@@ -1,23 +1,29 @@
<template> <template>
<div style="display:flex;gap:4px;flex-wrap:wrap"> <div style="display:flex;gap:4px;flex-wrap:wrap">
<el-button size="small" type="primary" @click="$emit('preview')"> <el-button size="small" type="primary" @click="previewVisible=true">
<el-icon><VideoPlay /></el-icon> 实时预览 <el-icon><VideoPlay /></el-icon> 预览
</el-button> </el-button>
<el-button size="small" @click="$emit('ptz')"> <el-button size="small" @click="ptzVisible=true">
<el-icon><Aim /></el-icon> 云台控制 <el-icon><Aim /></el-icon> 云台
</el-button> </el-button>
<el-button size="small" @click="$emit('playback')"> <el-button size="small" @click="$emit('playback')">
<el-icon><VideoCamera /></el-icon> 查看回放 <el-icon><VideoCamera /></el-icon> 回放
</el-button> </el-button>
<el-button size="small" @click="$emit('snapshot')"> <el-button size="small" @click="$emit('snapshot')">
<el-icon><Camera /></el-icon> 获取快照 <el-icon><Camera /></el-icon> 快照
</el-button> </el-button>
<el-button v-if="row?.isParent==='是'" size="small" type="warning" @click="$emit('syncChannels')"> <el-button v-if="row?.isParent==='是'" size="small" type="warning" @click="$emit('syncChannels')">
同步通道 同步通道
</el-button> </el-button>
<DeviceLivePreview v-model="previewVisible" :device="row" />
<PtzControlPanel v-model="ptzVisible" :device="row" />
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'
import DeviceLivePreview from './DeviceLivePreview.vue'
import PtzControlPanel from './PtzControlPanel.vue'
defineProps({ row: { type: Object, default: () => ({}) } }) defineProps({ row: { type: Object, default: () => ({}) } })
defineEmits(['preview','ptz','playback','snapshot','syncChannels']) defineEmits(['playback','snapshot','syncChannels'])
const previewVisible = ref(false), ptzVisible = ref(false)
</script> </script>

View File

@@ -30,15 +30,14 @@ export default function(){
const searchFormOptions = []; const searchFormOptions = [];
const columns = [{field:'DeviceId',title:'设备ID',type:'int',width:110,hidden:true,require:true,align:'left'}, const columns = [{field:'DeviceId',title:'设备ID',type:'int',width:110,hidden:true,require:true,align:'left'},
{field:'DeviceName',title:'设备名称',type:'string',link:true,width:120,require:true,align:'left'}, {field:'DeviceName',title:'设备名称',type:'string',link:true,width:120,require:true,align:'left'},
{field:'AdapterCode',title:'来源适配器(类型:实例)',type:'string',width:110,hidden:true,require:true,align:'left'}, {field:'AdapterCode',title:'来源适配器(类型:实例)',type:'string',width:110,hidden:true,align:'left'},
{field:'SourceId',title:'源系统设备ID',type:'string',width:120,require:true,align:'left'}, {field:'SourceId',title:'源系统设备ID',type:'string',width:120,align:'left'},
{field:'DeviceCategory',title:'设备种类(数据字典)',type:'string',bind:{ key:'设备种类',data:[]},width:110,require:true,align:'left'}, {field:'DeviceCategory',title:'设备种类(数据字典)',type:'string',bind:{ key:'设备种类',data:[]},width:110,require:true,align:'left'},
{field:'DeviceGroup',title:'设备分组(数据字典)',type:'string',bind:{ key:'设备分组',data:[]},width:110,hidden:true,require:true,align:'left'}, {field:'DeviceGroup',title:'设备分组(数据字典)',type:'string',bind:{ key:'设备分组',data:[]},width:110,hidden:true,require:true,align:'left'},
{field:'PointId',title:'所属点位ID',type:'int',width:110,hidden:true,align:'left'}, {field:'PointId',title:'所属点位ID',type:'int',width:110,hidden:true,align:'left'},
{field:'GatewayNodeId',title:'所属网关节点ID',type:'int',width:110,hidden:true,align:'left'},
{field:'IsParent',title:'是否父设备(数据字典)',type:'string',bind:{ key:'是否父设备',data:[]},width:110,require:true,align:'left'}, {field:'IsParent',title:'是否父设备(数据字典)',type:'string',bind:{ key:'是否父设备',data:[]},width:110,require:true,align:'left'},
{field:'ParentDeviceId',title:'父设备ID(自引用,子设备挂父设备下)',type:'int',width:110,hidden:true,align:'left'}, {field:'ParentDeviceId',title:'父设备ID(自引用,子设备挂父设备下)',type:'int',width:110,hidden:true,align:'left'},
{field:'IsOnline',title:'在线状态(数据字典)',type:'string',bind:{ key:'在线状态',data:[]},width:110,require:true,align:'left'}, {field:'IsOnline',title:'在线状态(数据字典)',type:'string',bind:{ key:'在线状态',data:[]},width:110,align:'left'},
{field:'IpAddress',title:'IP地址',type:'string',width:110,align:'left'}, {field:'IpAddress',title:'IP地址',type:'string',width:110,align:'left'},
{field:'Port',title:'端口',type:'int',width:110,align:'left'}, {field:'Port',title:'端口',type:'int',width:110,align:'left'},
{field:'Location',title:'安装位置',type:'string',width:180,align:'left'}, {field:'Location',title:'安装位置',type:'string',width:180,align:'left'},
@@ -56,7 +55,8 @@ export default function(){
{field:'CreateDate',title:'创建时间',type:'datetime',width:110,align:'left'}, {field:'CreateDate',title:'创建时间',type:'datetime',width:110,align:'left'},
{field:'ModifyID',title:'修改人ID',type:'int',width:80,hidden:true,align:'left'}, {field:'ModifyID',title:'修改人ID',type:'int',width:80,hidden:true,align:'left'},
{field:'Modifier',title:'修改人',type:'string',width:100,align:'left'}, {field:'Modifier',title:'修改人',type:'string',width:100,align:'left'},
{field:'ModifyDate',title:'修改时间',type:'datetime',width:110,align:'left'}]; {field:'ModifyDate',title:'修改时间',type:'datetime',width:110,align:'left'},
{field:'NodeId',title:'所属网关节点ID',type:'int',width:120,hidden:true,align:'left'}];
const detail ={columns:[]}; const detail ={columns:[]};
const details = [ { const details = [ {
cnName: '视频通道', cnName: '视频通道',

View File

@@ -40,7 +40,64 @@ export default function(){
{field:'Modifier',title:'修改人',type:'string',width:100,align:'left'}, {field:'Modifier',title:'修改人',type:'string',width:100,align:'left'},
{field:'ModifyDate',title:'修改时间',type:'datetime',width:110,align:'left'}]; {field:'ModifyDate',title:'修改时间',type:'datetime',width:110,align:'left'}];
const detail ={columns:[]}; const detail ={columns:[]};
const details = []; const details = [ {
cnName: '设备管理',
table: 'base_device',
columns: [{field:'DeviceId',title:'设备ID',type:'int',width:110,hidden:true,require:true,align:'left'},
{field:'DeviceName',title:'设备名称',type:'string',link:true,width:120,edit:{type:''},require:true,align:'left'},
{field:'AdapterCode',title:'来源适配器(类型:实例)',type:'string',width:110,hidden:true,align:'left'},
{field:'SourceId',title:'源系统设备ID',type:'string',width:120,align:'left'},
{field:'DeviceCategory',title:'设备种类(数据字典)',type:'string',bind:{ key:'设备种类',data:[]},width:110,edit:{type:'select'},require:true,align:'left'},
{field:'DeviceGroup',title:'设备分组(数据字典)',type:'string',bind:{ key:'设备分组',data:[]},width:110,hidden:true,edit:{type:'select'},require:true,align:'left'},
{field:'PointId',title:'所属点位ID',type:'int',width:110,hidden:true,edit:{type:''},align:'left'},
{field:'IsParent',title:'是否父设备(数据字典)',type:'string',bind:{ key:'是否父设备',data:[]},width:110,edit:{type:'select'},require:true,align:'left'},
{field:'ParentDeviceId',title:'父设备ID(自引用,子设备挂父设备下)',type:'int',width:110,hidden:true,align:'left'},
{field:'IsOnline',title:'在线状态(数据字典)',type:'string',bind:{ key:'在线状态',data:[]},width:110,align:'left'},
{field:'IpAddress',title:'IP地址',type:'string',width:110,align:'left'},
{field:'Port',title:'端口',type:'int',width:110,align:'left'},
{field:'Location',title:'安装位置',type:'string',width:180,edit:{type:''},align:'left'},
{field:'Lat',title:'纬度',type:'decimal',width:110,align:'left'},
{field:'Lng',title:'经度',type:'decimal',width:110,align:'left'},
{field:'MapModelId',title:'三维地图模型ID',type:'string',width:120,edit:{type:''},align:'left'},
{field:'MapModelScale',title:'模型缩放比例',type:'decimal',width:110,align:'left'},
{field:'MapModelRotation',title:'模型旋转角度(JSON)',type:'string',width:120,align:'left'},
{field:'ExtraData',title:'适配器扩展数据JSON(Owl/MC4/门禁字段均存于此)',type:'string',width:110,align:'left'},
{field:'LastSyncTime',title:'上次同步时间',type:'datetime',width:110,align:'left'},
{field:'Enable',title:'启用状态(数据字典)',type:'string',bind:{ key:'启用状态',data:[]},width:110,edit:{type:'select'},align:'left'},
{field:'Remark',title:'备注',type:'string',width:150,edit:{type:'textarea'},align:'left'},
{field:'CreateID',title:'创建人ID',type:'int',width:80,hidden:true,align:'left'},
{field:'Creator',title:'创建人',type:'string',width:100,align:'left'},
{field:'CreateDate',title:'创建时间',type:'datetime',width:110,align:'left'},
{field:'ModifyID',title:'修改人ID',type:'int',width:80,hidden:true,align:'left'},
{field:'Modifier',title:'修改人',type:'string',width:100,align:'left'},
{field:'ModifyDate',title:'修改时间',type:'datetime',width:110,align:'left'},
{field:'NodeId',title:'所属网关节点ID',type:'int',width:120,hidden:true,readonly:true,align:'left'}],
sortName: 'DeviceName',
key: 'DeviceId',
buttons:[],
delKeys:[],
detail:{
cnName: '设备管理_视频通道',
table: 'video_channel',
firstTable: 'gateway_nodes',
secondTable: 'base_device',
secondKey: 'DeviceId',
sortName: 'DeviceId',
key: 'ChannelId',
buttons:[],
delKeys:[],
columns: [{field:'ChannelId',title:'通道记录ID',type:'int',width:110,hidden:true,require:true,align:'left'},
{field:'OwlChannelId',title:'Owl系统通道ID',type:'string',width:120,hidden:true,require:true,align:'left'},
{field:'DeviceId',title:'关联Base_Device设备ID',type:'int',width:110,hidden:true,require:true,align:'left'},
{field:'OwlStreamApp',title:'Owl流应用名',type:'string',width:110,align:'left'},
{field:'OwlStreamName',title:'Owl流名称',type:'string',width:120,align:'left'},
{field:'HasPtz',title:'是否支持云台',type:'byte',width:110,align:'left'},
{field:'HasRecording',title:'是否支持录像',type:'byte',width:110,align:'left'},
{field:'RecordMode',title:'录像模式',type:'int',width:110,align:'left'},
{field:'SnapshotUrl',title:'快照地址',type:'string',width:150,align:'left'},
{field:'CreateDate',title:'创建时间',type:'datetime',width:110,align:'left'},]
}
}];
return { return {
table, table,