You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

5092 lines
147 KiB

<template>
<CardBox
class="card-page page-content"
:showTitle="false"
>
<div class="sec-left point-layout app-container card-page" style="height: 100%">
<!-- 左侧可拖拽区域 - 只影响左侧树 -->
<div class="resize-container" :style="{ width: leftPanelWidth + 'px' }">
<div class="sec-line">
<div class="search">
<el-input
placeholder="搜索设备类型、设备部位"
icon="el-icon-search"
v-model="inputSearch1"
></el-input>
<el-button
class="button task-toolbar-btn"
type="primary"
icon="el-icon-search"
@click="searchTree"
></el-button>
</div>
<div class="sec-a2">
<el-tree
node-key="areaid"
class="xg_tree"
@current-change="handleAreaTreeCurrentNodeChange"
ref="areaTree"
:data="areaList"
:props="defaultPropsArea"
@node-click="handleAreaList"
>
<span class="custom-tree-node" slot-scope="{ data }">
<span class="node-content">{{ getNodeLabel(data) }}</span>
<span v-if="shouldShowCount(data)" class="child-count">
({{ data.pointNum || 0 }})
</span>
</span>
</el-tree>
</div>
<div
class="resize-handle"
@mousedown="startResize"
:class="{ resizing: isResizing }"
>
<div class="handle-line"></div>
<div class="handle-dots">
<span></span>
<span></span>
<span></span>
</div>
</div>
<!-- 添加重置宽度按钮 -->
<div
class="reset-width-btn"
@click="resetLeftPanelWidth"
title="重置面板宽度"
>
<i class="el-icon-refresh"></i>
</div>
</div>
</div>
<!-- 中间内容区域 - 保持原有结构,不受拖拽影响 -->
<div
class="wid"
:class="{ 'has-right-panel': showRightPanel }"
:style="{ left: leftPanelWidth + 10 + 'px' }"
>
<div class="form" style="padding-top: 10px" ref="form">
<div class="line">
<div class="left-buttons">
<!-- 添加点位按钮(汇聚门户进入的只读视图下隐藏) -->
<el-button
v-if="!$store.getters.fromPortal"
type="primary"
class="task-toolbar-btn"
@click="addAlertShow1"
:disabled="
currentTreeNode === null ||
currentTreeNode.deviceId === null ||
batchDeleteMode
"
size="small"
icon="el-icon-plus"
>添加</el-button
>
<!-- 批量删除模式切换按钮(汇聚门户进入的只读视图下隐藏) -->
<el-button
v-if="!$store.getters.fromPortal"
class="task-toolbar-btn"
:type="batchDeleteMode ? 'info' : 'danger'"
@click="toggleBatchDeleteMode"
size="small"
:icon="batchDeleteMode ? 'el-icon-close' : 'el-icon-delete'"
>
{{ batchDeleteMode ? "取消批量删除" : "批量删除" }}
</el-button>
<!-- 确认删除按钮 - 仅在批量删除模式下显示 -->
<el-button
v-if="batchDeleteMode && !$store.getters.fromPortal"
type="danger"
class="task-toolbar-btn"
@click="confirmBatchDelete"
size="small"
icon="el-icon-check"
:disabled="batchSelectList.length === 0"
>
确认删除({{ batchSelectList.length }})
</el-button>
</div>
<div class="search-group right-filters">
<!-- 算法分类筛选下拉框 - 使用接口返回数据 -->
<el-select
v-model="selectedAlgorithmCategory"
placeholder="全部算法分类"
clearable
@change="filterByAlgorithmCategory"
class="algorithm-filter"
size="small"
>
<el-option
v-for="item in algorithmCategoryOptions"
:key="item.dictCode"
:label="item.dictLabel"
:value="item.dictValue"
>
<span>{{ item.dictLabel }}</span>
<span
style="
float: right;
color: #8492a6;
font-size: 12px;
margin-left: 10px;
"
>
{{ getAlgorithmCategoryCount(item) }}
</span>
</el-option>
</el-select>
<!-- 设备类型筛选下拉框 -->
<el-select
v-model="selectedDeviceType"
placeholder="全部设备类型"
clearable
@change="filterByDeviceType"
class="device-type-filter"
size="small"
>
<el-option
v-for="item in deviceTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
<span>{{ item.label }}</span>
<span
style="
float: right;
color: #8492a6;
font-size: 12px;
margin-left: 10px;
"
>
{{ item.count }}
</span>
</el-option>
</el-select>
</div>
</div>
<el-table
v-loading="loading"
element-loading-background="rgba(34, 47, 61, 0.92)"
ref="multipleTable"
:row-key="getRowKey"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
highlight-current-row
stripe
:fit="false"
:height="tableHeight"
:data="filteredTableData"
tooltip-effect="dark"
style="width: 100%"
class="custom-table resizable-table"
:header-cell-style="{
background: '#012773',
color: '#ffffff',
fontWeight: 'bold',
borderBottom: '2px solid #0b374e',
}"
:cell-style="{
color: '#e0e0e0',
borderBottom: '1px solid #1e3a4a',
}"
>
<!-- 序号/复选框列 - 根据模式动态显示,使用key强制重新渲染 -->
<el-table-column
v-if="batchDeleteMode"
:key="'selection-' + batchDeleteMode"
type="selection"
align="center"
width="55"
:selectable="checkSelectable"
>
</el-table-column>
<el-table-column
v-else
:key="'index-' + batchDeleteMode"
align="center"
label="序号"
width="55"
>
<template slot-scope="scope">
<span>{{ getRowIndex(scope.$index) }}</span>
</template>
</el-table-column>
<el-table-column
prop="patrolpointName"
header-align="center"
label="点位名称"
width="400"
>
<template #header>
<span>点位名称 <span style="color: red">*</span> </span>
</template>
<template slot-scope="scope">
<!-- 汇聚门户只读视图:点位名称展示纯文本,不允许修改 -->
<span
v-if="$store.getters.fromPortal"
class="point-name-text"
>{{ scope.row.patrolpointName || "-" }}</span>
<el-input
v-else
v-model="scope.row.patrolpointName"
@change="handleChangePointCode(scope.row)"
@input="handlePatrolpointNameInput($event, scope.row)"
placeholder="请输入点位名称"
class="table-input"
:disabled="batchDeleteMode"
></el-input>
</template>
</el-table-column>
<!-- 站名列:汇聚门户聚合数据下展示 -->
<el-table-column
v-if="$store.getters.fromPortal"
label="站名"
align="center"
header-align="center"
class-name="station-name-col"
prop="stationName"
min-width="100"
>
</el-table-column>
<el-table-column
prop="algSubtypeName"
label="算法类型"
align="center"
width="150"
>
<template slot-scope="scope">
<a
:class="[
'nowarp',
'algorithm-setting-link',
{ 'is-disabled': batchDeleteMode || $store.getters.fromPortal },
]"
@click="!batchDeleteMode && !$store.getters.fromPortal && chooseAlgorithm(scope.row)"
:title="scope.row.algSubtypeName"
>
{{
scope.row.algSubtypeName ? scope.row.algSubtypeName : "设置"
}}
</a>
</template>
</el-table-column>
<el-table-column
v-if="!$store.getters.fromPortal"
prop="pointTypeName"
header-align="center"
align="center"
label="设备类型"
width="150"
>
<template slot-scope="scope">
<span>{{ scope.row.pointTypeName || "-" }}</span>
</template>
</el-table-column>
<el-table-column
v-if="!$store.getters.fromPortal"
align="center"
prop="patrolContent"
header-align="center"
label="预置位选择"
width="150"
>
<template slot-scope="">
<el-button
class="task-row-btn"
style="
padding-left: 10px;
padding-right: 10px;
"
@click="pointConfiguration"
type="text"
size="small"
:disabled="batchDeleteMode"
>选择</el-button
>
</template>
</el-table-column>
<!-- <el-table-column prop="code" width="220" align="center">
<template #header>
<span>重要等级 <span style="color: red">*</span> </span>
</template>
<template slot-scope="scope">
<el-select
v-model="scope.row.pointImptLevel"
@change="handleChangePointCode(scope.row)"
class="table-select"
:disabled="batchDeleteMode"
>
<el-option
v-for="dict in dict.type.point_impt_level"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</template>
</el-table-column> -->
<el-table-column
v-if="!$store.getters.fromPortal"
label="操作"
align="center"
width="150"
>
<template slot-scope="scope">
<el-button
class="task-row-btn"
style="
padding-left: 10px;
padding-right: 10px;
"
@click.native.prevent="editRows1(scope.$index, scope.row)"
:disabled="
scope.row.stdparolPointId != null || batchDeleteMode
"
type="text"
size="small"
>修改</el-button
>
<el-button
class="task-row-btn"
style="
padding-left: 10px;
padding-right: 10px;
"
@click.native.prevent="deleteRows1(scope.$index, scope.row)"
:disabled="
scope.row.stdparolPointId != null || batchDeleteMode
"
type="text"
size="small"
>移除</el-button
>
</template>
</el-table-column>
</el-table>
<!-- 添加点位弹窗 -->
<el-dialog
class="card-dialog"
custom-class="site-point-add-dialog"
:title="titleAlert"
:visible.sync="showAdd"
width="60%"
append-to-body
:close-on-click-modal="false"
:lock-scroll="false"
>
<el-form
ref="formAdd"
class="card-page"
:model="formAdd"
:rules="formAddRules"
label-width="120px"
>
<el-form-item label="点位名称" prop="patrolpointName">
<el-input
v-model="formAdd.patrolpointName"
@input="handlePatrolpointNameInput($event, formAdd)"
placeholder="请输入点位名称"
></el-input>
</el-form-item>
<!-- <el-form-item label="监控点位编号" prop="patrolpointCode">
<el-input
v-model="formAdd.patrolpointCode"
placeholder="请输入监控编号"
></el-input>
</el-form-item> -->
<el-form-item label="监控内容" prop="patrolContent">
<!-- resize="none"
type
show-word-limit -->
<el-input
placeholder="请输入监控内容"
v-model="formAdd.patrolContent"
maxlength="200"
></el-input>
</el-form-item>
<el-form-item label="巡检类型" prop="patrolTypeId">
<treeselect
:zIndex="110000"
:append-to-body="true"
:defaultExpandLevel="2"
:limit="100"
:disable-branch-nodes="true"
:limitText="(count) => `+${count}`"
multiple
flat
v-model="formAdd.patrolTypeId"
:options="listPatroltype"
:show-count="true"
:no-results-text="'\u65e0\u5339\u914d\u7ed3\u679c'"
placeholder="请选择巡检类型"
/>
</el-form-item>
<!-- <el-form-item label="重要等级" prop="pointImptLevel">
<el-select v-model="formAdd.pointImptLevel">
<el-option
v-for="dict in dict.type.point_impt_level"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="读数单位">
<el-input
placeholder="请输入单位"
v-model="formAdd.purpose"
show-word-limit
></el-input>
</el-form-item>如:℃、mm、kPa -->
<el-form-item label="重要等级" prop="pointImptLevel">
<el-select
v-model="formAdd.pointImptLevel"
placeholder="请选择重要等级"
style="width: 50%"
>
<el-option
v-for="dict in dict.type.point_impt_level"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="读数单位">
<el-input
placeholder="请输入单位"
v-model="formAdd.purpose"
show-word-limit
style="width: 50%"
></el-input>
</el-form-item>
<div
class="dialog-footer"
style="
display: flex;
justify-content: flex-end;
margin-top: 20px;
padding-top: 20px;
"
>
<el-button
class="site-point-cancel-btn"
@click="alertCancel1"
>取消</el-button
>
<el-button
type="primary"
:loading="formAddLoading"
@click="alertConfirm1"
>确定</el-button
>
</div>
</el-form>
</el-dialog>
</div>
<!-- 右侧配置面板 - 完全独立,不受左侧拖拽影响 -->
<div
class="right"
:style="{ display: showRightPanel ? 'inline-block' : 'none' }"
ref="right"
>
<div class="eq">
<img class="eq-img" src="@/assets/images/eq.png" />
<div class="eq-a1">
<div class="eq-a1-d1">请选中左点位查看监控点或监测设备</div>
</div>
<img
class="collect"
@click="collectPoint"
src="../assets/collect.png"
/>
<el-tabs v-model="activeTab" :stretch="true" class="right-tabs">
<el-tab-pane label="视频设备预置点" name="first">
<div class="eq-a2">
<!-- 绑定入口(汇聚门户进入的只读视图下隐藏) -->
<el-button
v-if="!$store.getters.fromPortal"
class="task-toolbar-btn"
@click="visibleShow1"
title="一个监控点位只允许绑定一个预置点"
:disabled="
currentRow != null && currentRow.bindPoints.length > 0
"
>绑定可见光监测点</el-button
>
<el-button
v-if="!$store.getters.fromPortal"
class="task-toolbar-btn"
@click="infraredShow1"
title="一个监控点位只允许绑定一个预置点"
:disabled="
currentRow != null && currentRow.bindPoints.length > 0
"
>绑定热成像监控点</el-button
>
</div>
<el-table
v-loading="pointLoading"
element-loading-background="rgba(34, 47, 61, 0.92)"
v-if="currentRow != null"
:height="533"
:data="currentRow.bindPoints"
class="right-table"
:header-cell-style="{
background: '#012773',
color: '#ffffff',
borderBottom: '2px solid #0b374e',
}"
:cell-style="{
color: '#e0e0e0',
borderBottom: '1px solid #1e3a4a',
}"
>
<el-table-column
label="设备名称"
align="center"
min-width="100"
prop="patrolDeviceName"
show-overflow-tooltip
></el-table-column>
<el-table-column
label="预置点名称"
align="center"
min-width="120"
prop="presetPosName"
show-overflow-tooltip
></el-table-column>
<el-table-column
v-if="!$store.getters.fromPortal"
label="操作"
width="150"
fixed="right"
class-name="operation-column"
align="center"
>
<template slot-scope="scope">
<el-button
class="task-row-btn"
style="
padding-left: 10px;
padding-right: 10px;
"
type="text"
size="small"
@click="handleUnbindPoint(scope.row, scope.$index)"
>解绑</el-button
>
<el-button
class="task-row-btn"
style="
padding-left: 10px;
padding-right: 10px;
"
type="text"
size="small"
:loading="scope.row.loading"
v-if="
scope.row.channelImg &&
scope.row.channelImg.length > 0
"
@click="handleViewImages(scope.row, scope.$index)"
>查看快照</el-button
>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</div>
</div>
<!-- 上传组件 -->
<UploadDialog
class="card-dialog"
:show.sync="showUploader"
@confirm="handleUploaderConfirm"
:filePath="currentRow != null ? currentRow.disRefPicturePath : ''"
/>
<UploadDialog
class="card-dialog"
:showConfirmBtn="false"
:updata="updata"
:accept="'.xml'"
:action="rebotUploadUrl"
:title="'机器人离线点位文件上传'"
:show.sync="showRebotUploader"
@confirm="handleRebotUploaderConfirm"
:filePath="''"
/>
<!-- 图片预览 -->
<AsyncImage
ref="asyncImage"
errorTips=""
class="xg_image_viewer"
@imgload="handleImageLoad"
@imgloaderror="handleImageLoadError"
:src="currentSrc"
:srcList="currentSrcList"
/>
<!-- 监控配置弹窗 -->
<el-dialog
class="monitor-config-dialog-wrap"
custom-class="card-dialog card-page monitor-config-dialog"
:title="titleLightTitle"
:close-on-click-modal="false"
:visible.sync="monitorShow"
width="1200px"
top="4vh"
@close="handleCloseVL"
append-to-body
:lock-scroll="false"
>
<div class="horizontal-tabs-container">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="摄像头" name="home">
<!-- 修改为上下结构:上方视频和云台分开,下方动作列表 -->
<div
class="card-page"
style="display: flex; flex-direction: column; gap: 20px"
>
<!-- 上方区域:左侧树 + 中间视频 + 右侧云台(完全独立) -->
<div style="display: flex; gap: 10px; height: 400px">
<!-- 左侧树 -->
<div
class="left"
style="
width: 300px;
height: 390px;
margin-right: 0;
overflow: scroll;
"
>
<el-tree
:data="monitorList"
:props="defaultPropsMonitor"
@node-click="handleMonitor"
></el-tree>
</div>
<!-- 中间视频区域 - 独立带边框 -->
<div style="flex: 1; height: 390px; overflow: hidden">
<VideoPlayerPlatform
ref="playerPlayform"
@changeIndex="handleChangeIndex"
:showBottomToolBar="false"
:defaultLayout="'1x1'"
>
<template slot-scope="scope">
<DrawShape
@drawComplete="
(shape) => {
handleDrawComplete(scope, shape);
}
"
/>
</template>
</VideoPlayerPlatform>
</div>
<!-- 右侧云台区域 - 独立无边框 -->
<div style="width: 290px; height: 390px">
<div class="r-ti1">云台</div>
<div class="r-box">
<div class="r-ti2">
<img src="@/assets/images/yuntai1.png" />
<div class="r-ti2-pos">
<div
@mousedown="controlEquip1('UP')"
@mouseup="controlEquip1('UP_STOP')"
></div>
<div
@mousedown="controlEquip1('UP_RIGHT')"
@mouseup="controlEquip1('UP_RIGHT_STOP')"
></div>
<div
@mousedown="controlEquip1('RIGHT')"
@mouseup="controlEquip1('RIGHT_STOP')"
></div>
<div
@mousedown="controlEquip1('DOWN_RIGHT')"
@mouseup="controlEquip1('DOWN_RIGHT_STOP')"
></div>
<div
@mousedown="controlEquip1('DOWN')"
@mouseup="controlEquip1('DOWN_STOP')"
></div>
<div
@mousedown="controlEquip1('DOWN_LEFT')"
@mouseup="controlEquip1('DOWN_LEFT_STOP')"
></div>
<div
@mousedown="controlEquip1('LEFT')"
@mouseup="controlEquip1('LEFT_STOP')"
></div>
<div
@mousedown="controlEquip1('UP_LEFT')"
@mouseup="controlEquip1('UP_LEFT_STOP')"
></div>
<div @mousedown="controlEquip1('UP_LEFT')"></div>
</div>
</div>
<div class="r-ti3">
<div class="r-ti3-d1">
<img src="@/assets/images/r1.png" />
<div class="r-ti3-d2">
<div
@mousedown="controlEquip1('ZOOM_OUT')"
@mouseup="controlEquip1('ZOOM_OUT_STOP')"
></div>
<div
@mousedown="controlEquip1('ZOOM_IN')"
@mouseup="controlEquip1('ZOOM_IN_STOP')"
></div>
</div>
</div>
<div class="r-ti3-d1">
<img src="@/assets/images/r2.png" />
<div class="r-ti3-d2">
<div
@mousedown="controlEquip2('nearfocus')"
@mouseup="controlEquip2('nearfocusstop')"
></div>
<div
@mousedown="controlEquip2('farfocus')"
@mouseup="controlEquip2('farfocusstop')"
></div>
</div>
</div>
<div class="r-ti3-d1">
<img src="@/assets/images/r3.png" />
<div class="r-ti3-d2">
<div
@mousedown="controlEquip2('apertureoff')"
@mouseup="controlEquip2('apertureoffstop')"
></div>
<div
@mousedown="controlEquip2('apertureon')"
@mouseup="controlEquip2('apertureonstop')"
></div>
</div>
</div>
<div class="r-ti3-d1">
<img src="@/assets/images/r4.png" />
<div class="r-ti3-d2">
<div
@mousedown="controlEquip1('ZOOM_OUT')"
@mouseup="controlEquip1('ZOOM_OUT_STOP')"
></div>
<div
@mousedown="controlEquip1('ZOOM_IN')"
@mouseup="controlEquip1('ZOOM_IN_STOP')"
></div>
</div>
</div>
<div class="r-ti3-d1">
<img src="@/assets/images/r5.png" />
<div class="r-ti3-d2">
<div
@mousedown="controlEquip1('ZOOM_OUT')"
@mouseup="controlEquip1('ZOOM_OUT_STOP')"
></div>
<div
@mousedown="controlEquip1('ZOOM_IN')"
@mouseup="controlEquip1('ZOOM_IN_STOP')"
></div>
</div>
</div>
</div>
<div class="r-ti4">
<div style="display: flex">
<el-select
v-model="presetDotNum"
placeholder="请选择预置点"
@change="presetDotChange1"
:disabled="presetDotDis"
>
<el-option
v-for="item in presetArr"
:key="item.dictValue"
:label="item.dictLabel"
:value="item.dictValue"
></el-option>
</el-select>
<img
class="alert1_img1"
@click="convertPresetDot1"
src="@/assets/images/r6.png"
title="转到预置点"
/>
<el-popover
placement="top"
width="300"
v-model="popovervisible"
trigger="click"
popper-class="monitor-config-popover"
>
<el-input
v-model="newpresetName"
placeholder="请输入内容"
style="background-color: #222f3d"
:disabled="presetDotDis"
></el-input>
<div
style="text-align: right; margin-top: 10px"
class="card-page"
>
<el-button
size="mini"
type="primary"
@click="popovervisible = false"
>取消</el-button
>
<el-button
type="primary"
size="mini"
@click="setPresetDot1"
>确定</el-button
>
</div>
<img
v-if="!$store.getters.fromPortal"
slot="reference"
class="alert1_img1"
src="@/assets/images/r7.png"
title="添加预置点"
/>
</el-popover>
</div>
<div style="display: flex">
<el-input
v-model="presetName"
placeholder="请输入内容"
></el-input>
<img
v-if="!$store.getters.fromPortal"
class="alert1_img1"
@click="editPresetDot1"
src="@/assets/images/r8.png"
title="编辑预置点名称"
/>
</div>
<div
style="display: flex; margin: 10px 0px; width: 92%"
>
<el-input
v-model="presetNumber"
placeholder="预置位编号"
></el-input>
</div>
</div>
</div>
</div>
</div>
<!-- 下方区域:动作列表 -->
<div>
<div>动作列表</div>
<div class="card-page">
<el-table
:data="presetData.presetActionList"
style="margin-top: 10px; width: 100%"
height="200px"
border
>
<el-table-column
align="center"
label="动作类型"
min-width="100"
>
<template slot-scope="scope">
<el-select v-model="scope.row.actionType">
<el-option
v-for="dict in dict.type.video_action_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column
align="center"
label="录像时长(秒)"
min-width="100"
>
<template slot-scope="scope">
<el-input
v-model="scope.row.videoTime"
v-if="scope.row.actionType == 2"
class="centered-input"
></el-input>
</template>
</el-table-column>
<el-table-column
align="center"
label="拍照次数"
min-width="100"
>
<template slot-scope="scope">
<el-input
v-model="scope.row.photoCount"
v-if="scope.row.actionType == 1"
type="number"
min="0"
step="1"
class="centered-input"
></el-input>
</template>
</el-table-column>
<el-table-column
align="center"
label="拍照间隔(秒)"
min-width="100"
>
<template slot-scope="scope">
<el-input
v-model="scope.row.photoSeper"
v-if="scope.row.actionType == 1"
type="number"
min="0"
step="1"
class="centered-input"
></el-input>
</template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="100"
>
<template #header>
<el-button
type="primary"
size="small"
@click="addAction"
>添加</el-button
>
</template>
<template slot-scope="scope">
<el-button
type="text"
size="small"
@click="removeAction(scope.$index)"
>移除</el-button
>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</el-tab-pane>
<!-- 移动设备绑定暂时停用,保留此代码供后续恢复。 -->
<el-tab-pane
label="移动设备"
name="solutions"
v-if="false && monitorType == 'ir'"
>
<div class="box card-page" style="height: 570px">
<div class="middle">
<div>
<ImageAnnotation
:image-src="
'/app/htjc6/htjcImage/' +
imageUrl
"
ref="checkItemSetRef"
/>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
<template #footer>
<div
class="add-btn add-bth1 card-page"
v-if="activeName !== 'solutions'"
>
<el-button
type="primary"
v-if="monitorType == 'vl'"
:loading="loadingBind"
@click="saveDot1"
>确定</el-button
>
<el-button type="primary" v-else @click="nextStep1"
>下一步,配置测温位</el-button
>
<el-button @click="monitorShow = false">取消</el-button>
</div>
<div
class="add-btn add-bth1 card-page"
v-if="activeName == 'solutions'"
>
<el-button type="primary" @click="uavComplete()">完成</el-button>
<el-button @click="monitorShow = false">取消</el-button>
</div>
</template>
</el-dialog>
<!-- 测温配置弹窗 -->
<el-dialog
:visible.sync="monitorSecShow"
:close-on-click-modal="false"
custom-class="bind-thermal-dialog"
width="1200px"
append-to-body
@close="handleCloseIR"
:lock-scroll="false"
>
<div class="sec-left card-page">
<div class="fl">
<div class="toollist1-a1">
<div
:class="drawShapIndex == 3 ? 'on1' : ''"
@click="chooseDrawShape1(3, ShapeEnum['rect'])"
>
<img src="@/assets/images/ic04.png" />
</div>
<div
:class="drawShapIndex == 2 ? 'on1' : ''"
@click="chooseDrawShape1(2, ShapeEnum['polygon'])"
>
<img src="@/assets/images/ic03.png" />
</div>
<div
:class="drawShapIndex == 1 ? 'on1' : ''"
@click="chooseDrawShape1(1, ShapeEnum['brokenline'])"
>
<img src="@/assets/images/ic02.png" />
</div>
<div
:class="drawShapIndex == 0 ? 'on1' : ''"
@click="chooseDrawShape1(0, ShapeEnum['point'])"
>
<img src="@/assets/images/ic01.png" />
</div>
</div>
<VideoPlayerPlatform
ref="playerPlayform2"
@changeIndex="handleChangeIndex"
:showBottomToolBar="false"
:defaultLayout="'1x1'"
>
<template slot-scope="scope">
<DrawShape
@drawComplete="
(shape) => {
handleDrawComplete(scope, shape);
}
"
/>
</template>
</VideoPlayerPlatform>
</div>
<div class="fr">
<div class="fr-ti1">
<span>{{ patroldeviceName }}</span>
<span v-if="monitorType == 'vl'">可见光</span>
<span v-else>热成像</span>
<span>/{{ presetName }}</span>
<span v-if="monitorType == 'vl'">位置</span>
<span v-else>测温位</span>
<span>配置</span>
</div>
<div class="fr-ti2">
点击画面右上方按钮配置测温位,最多配置10个测温点、1条测温线、10个测温框
</div>
<el-table
:data="presetData.presetParamList"
style="width: 100%"
max-height="250"
>
<el-table-column
fixed
prop="measureName"
:label="monitorType == 'vl' ? '位置' : '测温位'"
width="150"
>
</el-table-column>
<el-table-column
prop="measureAreaType"
label="类型"
width="120"
></el-table-column>
<el-table-column fixed="right" label="操作" width="80">
<template slot-scope="scope">
<i
class="el-icon-delete fss"
title="删除"
@click.prevent="deleteDot1(scope.$index, scope.row)"
></i>
</template>
</el-table-column>
</el-table>
</div>
</div>
<span slot="footer" class="dialog-footer card-page">
<el-button type="primary" :loading="loadingBind" @click="saveDot1"
>保存</el-button
>
<el-button class="bind-thermal-secondary-btn" @click="prevStep"
>上一步</el-button
>
<el-button
class="bind-thermal-secondary-btn"
@click="monitorSecShow = false"
>取消</el-button
>
</span>
</el-dialog>
<!-- 测温参数配置弹窗 -->
<el-dialog
:title="title2"
:close-on-click-modal="false"
:visible.sync="presetDataShow"
@close="handleCloseTemptrueModel"
width="65%"
append-to-body
:lock-scroll="false"
>
<el-form
ref="formTemptrue"
class="card-page"
:model="testobj"
:rules="formTemptrueRules"
label-width="120px"
>
<el-row>
<el-col :span="24">基本信息</el-col>
</el-row>
<el-row>
<el-col :span="6">
<el-form-item label="名称" prop="measureName">
<el-input
v-model="testobj.measureName"
placeholder="请输入名称"
></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="规则编号" prop="presetParamCode">
<el-input
v-model="testobj.presetParamCode"
placeholder="请输入编号"
></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="测温距离" prop="measureDistance">
<el-input v-model="testobj.measureDistance"
><i slot="suffix" class="input_slot_a1">m</i></el-input
>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="辐射系数" prop="radioRate">
<el-input v-model="testobj.radioRate"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="反射温度" prop="reflexTemp">
<el-input v-model="testobj.reflexTemp"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="状态" prop="reflexTemp">
<el-checkbox
v-model="testobj.isEnable"
:true-label="1"
:false-label="0"
>
{{ testobj.isEnable == "1" ? "已启用" : "已停用" }}
</el-checkbox>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">预警配置</el-col>
</el-row>
<el-row>
<el-col :span="6">
<el-form-item label="最高温度" prop="warnMaxTemp">
<el-input v-model="testobj.warnMaxTemp"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="最低温度" prop="warnMinTemp">
<el-input v-model="testobj.warnMinTemp"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="平均温度" prop="warnAverageTemp">
<el-input v-model="testobj.warnAverageTemp"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="温差" prop="warnDifferenceTemp">
<el-input v-model="testobj.warnDifferenceTemp"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">告警配置</el-col>
</el-row>
<el-row>
<el-col :span="6">
<el-form-item label="最高温度" prop="alarmMaxTemp">
<el-input v-model="testobj.alarmMaxTemp"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="最低温度" prop="alarmMinTemp">
<el-input v-model="testobj.alarmMinTemp"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="平均温度" prop="alarmAverageTemp">
<el-input v-model="testobj.alarmAverageTemp"></el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="温差" prop="alarmDifferenceTemp">
<el-input v-model="testobj.alarmDifferenceTemp"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="add-bth add-bth1 card-page" slot="footer">
<el-button type="primary" @click="aletrSecConfirm1">确定</el-button>
<el-button @click="aletrSecCancel1">取消</el-button>
</div>
</el-dialog>
</div>
<!-- 算法选择组件 -->
<ChooseAlg
:algSubtypeIds="currentRow != null ? currentRow.algSubtypeIds : ''"
:algSubtypeName="currentRow != null ? currentRow.algSubtypeName : ''"
:alarmThreshold="currentRow != null ? currentRow.alarmThreshold : ''"
:show.sync="showChooseAlg"
@confirm="handleChooseAlgOk"
/>
<!-- 设备选择组件 -->
<ChooseDevices
:show.sync="showChooseDevices"
@confirm="handleChooseDevicesOk"
></ChooseDevices>
<!-- 算法配置弹窗 -->
<el-dialog
class="card-dialog"
title="算法配置"
:visible.sync="algorithmShow"
width="1200px"
style="height: 95%"
:lock-scroll="false"
>
<Algorith></Algorith>
</el-dialog>
<!-- 图片预览弹窗 -->
<el-dialog
title="图片预览"
:visible.sync="showPreviewImages"
:close-on-click-modal="false"
:close-on-press-escape="false"
:destroy-on-close="true"
custom-class="card-dialog card-page image-preview-dialog"
width="70%"
append-to-body
:lock-scroll="false"
>
<div v-if="showPreviewImages" class="image-preview-stage">
<ImagePreview
:imageUrls="currentSrcList"
:selectImgPosition="previewImagePos"
:showTemperatureTool="false"
/>
</div>
</el-dialog>
</div>
</CardBox>
</template>
<script>
import {
listPatrolpointpreset,
addPatrolpointpreset,
delPatrolpointpreset,
} from "@/api/basedata/patrolpointmnt/patrolpointpreset.js";
import ChooseDevices from "./components/chooseDevices.vue";
import UploadDialog from "./components/uploadDialog.vue";
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import CardBox from "../components/cardBox.vue";
import ImagePreview from "@/views/cards/components/ImagePreview/index.vue";
import {
proDotManTable,
proDotTableDeleteSin,
proDotTableAddSin,
proDotTableEditSin,
bindingMonitorDotList,
presetDotDistribute,
convertPresetDot,
cloudControl,
focalAperture,
bindingPreset,
issueUpdate,
proDotManTableRelation, //(右侧新的点位列表)
} from "@/api/basedata/area/area";
import VideoPlayerPlatform from "@/components/VideoPlayerPlatform";
import DrawShape, {
toTargetRect,
ShapeEnum,
} from "@/components/Canvas/DrawShape.vue";
import linq from "linq";
import { listData } from "@/api/system/dict/data";
import { normalizePatrolName } from "@/utils/patrolName";
import {
listPatroltype,
getDeviceList,
} from "@/api/basedata/patrolpointmnt/patroltype";
import {
getAreaDeviceTree,
getPatroPointDevicePoints,
unbindPoint,
getBdconfigMainsystem,
treeAreaDeviceSelectFilterByPreset,
treeAreaDeviceSelectFilterByPresetRelation,
} from "@/api/basedata/patrolpointmnt/patrolpoint";
import AsyncImage from "@/components/AsyncImage";
import lodash from "lodash";
import { editPoint } from "@/api/basedata/patrolpointmnt/patrolpointpreset";
import {
capturePicture,
irUavImg,
} from "@/api/basedata/patrolpointmnt/patrolpointpreset";
import ChooseAlg from "./components/chooseAlg.vue";
import Algorith from "@/views/cards/site_management/algorithm.vue";
import ImageAnnotation from "./components/ImageAnnotation.vue";
// 设备树面板默认宽度(收窄后);存储 key 带 v2,避免老用户 localStorage 里的旧宽度盖住新默认
const DEFAULT_LEFT_PANEL_WIDTH = 200;
const LEFT_PANEL_WIDTH_STORAGE_KEY = "leftPanelWidthV2";
export default {
name: "site_management",
dicts: ["video_action_type", "point_impt_level", "system_type"],
components: {
Algorith,
CardBox,
ChooseDevices,
UploadDialog,
VideoPlayerPlatform,
DrawShape,
Treeselect,
AsyncImage,
ChooseAlg,
ImagePreview,
ImageAnnotation,
},
data() {
return {
// 图片标注
imageUrl: "",
imagePath: "",
// 图片标注结束
activeName: "home", //tab切换
algorithmShow: false,
activeTab: "first",
showChooseDevices: false,
showChooseAlg: false,
showUploader: false,
showRebotUploader: false,
rebotUploadUrl:
process.env.VUE_APP_BASE_API +
"/basedata/patrolpointmnt/patrolpoint/uploadXml",
updata: {},
showPreviewImages: false,
previewImagePos: 0,
currentSrc: "",
currentSrcList: [],
currentImageRow: {},
formAddLoading: false,
currentTreeNode: null,
currentRow: null,
loading: false,
pointLoading: false,
loadingBind: false,
treeobj: null /* 左侧列表树点击后获取相关信息的暂存区 */,
areaList: [] /* 左侧数据列表 */,
defaultPropsArea: {
children: "children",
label: "deviceName",
} /* 左侧数据列表对应字段设置 */,
monitorList: [] /* 中间数据列表 */,
defaultPropsMonitor: {
children: "children",
label: "name",
} /* 中间数据列表对应字段设置 */,
description:
"一个监控对象(区域、主设备),最多支持绑定16个不同摄像头;一个监控对象(区域、主设备),最多添加200个监控点位;一个监控点位绑定监控点不超过9个预置位(无论是否同一监控点);" /* 警告提示的文案 */,
showAdd: false /* 添加、修改弹窗控制开关 */,
titleAlert: "" /* 添加、修改弹窗标题 */,
tableData: [] /* 监控点位数据数组 */,
filters: [{ "filter-multiple": false }] /* 数据是否多选 */,
mulSelect: [] /* 监控点位多选数据数组 */,
inputSearch: "" /* 监控点位数据搜索框输入值 */,
inputSearch1: "" /* 树形据搜索框输入值 */,
bindingStyle: 1 /* 选择手动、平台 手动1、平台2 */,
monitorShow: false /* 可见光、热成像弹窗显示状态值 */,
titleLightTitle: "" /* 可见光、热成像弹窗标题 */,
monitorType: "" /* 绑定监控点位类型 可见光值为:vl,热成像值为:ir */,
equipobj: {} /* 监控设备相应的数据 */,
patroldeviceName: "" /* 绑定监控设备名称 */,
presetDotNum: "" /* 当前选择的预置点 */,
presetDotDis: "" /* 预置点是否可选 */,
presetArr: [] /* 预置点下拉点位数组 0——255 */,
presetName: "" /* 选到的预置点名字 */,
presetNumber: "", //预置位编号
is_convert: false /* 是否转到预置点 */,
is_set: false /* 是否设置预置点 */,
monitorSecShow: false /* 下一步弹窗显示状态 */,
formTemptrueRules: {
measureName: [{ required: true, message: "必填", trigger: "blur" }], //区域名称
measureDistance: [{ required: true, message: "必填", trigger: "blur" }], //距离
radioRate: [{ required: true, message: "必填", trigger: "blur" }], //辐射率
reflexTemp: [{ required: true, message: "必填", trigger: "blur" }], //反射温度
warnMaxTemp: [{ required: true, message: "必填", trigger: "blur" }], //预警最高温度
warnMinTemp: [{ required: true, message: "必填", trigger: "blur" }], //预警最低温度
alarmMaxTemp: [{ required: true, message: "必填", trigger: "blur" }], //告警最高温度
alarmMinTemp: [{ require: true, message: "必填", trigger: "blur" }], //告警最低温度
warnAverageTemp: [{ required: true, message: "必填", trigger: "blur" }], //预警平均温度
alarmAverageTemp: [
{ required: true, message: "必填", trigger: "blur" },
], // 告警平均温度
warnDifferenceTemp: [
{ required: true, message: "必填", trigger: "blur" },
], //预警温差
alarmDifferenceTemp: [
{ required: true, message: "必填", trigger: "blur" },
], //告警温差
presetParamCode: [{ required: true, message: "必填", trigger: "blur" }], //测温编号
},
presetData: {
presetPosCode: "",
channelId: "",
eqpbookId: "",
patrolpointId: "",
stdparolPointId: "",
deviceId: "",
presetPosName: "",
stopTime: "",
channelImg: "",
description: "",
presetActionList: [],
presetParamList: [],
presetPointList: [],
} /* 预置点相关参数 */,
presetDataShow: false /* 监控区域温度弹窗状态值 */,
drawShapIndex: -1 /* 选择绘制类型索引值 */,
ShapeEnum: {
rect: "Rectangle",
brokenline: "BrokenLine",
polygon: "Polygon",
point: "Point",
} /* 绘制方式 */,
testobj: {
measureAreaType: "", //区域类型
measureName: "", //区域名称
measureDistance: "", //距离
radioRate: "", //辐射率
warnMaxTemp: "", //预警最高温度
warnMinTemp: "", //预警最低温度
alarmMaxTemp: "", //告警最高温度
alarmMinTemp: "", //告警最低温度
warnAverageTemp: "", //预警平均温度
alarmAverageTemp: "", // 告警平均温度
warnDifferenceTemp: "", //预警温差
alarmDifferenceTemp: "", //告警温差
coordinate: "", //坐标
reflexTemp: "",
presetParamCode: "", //测温编号
isEnable: 1, //启用停用
} /* 测温参数,和画框数据 */,
popovervisible: false,
newpresetName: "",
title2: "测温位配置",
show1: true,
value: "",
formAdd: {
patrolpointName: "",
patrolContent: "",
purpose: "",
areaId: "",
mainDeviceId: "",
deviceId: "",
patrolpointCode: "",
patrolTypeId: null,
pointImptLevel: null,
positionX: null,
positionY: null,
positionZ: null,
},
formAddRules: {
patrolpointName: [
{
required: true,
message: "必填",
trigger: "blur",
},
],
patrolContent: [
{
required: false,
message: "必填",
trigger: "blur",
},
],
patrolpointCode: [
{
required: true,
message: "必填",
trigger: "blur",
},
],
patrolTypeId: [
{
required: false,
message: "必填",
trigger: "blur",
},
],
pointImptLevel: [
{
required: false,
message: "必填",
trigger: "blur",
},
],
purpose: [
{
required: true,
message: "必填",
trigger: "blur",
},
],
areaId: "",
mainDeviceId: "",
deviceId: "",
},
listPatroltype: [],
patroldeviceCode: null,
channelCode: null,
cssClass: false,
// 新增列宽相关属性
columnWidths: {}, // 列宽缓存
defaultColumnWidths: [55, 400, 150, 150, 150, 220, 125], // 默认列宽,增加了序号列
// 算法分类筛选相关属性 - 使用接口返回数据
selectedAlgorithmCategory: "", // 选中的算法分类(dictValue)
algorithmCategoryOptions: [], // 算法分类选项列表(接口返回数据)
selectedAlgorithmCategoryLabel: "", // 选中的算法分类标签
// 设备类型筛选相关属性
selectedDeviceType: "", // 选中的设备类型
deviceTypeOptions: [], // 设备类型选项列表
selectedDeviceTypeLabel: "", // 选中的设备类型标签
// 新增的拖拽相关属性 - 只影响左侧树
leftPanelWidth: DEFAULT_LEFT_PANEL_WIDTH, // 左侧面板默认宽度
minLeftWidth: 200, // 左侧面板最小宽度
maxLeftWidth: 600, // 左侧面板最大宽度
tableLayoutFrame: null,
tableHeight: 560,
isResizing: false, // 是否正在拖拽
startX: 0, // 拖拽起始X坐标
startWidth: 0, // 拖拽起始宽度
// 右侧面板显示控制
showRightPanel: false,
// 批量删除模式相关属性
batchDeleteMode: false, // 是否处于批量删除模式
batchSelectList: [], // 批量删除选中的行
};
},
computed: {
// 根据选中的算法分类和设备类型过滤表格数据
filteredTableData() {
let filtered = this.tableData;
if (this.selectedAlgorithmCategory) {
// 根据算法分类的值过滤
const selectedCategory = this.algorithmCategoryOptions.find(
(item) => item.dictValue === this.selectedAlgorithmCategory
);
if (selectedCategory) {
// 获取该分类下的所有算法ID
const algIdsInCategory = selectedCategory.dictValue
.split(",")
.map((id) => id.trim());
filtered = filtered.filter((row) => {
if (!row.algSubtypeIds) return false;
// 检查行的algSubtypeIds是否包含该分类下的任一算法ID
const rowAlgIds = row.algSubtypeIds
.split(",")
.map((id) => id.trim());
return algIdsInCategory.some((id) => rowAlgIds.includes(id));
});
}
}
if (this.selectedDeviceType) {
filtered = filtered.filter(
(row) => row.pointTypeName === this.selectedDeviceType
);
}
return filtered;
},
},
created() {
this.testobjBackup = lodash.cloneDeep(this.testobj);
this.presetDataBackup = lodash.cloneDeep(this.presetData);
listPatroltype().then((res) => {
const list = [];
let childs = [];
let ptype = {};
linq
.from(res.rows)
.where((x) => x.parentId === 0)
.toArray()
.forEach((node) => {
childs = linq
.from(res.rows)
.where((c) => c.parentId === node.patrolTypeId)
.select((q) => {
return {
id: q.patrolTypeId,
label: normalizePatrolName(q.patrolTypeName),
};
})
.toArray();
ptype = {
id: node.patrolTypeId,
isOpen: true,
label: normalizePatrolName(node.patrolTypeName),
};
if (childs.length > 0) {
ptype.children = childs;
}
list.push(ptype);
});
this.listPatroltype = list;
});
this.getAreaList1();
this.getAlgCategory();
getBdconfigMainsystem().then((res) => {
const config = res.data.find((x) => x.systemType.includes("xsxt"));
if (config) {
this.updata = {
sendCode: config.mainSystemCode,
};
}
});
// 从localStorage加载保存的宽度
this.loadLeftPanelWidth();
},
mounted() {
this.updateTableHeight();
this.$nextTick(() => {
this.initColumnResize();
this.loadColumnWidths();
});
// 添加窗口大小变化监听
window.addEventListener("resize", this.handleResize);
},
beforeDestroy() {
// 移除窗口大小变化监听
window.removeEventListener("resize", this.handleResize);
// 清理拖拽遮罩
const mask = document.getElementById("resize-mask");
if (mask) mask.remove();
// 移除全局事件监听
document.removeEventListener("mousemove", this.onResize);
document.removeEventListener("mouseup", this.stopResize);
if (this.tableLayoutFrame) {
cancelAnimationFrame(this.tableLayoutFrame);
}
},
methods: {
handlePatrolpointNameInput(value, target) {
const name = String(value || "");
if (name.length > 200) {
target.patrolpointName = name.slice(0, 200);
this.$message.warning("\u70b9\u4f4d\u540d\u79f0\u4e0d\u80fd\u8d85\u8fc7200\u4e2a\u5b57\u7b26");
}
},
// 获取行的唯一key(用于表格多选)
getRowKey(row) {
return row.patrolpointId;
},
// 判断行是否可选(过滤标准点位)
checkSelectable(row) {
// 标准点位不可选
return !row.stdparolPointId;
},
// 获取行序号(基于过滤后的数据)
getRowIndex(index) {
return index + 1;
},
// 切换批量删除模式
toggleBatchDeleteMode() {
if (this.batchDeleteMode) {
// 退出批量删除模式,清空选中
this.batchDeleteMode = false;
this.batchSelectList = [];
// 清除表格的选中状态
if (this.$refs.multipleTable) {
this.$refs.multipleTable.clearSelection();
}
} else {
// 进入批量删除模式
this.batchDeleteMode = true;
this.batchSelectList = [];
// 清除表格的选中状态
if (this.$refs.multipleTable) {
this.$refs.multipleTable.clearSelection();
}
// 强制刷新表格,确保复选框正确显示
this.$nextTick(() => {
if (this.$refs.multipleTable) {
this.$refs.multipleTable.doLayout();
}
});
}
},
// 处理表格选中变化(批量删除模式下)
handleSelectionChange(val) {
if (this.batchDeleteMode) {
this.batchSelectList = val;
} else {
// 非批量删除模式下,不处理多选,但保留原有功能
this.mulSelect = val;
}
},
// 处理行点击(非批量删除模式下,用于选中行;批量删除模式下,不处理)
handleRowClick(row) {
if (!this.batchDeleteMode) {
this.handleCurrentChange(row);
}
},
// 确认批量删除
confirmBatchDelete() {
if (this.batchSelectList.length === 0) {
this.$message.warning("请选择要删除的点位");
return;
}
// batchSelectList 已经通过 checkSelectable 过滤了标准点位,所以不需要再次过滤
const deletableList = this.batchSelectList;
const pointNames = deletableList
.map((item) => item.patrolpointName)
.join("、");
const message =
deletableList.length === 1
? `确定要删除点位 "${pointNames}" 吗?`
: `确定要删除选中的 ${deletableList.length} 个点位吗?\n${pointNames}`;
this.$confirm(message, "批量删除确认", {
confirmButtonText: "确定删除",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
const ids = deletableList.map((item) => item.patrolpointId).join(",");
proDotTableDeleteSin(ids).then((res) => {
this.$message.success(`成功删除 ${deletableList.length} 个点位`);
// 退出批量删除模式
this.batchDeleteMode = false;
this.batchSelectList = [];
// 重新加载数据
this.loadList();
});
});
},
// 获取算法分类(点位类型)
getAlgCategory() {
var data = { dictType: "point_alg_category" };
listData(data).then((res) => {
this.algorithmCategoryOptions = res.rows || [];
});
},
// 获取算法分类中该分类下的记录数量
getAlgorithmCategoryCount(category) {
if (!this.tableData || !category || !category.dictValue) return 0;
const algIdsInCategory = category.dictValue
.split(",")
.map((id) => id.trim());
const count = this.tableData.filter((row) => {
if (!row.algSubtypeIds) return false;
const rowAlgIds = row.algSubtypeIds.split(",").map((id) => id.trim());
return algIdsInCategory.some((id) => rowAlgIds.includes(id));
}).length;
return count;
},
// 获取选中的算法分类标签
getSelectedAlgorithmCategoryLabel() {
const selected = this.algorithmCategoryOptions.find(
(item) => item.dictValue === this.selectedAlgorithmCategory
);
return selected ? selected.dictLabel : "";
},
// 更新设备类型选项
updateDeviceTypeOptions() {
const deviceTypeMap = new Map();
this.tableData.forEach((row) => {
if (row.pointTypeName) {
const typeName = row.pointTypeName;
if (deviceTypeMap.has(typeName)) {
deviceTypeMap.set(typeName, deviceTypeMap.get(typeName) + 1);
} else {
deviceTypeMap.set(typeName, 1);
}
}
});
this.deviceTypeOptions = Array.from(deviceTypeMap.entries()).map(
([label, count]) => ({
value: label,
label: label,
count: count,
})
);
},
// 按算法分类筛选
filterByAlgorithmCategory(value) {
this.selectedAlgorithmCategory = value;
this.selectedAlgorithmCategoryLabel =
this.getSelectedAlgorithmCategoryLabel();
},
// 按设备类型筛选
filterByDeviceType(value) {
this.selectedDeviceType = value;
const selectedOption = this.deviceTypeOptions.find(
(opt) => opt.value === value
);
this.selectedDeviceTypeLabel = selectedOption ? selectedOption.label : "";
},
// 清除所有筛选
clearAllFilters() {
this.selectedAlgorithmCategory = "";
this.selectedAlgorithmCategoryLabel = "";
this.selectedDeviceType = "";
this.selectedDeviceTypeLabel = "";
},
getNodeLabel(data) {
return data.deviceName || data.areaName || "未知节点";
},
shouldShowCount(data) {
// return data.children && data.children.length > 0;
return true;
},
getChildCount(data) {
return data.children ? data.children.length : 0;
},
getNodeCount(data) {
return data.pointNum || 0;
},
// 递归处理树数据,确保每个节点都有pointNum字段
getTreeData(response) {
const findArray = (value, depth = 0) => {
if (Array.isArray(value)) {
return value;
}
if (!value || typeof value !== "object" || depth > 4) {
return [];
}
if (
Array.isArray(value.children) &&
(value.areaId ||
value.areaid ||
value.deviceId ||
value.deviceName ||
value.areaName)
) {
return [value];
}
const keys = ["data", "rows", "list", "records", "result", "items"];
for (const key of keys) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
const result = findArray(value[key], depth + 1);
if (result.length > 0 || Array.isArray(value[key])) {
return result;
}
}
}
return [];
};
return findArray(response);
},
mapTree(nodes) {
if (!Array.isArray(nodes)) {
return;
}
nodes.forEach((node) => {
if (!node.deviceName) {
node.deviceName = node.areaName;
}
// 确保pointNum字段存在,如果没有则默认为0
if (node.pointNum === undefined || node.pointNum === null) {
node.pointNum = 0;
}
if (node.children && node.children.length > 0) {
this.mapTree(node.children);
}
});
},
// 初始化列拖拽功能
initColumnResize() {
this.$nextTick(() => {
const table = this.$refs.multipleTable;
if (!table || !table.$el) return;
const tableWrapper = table.$el;
const headers = tableWrapper.querySelectorAll(".el-table__header th");
const tableBody = tableWrapper.querySelector(".el-table__body");
if (!headers.length) return;
headers.forEach((header, index) => {
// 清除可能存在的旧拖拽手柄
const oldResizer = header.querySelector(".column-resizer");
if (oldResizer) oldResizer.remove();
// 设置表头样式
header.style.position = "relative";
header.style.userSelect = "none";
// 创建拖拽手柄
const resizer = document.createElement("div");
resizer.className = "column-resizer";
// 创建视觉反馈线条
const resizerLine = document.createElement("div");
resizerLine.className = "column-resizer-line";
resizer.appendChild(resizerLine);
header.appendChild(resizer);
let startX = 0;
let startWidth = 0;
let isDragging = false;
const onMouseDown = (e) => {
e.preventDefault();
e.stopPropagation();
startX = e.pageX;
startWidth = header.offsetWidth;
isDragging = true;
// 添加拖拽时的样式
header.classList.add("dragging");
document.body.style.cursor = "col-resize";
document.body.style.userSelect = "none";
// 添加全局遮罩
const mask = document.createElement("div");
mask.id = "resize-mask";
document.body.appendChild(mask);
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
};
const onMouseMove = (e) => {
if (!isDragging) return;
// 计算新宽度
const diffX = e.pageX - startX;
const newWidth = Math.max(80, Math.min(800, startWidth + diffX)); // 限制最小80px,最大800px
// 更新表头宽度
header.style.width = newWidth + "px";
header.style.minWidth = newWidth + "px";
header.style.maxWidth = newWidth + "px";
// 更新对应的列单元格
if (tableBody) {
const rows = tableBody.querySelectorAll("tr");
rows.forEach((row) => {
const cell = row.children[index];
if (cell) {
cell.style.width = newWidth + "px";
cell.style.minWidth = newWidth + "px";
cell.style.maxWidth = newWidth + "px";
}
});
}
// 更新列宽缓存
this.columnWidths[index] = newWidth;
};
const onMouseUp = () => {
if (!isDragging) return;
isDragging = false;
header.classList.remove("dragging");
// 移除全局遮罩
const mask = document.getElementById("resize-mask");
if (mask) mask.remove();
// 恢复鼠标样式
document.body.style.cursor = "default";
document.body.style.userSelect = "";
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
// 保存列宽
this.saveColumnWidths();
};
// 添加鼠标事件
resizer.addEventListener("mousedown", onMouseDown);
// 添加双击重置
resizer.addEventListener("dblclick", (e) => {
e.stopPropagation();
this.resetColumnWidth(index);
});
// 添加悬停提示
resizer.title = "双击重置列宽";
});
});
},
// 保存列宽到 localStorage
saveColumnWidths() {
try {
const tableData = {
widths: this.columnWidths,
timestamp: Date.now(),
};
localStorage.setItem("tableColumnWidths", JSON.stringify(tableData));
} catch (e) {
console.warn("保存列宽失败:", e);
}
},
// 加载保存的列宽
loadColumnWidths() {
try {
const saved = localStorage.getItem("tableColumnWidths");
if (!saved) return;
const { widths, timestamp } = JSON.parse(saved);
// 检查保存时间,只保留最近7天的配置
const oneWeek = 7 * 24 * 60 * 60 * 1000;
if (Date.now() - timestamp > oneWeek) {
localStorage.removeItem("tableColumnWidths");
return;
}
this.columnWidths = widths || {};
this.$nextTick(() => {
this.applyColumnWidths();
});
} catch (e) {
console.warn("加载列宽失败:", e);
}
},
// 应用列宽
applyColumnWidths() {
const table = this.$refs.multipleTable;
if (!table || !table.$el) return;
const tableWrapper = table.$el;
const headers = tableWrapper.querySelectorAll(".el-table__header th");
const tableBody = tableWrapper.querySelector(".el-table__body");
if (!headers.length || Object.keys(this.columnWidths).length === 0)
return;
headers.forEach((header, index) => {
const savedWidth = this.columnWidths[index];
if (savedWidth) {
header.style.width = savedWidth + "px";
header.style.minWidth = savedWidth + "px";
header.style.maxWidth = savedWidth + "px";
if (tableBody) {
const rows = tableBody.querySelectorAll("tr");
rows.forEach((row) => {
const cell = row.children[index];
if (cell) {
cell.style.width = savedWidth + "px";
cell.style.minWidth = savedWidth + "px";
cell.style.maxWidth = savedWidth + "px";
}
});
}
}
});
},
// 重置列宽
resetColumnWidth(index) {
const defaultWidth = this.defaultColumnWidths[index] || 100;
// 更新缓存
this.columnWidths[index] = defaultWidth;
// 应用新宽度
const table = this.$refs.multipleTable;
if (!table || !table.$el) return;
const tableWrapper = table.$el;
const headers = tableWrapper.querySelectorAll(".el-table__header th");
const tableBody = tableWrapper.querySelector(".el-table__body");
const header = headers[index];
if (header) {
header.style.width = defaultWidth + "px";
header.style.minWidth = defaultWidth + "px";
header.style.maxWidth = defaultWidth + "px";
if (tableBody) {
const rows = tableBody.querySelectorAll("tr");
rows.forEach((row) => {
const cell = row.children[index];
if (cell) {
cell.style.width = defaultWidth + "px";
cell.style.minWidth = defaultWidth + "px";
cell.style.maxWidth = defaultWidth + "px";
}
});
}
}
// 保存设置
this.saveColumnWidths();
this.$message.success("列宽已重置");
},
// 重置所有列宽
resetAllColumnWidths() {
this.columnWidths = {};
localStorage.removeItem("tableColumnWidths");
const table = this.$refs.multipleTable;
if (!table || !table.$el) return;
const tableWrapper = table.$el;
const headers = tableWrapper.querySelectorAll(".el-table__header th");
const tableBody = tableWrapper.querySelector(".el-table__body");
headers.forEach((header, index) => {
const defaultWidth = this.defaultColumnWidths[index] || 100;
header.style.width = "";
header.style.minWidth = "";
header.style.maxWidth = "";
if (tableBody) {
const rows = tableBody.querySelectorAll("tr");
rows.forEach((row) => {
const cell = row.children[index];
if (cell) {
cell.style.width = "";
cell.style.minWidth = "";
cell.style.maxWidth = "";
}
});
}
});
this.$message.success("所有列宽已重置");
},
// 处理窗口大小变化
handleResize() {
this.updateTableHeight();
this.$nextTick(() => {
this.initColumnResize();
this.applyColumnWidths();
});
// 窗口大小变化时,检查左侧面板宽度是否超出最大限制
const containerWidth = this.$el?.clientWidth || window.innerWidth;
const maxAllowedWidth = containerWidth * 0.4; // 最大不超过容器宽度的40%
if (this.leftPanelWidth > maxAllowedWidth) {
this.leftPanelWidth = Math.max(
this.minLeftWidth,
Math.min(maxAllowedWidth, this.leftPanelWidth)
);
}
// 触发表格重新布局
this.$nextTick(() => {
if (this.$refs.multipleTable) {
this.$refs.multipleTable.doLayout();
}
});
},
updateTableHeight() {
const viewportHeight = window.innerHeight || 800;
this.tableHeight = Math.max(420, Math.min(620, viewportHeight - 230));
},
// 新增的拖拽相关方法 - 只影响左侧树
startResize(event) {
this.isResizing = true;
this.startX = event.clientX;
this.startWidth = this.leftPanelWidth;
// 添加全局事件监听
document.addEventListener("mousemove", this.onResize);
document.addEventListener("mouseup", this.stopResize);
// 添加拖拽时的样式
document.body.style.cursor = "col-resize";
document.body.style.userSelect = "none";
// 添加拖拽遮罩
const mask = document.createElement("div");
mask.id = "resize-mask";
mask.style.position = "fixed";
mask.style.top = "0";
mask.style.left = "0";
mask.style.width = "100%";
mask.style.height = "100%";
mask.style.zIndex = "9999";
mask.style.cursor = "col-resize";
document.body.appendChild(mask);
},
onResize(event) {
if (!this.isResizing) return;
// 计算新的宽度
const diffX = event.clientX - this.startX;
let newWidth = this.startWidth + diffX;
// 限制最小最大宽度
newWidth = Math.max(
this.minLeftWidth,
Math.min(this.maxLeftWidth, newWidth)
);
this.leftPanelWidth = newWidth;
// Element UI does not observe this absolutely positioned container, so
// recalculate its header/body widths while the tree is being resized.
this.$nextTick(() => {
if (this.$refs.areaTree) {
this.$refs.areaTree.$el.style.width = "100%";
}
this.scheduleMainTableLayout();
});
},
stopResize() {
if (!this.isResizing) return;
this.isResizing = false;
// 移除全局事件监听
document.removeEventListener("mousemove", this.onResize);
document.removeEventListener("mouseup", this.stopResize);
// 恢复鼠标样式
document.body.style.cursor = "";
document.body.style.userSelect = "";
// 移除拖拽遮罩
const mask = document.getElementById("resize-mask");
if (mask) mask.remove();
// 保存宽度到localStorage
this.saveLeftPanelWidth();
this.$nextTick(this.scheduleMainTableLayout);
},
scheduleMainTableLayout() {
if (this.tableLayoutFrame) {
cancelAnimationFrame(this.tableLayoutFrame);
}
this.tableLayoutFrame = requestAnimationFrame(() => {
this.tableLayoutFrame = null;
if (this.$refs.multipleTable) {
this.$refs.multipleTable.doLayout();
}
});
},
// 保存左侧面板宽度
saveLeftPanelWidth() {
try {
localStorage.setItem(LEFT_PANEL_WIDTH_STORAGE_KEY, this.leftPanelWidth);
} catch (e) {
// console.warn('保存左侧面板宽度失败:', e);
}
},
// 加载左侧面板宽度
loadLeftPanelWidth() {
try {
const savedWidth = localStorage.getItem(LEFT_PANEL_WIDTH_STORAGE_KEY);
if (savedWidth) {
this.leftPanelWidth = parseInt(savedWidth, 10);
}
} catch (e) {
// console.warn('加载左侧面板宽度失败:', e);
}
},
// 重置左侧面板宽度
resetLeftPanelWidth() {
this.leftPanelWidth = DEFAULT_LEFT_PANEL_WIDTH;
this.saveLeftPanelWidth();
// 更新树布局
this.$nextTick(() => {
if (this.$refs.areaTree) {
this.$refs.areaTree.$el.style.width = "100%";
}
});
},
//获取无人机图片
getUavImg() {
var data = {
patrolPointId: this.currentRow.patrolpointId,
};
irUavImg(data)
.then((response) => {
this.imageUrl = response.data;
this.imagePath = response.data;
})
.catch(() => {
item.loading = false;
});
},
// 无人机画框完成
uavComplete() {
var all_data = JSON.parse(localStorage.getItem("drawData"));
var imgElement = JSON.parse(localStorage.getItem("imgElement"));
const data = {
coordinates: all_data,
filePath: this.imagePath,
imgWidth: imgElement.width,
imgHeight: imgElement.height,
imgType: "infra_yu3",
patrolPointCode: this.currentRow.patrolpointCode,
patrolPointId: this.currentRow.patrolpointId,
};
capturePicture(data)
.then((response) => {
if (response.code == 200) {
this.monitorShow = false;
}
})
.catch(() => {
item.loading = false;
});
},
// tab切换
handleClick(tab, event) {
if (tab.name != "home") {
this.$refs.checkItemSetRef.clearAll();
}
this.activeName = tab.name;
},
algorithmClick() {
this.algorithmShow = true;
},
//选择完设备后调用
handleChooseDevicesOk(device) {
addPatrolpointpreset({
patroldeviceCode: device.patroldeviceCode,
eqpbookId: device.eqpbookId,
patrolpointId: this.currentRow.patrolpointId,
}).then(() => {
this.$message.success("绑定成功");
this.loadPointDevices();
});
},
//显示上传
eventUpload() {
this.showRebotUploader = true;
},
handleRebotUploaderConfirm() {},
// 选择算法完毕
handleChooseAlgOk(result) {
const selectedAlgs = result;
// 提取选中的算法ID和名称
const algSubtypeIds = selectedAlgs.map((item) => item.id).join(",");
const algSubtypeNames = selectedAlgs.map((item) => item.name).join(",");
this.currentRow.algSubtypeIds = algSubtypeIds;
this.currentRow.algSubtypeName = algSubtypeNames;
this.currentRow.alarmThreshold = JSON.stringify(selectedAlgs);
// 调用保存到后端的方法
this.handleChangePointCode(this.currentRow);
this.$message.success("算法配置已更新");
// setTimeout(() => {
// this.loadList();
// }, 500);
},
saveAlgorithmConfig(row) {
const postData = {
patrolpointId: row.patrolpointId,
algSubtypeIds: row.algSubtypeIds,
algSubtypeName: row.algSubtypeName,
alarmThreshold: row.alarmThreshold,
};
// 调用专门的API保存算法配置
// 假设有一个 updateAlgorithm 的 API
// updateAlgorithm(postData).then(res => {
// console.log("算法配置保存成功");
// }).catch(err => {
// console.error("保存失败:", err);
// this.$message.error("保存失败");
// });
// 或者使用现有的 handleChangePointCode
this.handleChangePointCode(row);
},
//点位列表,更换智能识别类型
chooseAlgorithm(row) {
if (this.batchDeleteMode) {
this.$message.warning("请先退出批量删除模式");
return;
}
this.currentRow = row;
this.showChooseAlg = true;
},
//点击图片上传按钮
handleUploadPicture(row) {
this.currentRow = row;
this.$nextTick(() => {
this.showUploader = true;
});
},
// 显示基准判别图片数量
rowPicCount(row) {
if (row.disRefPicturePath && row.disRefPicturePath.length > 0) {
return row.disRefPicturePath.split(",").length;
}
return 0;
},
// 图片上传弹窗,点击确定
handleUploaderConfirm(fileList) {
if (fileList.length > 0) {
this.currentRow.disRefPicturePath = linq
.from(fileList)
.select((x) => x.url)
.toArray()
.join(",");
} else {
this.currentRow.disRefPicturePath = "";
}
this.handleChangePointCode(this.currentRow);
},
handleCloseTemptrueModel() {
this.resetForm("formTemptrue");
const drawRefs = linq
.from(this.$refs.playerPlayform2.$children)
.where((x) => x.$vnode.componentOptions.tag === "DrawShape")
.toArray();
const drawRef = drawRefs[0];
drawRef.clearShapes();
},
handleCloseVL() {
this.$refs.playerPlayform.handleCloseAllChannel();
},
handleCloseIR() {
this.$refs.playerPlayform2.handleCloseAllChannel();
},
handleLoading(isLoading) {
this.currentImageRow.loading = isLoading;
},
handleImageLoad() {
this.currentImageRow.loading = false;
this.$refs.asyncImage.$el.querySelector("img").click();
},
handleImageLoadError() {
this.currentImageRow.loading = false;
},
handleViewImages(row) {
this.currentSrc = "";
this.currentSrcList = [];
this.showPreviewImages = false;
this.$nextTick(() => {
this.currentImageRow = row;
const srcs = row.channelImg.split(",");
this.currentSrc = srcs[0];
this.currentSrcList = srcs;
this.showPreviewImages = true;
});
},
handleAreaTreeCurrentNodeChange(data) {
this.currentTreeNode = data;
},
handleChangePointCode(row) {
const postData = lodash.cloneDeep(row);
postData.areaId = this.currentTreeNode.areaId;
postData.deviceId = this.currentTreeNode.deviceId;
if (postData.patrolTypeId && postData.patrolTypeId.length > 0) {
postData.patrolTypeId = postData.patrolTypeId.join(",");
} else {
postData.patrolTypeId = "";
}
if (row.patrolpointId) {
proDotTableEditSin(postData);
} else {
proDotTableAddSin(postData).then((res) => {
row.patrolpointId = res.data;
});
}
},
handleSelectPointCode(node, row) {
row.patrolTypeId.push(node.id);
this.handleChangePointCode(row);
},
handleDeSelectPointCode(node, row) {
if (row.patrolTypeId.indexOf(node.id) != -1) {
row.patrolTypeId.splice(row.patrolTypeId.indexOf(node.id), 1);
this.handleChangePointCode(row);
}
},
addAction() {
this.presetData.presetActionList.push({
actionType: "",
videoTime: "",
photoCount: "1",
photoSeper: "",
});
},
removeAction(index) {
this.presetData.presetActionList.splice(index, 1);
},
getTreeNodeQuery(node = {}, patrolpointName = "") {
const areaId = node.areaId || node.areaid || node.id || "";
return {
deviceId: node.deviceId || node.deviceid || "",
mainDeviceId: node.mainDeviceId || node.mainDeviceID || node.mainDeviceid || "",
areaId,
areaid: areaId,
patrolpointName: patrolpointName || node.patrolpointName || "",
pageNum: "",
pageSize: "",
};
},
/* 初始化左侧列表数据 */
getAreaList1() {
treeAreaDeviceSelectFilterByPresetRelation().then((res) => {
const treeData = this.getTreeData(res);
this.mapTree(treeData);
this.areaList = treeData;
if (treeData.length === 0) {
this.treeobj = null;
this.currentTreeNode = null;
this.tableData = [];
this.loading = false;
return;
}
let data = this.getTreeNodeQuery(treeData[0]);
this.treeobj = data;
this.loadList();
}).catch(() => {
this.areaList = [];
this.treeobj = null;
this.currentTreeNode = null;
this.tableData = [];
});
},
/* 点击左侧获取响应节点数据 */
handleAreaList(e) {
this.clearAllFilters();
this.currentTreeNode = e;
this.treeobj = this.getTreeNodeQuery(e);
this.formAdd.deviceId = this.treeobj.deviceId;
this.formAdd.mainDeviceId = this.treeobj.mainDeviceId;
this.formAdd.areaId = this.treeobj.areaId;
this.currentRow = null;
this.loadList();
},
loadList(data) {
this.loading = true;
const query = data || this.treeobj || {};
const getRows = (response) => {
if (Array.isArray(response && response.rows)) {
return response.rows;
}
if (Array.isArray(response && response.list)) {
return response.list;
}
if (Array.isArray(response && response.records)) {
return response.records;
}
if (response && response.data && Array.isArray(response.data.rows)) {
return response.data.rows;
}
if (response && response.data && Array.isArray(response.data.list)) {
return response.data.list;
}
if (response && response.data && Array.isArray(response.data.records)) {
return response.data.records;
}
if (response && Array.isArray(response.data)) {
return response.data;
}
if (
response &&
response.data &&
response.data.data &&
Array.isArray(response.data.data.rows)
) {
return response.data.data.rows;
}
if (
response &&
response.data &&
response.data.data &&
Array.isArray(response.data.data.list)
) {
return response.data.data.list;
}
if (
response &&
response.data &&
response.data.data &&
Array.isArray(response.data.data.records)
) {
return response.data.data.records;
}
if (response && response.data && Array.isArray(response.data.data)) {
return response.data.data;
}
return [];
};
const loadRelationList = () =>
proDotManTableRelation(query)
.then((response) => {
return getRows(response).length > 0
? response
: proDotManTable(query);
})
.catch(() => proDotManTable(query));
loadRelationList()
.then((res) => {
this.loading = false;
const rows = getRows(res);
rows.forEach((row) => {
row.bindPoints = [];
row.bindDevices = [];
if (row.patrolTypeId && /^\d/.test(row.patrolTypeId) == false) {
var id = row.patrolTypeId;
row.patrolTypeId = id.substring(1);
}
if (row.patrolTypeId && row.patrolTypeId.length > 0) {
row.patrolTypeId = linq
.from(row.patrolTypeId.split(","))
.select((x) => {
return parseInt(x);
})
.toArray();
} else {
row.patrolTypeId = [];
}
// 调试:打印 alarmThreshold
// if (row.alarmThreshold) {
// console.log(
// "点位",
// row.patrolpointName,
// "的 alarmThreshold:",
// row.alarmThreshold
// );
// }
});
this.tableData = rows;
this.updateDeviceTypeOptions();
this.$nextTick(() => {
if (this.$refs.multipleTable) {
this.$refs.multipleTable.doLayout();
}
this.initColumnResize();
this.loadColumnWidths();
});
})
.catch(() => {
this.loading = false;
});
},
/* 修改单条监控点位数据 */
editRows1(index, row) {
// 批量删除模式下不允许编辑
if (this.batchDeleteMode) {
this.$message.warning("请先退出批量删除模式");
return;
}
this.showAdd = true;
this.titleAlert = "修改监控点位";
// 深拷贝数据用于修改
this.formAdd = lodash.cloneDeep(row);
// 确保 patrolTypeId 是数组格式(如果是字符串则转换)
if (
this.formAdd.patrolTypeId &&
typeof this.formAdd.patrolTypeId === "string"
) {
this.formAdd.patrolTypeId = this.formAdd.patrolTypeId
.split(",")
.map((id) => parseInt(id));
}
// 确保 pointImptLevel 正确处理
if (
this.formAdd.pointImptLevel === undefined ||
this.formAdd.pointImptLevel === null
) {
this.formAdd.pointImptLevel = null;
}
// 确保 purpose 正确处理
if (this.formAdd.purpose === undefined) {
this.formAdd.purpose = "";
}
// 清除表单验证状态
this.$nextTick(() => {
if (this.$refs.formAdd) {
this.$refs.formAdd.clearValidate();
}
});
},
/* 删除单条监控点位数据 */
deleteRows1(index, row) {
// 批量删除模式下不允许删除
if (this.batchDeleteMode) {
this.$message.warning("请先退出批量删除模式");
return;
}
this.$confirm("确定要删除数据吗?").then((res) => {
proDotTableDeleteSin(row.patrolpointId).then((res) => {
this.loadList();
});
});
},
/* 新增单条监控点位数据t弹窗展示 */
addAlertShow1() {
// 批量删除模式下不允许新增
if (this.batchDeleteMode) {
this.$message.warning("请先退出批量删除模式");
return;
}
this.showAdd = true;
this.titleAlert = "添加监控点位";
// 完全重置formAdd对象,清空所有输入字段
this.formAdd = {
patrolpointName: "",
patrolContent: "",
purpose: "", // 清空读数单位
areaId: this.currentTreeNode ? this.currentTreeNode.areaId : "",
mainDeviceId: this.currentTreeNode
? this.currentTreeNode.mainDeviceId
: "",
deviceId: this.currentTreeNode ? this.currentTreeNode.deviceId : "",
patrolpointCode: "",
patrolTypeId: null, // 关键:清空巡检类型
pointImptLevel: null, // 关键:清空重要等级
positionX: null,
positionY: null,
positionZ: null,
};
// 清除表单验证状态
this.$nextTick(() => {
if (this.$refs.formAdd) {
this.$refs.formAdd.clearValidate();
}
});
},
/* 新增、修改单条监控点位数据取消 */
alertCancel1() {
this.showAdd = false;
this.formAdd = {
patrolpointName: "",
patrolContent: "",
purpose: "",
areaId: this.currentTreeNode ? this.currentTreeNode.areaId : "",
mainDeviceId: this.currentTreeNode
? this.currentTreeNode.mainDeviceId
: "",
deviceId: this.currentTreeNode ? this.currentTreeNode.deviceId : "",
patrolpointCode: "",
patrolTypeId: null,
pointImptLevel: null,
positionX: null,
positionY: null,
positionZ: null,
};
// 清除验证
this.$nextTick(() => {
if (this.$refs.formAdd) {
this.$refs.formAdd.clearValidate();
}
});
},
/* 新增、修改单条监控点位数据确认 */
alertConfirm1() {
this.$refs.formAdd.validate((valid) => {
if (valid) {
this.formAddLoading = true;
const postData = lodash.cloneDeep(this.formAdd);
postData.areaId = this.currentTreeNode.areaId;
postData.deviceId = this.currentTreeNode.deviceId;
if (postData.patrolTypeId && postData.patrolTypeId.length > 0) {
postData.patrolTypeId = postData.patrolTypeId.join(",");
} else {
postData.patrolTypeId = "";
}
if (this.titleAlert == "添加监控点位") {
proDotTableAddSin(postData)
.then(() => {
this.showAdd = false;
this.resetForm("formAdd");
this.formAddLoading = false;
this.loadList();
})
.catch(() => {
this.formAddLoading = false;
});
} else {
proDotTableEditSin(postData)
.then((res) => {
this.showAdd = false;
this.resetForm("formAdd");
this.formAddLoading = false;
this.loadList();
})
.catch(() => {
this.formAddLoading = false;
});
}
}
});
},
//选中一条巡检点位
handleCurrentChange(row) {
this.currentRow = row;
this.loadDevicePoints();
this.loadPointDevices();
},
// 加载巡检点位绑定的巡检设备 (机器人或者无人机)
loadPointDevices() {
if (!this.currentRow.patrolpointId) {
return;
}
this.pointLoading = true;
listPatrolpointpreset({
patrolpointId: this.currentRow.patrolpointId,
noVideo: true,
})
.then((res) => {
this.pointLoading = false;
this.currentRow.bindDevices = res.rows;
})
.catch(() => {
this.pointLoading = false;
});
},
//加载巡检点位绑定的预置点
loadDevicePoints() {
this.pointLoading = true;
getPatroPointDevicePoints(this.currentRow.patrolpointId)
.then((res) => {
this.pointLoading = false;
res.data.forEach((row) => {
row.currentSrc = "";
row.currentSrcList = [];
row.loading = false;
});
this.currentRow.bindPoints = res.data;
})
.catch(() => {
this.pointLoading = false;
});
},
handleEnablePoint(item, index) {
item.loading = true;
const postData = lodash.cloneDeep(item);
postData.isEnable = postData.isEnable == 1 ? 0 : 1;
editPoint(postData)
.then(() => {
item.loading = false;
item.isEnable = item.isEnable == 1 ? 0 : 1;
this.$message.success(
"操作成功:点位已" + (item.isEnable ? "启用" : "停用")
);
})
.catch(() => {
item.loading = false;
});
},
//解绑预置点
handleUnbindPoint(item, index) {
this.$confirm("确定解绑预置点吗?").then((res) => {
unbindPoint(item.presetPosId).then(() => {
this.$message.success("解绑预置点成功");
this.currentRow.bindPoints.splice(index, 1);
});
});
},
//解绑巡检设备
handleUnbindDevice(item, index) {
this.$confirm("确定解绑巡检设备吗?").then((res) => {
delPatrolpointpreset(item.patrolpointPresetId).then(() => {
this.$message.success("解设巡检设备绑成功");
this.currentRow.bindDevices.splice(index, 1);
});
});
},
/* 删除多条监控点位数据 - 已废弃,改用批量删除模式 */
deleteMul1() {
// 此方法已废弃,改用批量删除模式
this.toggleBatchDeleteMode();
},
// 搜索左侧的树
searchTree() {
const searchText = this.inputSearch1.trim();
if (!searchText) {
this.getAreaList1();
return;
}
const searchNodes = (nodes) => {
const result = [];
nodes.forEach((node) => {
const nodeName = node.areaName || node.deviceName || "";
const isMatch = nodeName.includes(searchText);
if (isMatch) {
result.push(node);
} else {
if (node.children && node.children.length > 0) {
const matchedChildren = searchNodes(node.children);
if (matchedChildren.length > 0) {
const newNode = {
...node,
children: matchedChildren,
pointNum: node.pointNum,
};
result.push(newNode);
}
}
}
});
return result;
};
const originalData = this.areaList;
const filteredData = searchNodes(originalData);
this.areaList = filteredData;
if (filteredData.length === 0) {
this.$message.warning("未找到匹配的区域");
}
},
/* 搜索相关名称的监控点位数据 */
searchTable1() {
let data = {
...(this.treeobj || this.getTreeNodeQuery(this.currentTreeNode || {})),
patrolpointName: this.inputSearch,
};
this.loadList(data);
},
// 点位配置,弹出框
pointConfiguration() {
this.$refs.form.style.width = "72%";
this.showRightPanel = true;
},
// 点位配置收起
collectPoint() {
this.$refs.form.style.width = "100%";
this.showRightPanel = false;
},
// 可见光弹窗展示
visibleShow1() {
if (this.currentRow === null) {
this.$modal.msgWarning("请先选择在列表中点击一条点位数据!");
return;
}
// 重置预置点相关数据
this.presetDotNum = "";
this.presetName = "";
this.presetNumber = "";
this.is_convert = false;
this.is_set = false;
// 重置 presetData 中的预置点名称
this.presetData.presetPosName = "";
this.presetData.presetPosCode = "";
this.monitorShow = true;
this.monitorType = "vl";
this.titleLightTitle = "预置位配置";
bindingMonitorDotList(this.monitorType).then((res) => {
this.monitorList = res.data;
});
},
// 红外光弹窗展示
infraredShow1() {
// 重置预置点相关数据
this.presetDotNum = "";
this.presetName = "";
this.presetNumber = "";
this.is_set = false;
// 重置 presetData 中的预置点名称
this.presetData.presetPosName = "";
this.presetData.presetPosCode = "";
// 移动设备绑定暂时停用,避免调用无人机图片接口。
this.monitorShow = true;
this.monitorType = "ir";
this.titleLightTitle = "绑定热成像监控点";
bindingMonitorDotList(this.monitorType).then((res) => {
this.monitorList = res.data;
});
},
// 监控点位弹窗获取左侧点位数据
handleMonitor(e) {
this.presetDotNum = "";
this.presetDotDis = true;
if (e.eqpbookId) {
this.presetDotDis = false;
this.equipobj = {
eqpbookId: e.eqpbookId,
channelCode: e.channelCode,
patroldeviceCode: e.patroldeviceCode,
channelId: e.channelId,
};
this.patroldeviceName = e.patroldeviceName;
let data = {
patroldeviceCode: e.patroldeviceCode,
channelId: e.channelId,
channelCode: e.channelCode,
};
this.presetData.patrolpointId = this.currentRow.patrolpointId;
this.presetData.stdparolPointId = this.currentRow.stdparolPointId;
this.presetData.deviceId = this.currentRow.deviceId;
this.presetData.eqpbookId = e.eqpbookId;
this.presetData.channelId = e.channelId;
// 获取该设备的预置点列表并填充 presetArr
getDeviceList(e.patroldeviceCode, e.channelCode).then(({ data }) => {
this.presetArr = data;
// 如果有预置点,默认选中第一个
if (data && data.length > 0) {
this.presetDotNum = data[0].dictValue;
this.presetName = data[0].dictLabel;
this.presetNumber = data[0].dictValue;
// 将预置点名称赋值给 presetData
this.presetData.presetPosName = this.presetName;
this.presetData.presetPosCode = this.presetDotNum;
} else {
this.presetName = "";
this.presetNumber = "";
this.presetData.presetPosName = "";
this.presetData.presetPosCode = "";
}
});
this.handleOpenChannel(data);
}
},
// 云台控制
controlEquip1(a1) {
let data = {
patroldeviceCode: this.equipobj.patroldeviceCode,
channelCode: this.equipobj.channelCode,
speed: 5,
ptzControlType: a1,
};
cloudControl(data).then((res) => {});
},
controlEquip2(a1) {
let data = {
patroldeviceCode: this.equipobj.patroldeviceCode,
channelCode: this.equipobj.channelCode,
speed: 5,
ptzControlType: a1,
};
focalAperture(data).then((res) => {});
},
// 选择预置点 0——255
presetDotChange1(e) {
this.cssClass = false;
this.presetData.presetPosCode = e;
var obj = this.presetArr.find((item) => {
return item.dictValue == e;
});
if (obj) {
this.presetName = obj.dictLabel;
this.presetNumber = obj.dictValue;
// 选择预置点时更新 presetData 中的预置点名称
this.presetData.presetPosName = this.presetName;
if (obj.cssClass == 1) {
this.cssClass = true;
}
} else {
this.presetName = "";
this.presetNumber = "";
this.presetData.presetPosName = "";
}
},
// 转换预置点
convertPresetDot1() {
convertPresetDot({
patroldeviceCode: this.equipobj.patroldeviceCode,
channelCode: this.equipobj.channelCode,
presetCode: this.presetDotNum,
name: this.presetDotNum,
}).then((res) => {
this.is_convert = true;
this.$modal.msgSuccess("转到预置点成功!");
});
},
// 设置预置点
setPresetDot1() {
if (this.presetDotDis) {
this.$modal.msgWarning("请选择通道!");
return;
}
if (this.newpresetName.trim().length === 0) {
this.$modal.msgWarning("请先输入预置点名称!");
return;
}
var obj = this.presetArr.find((item) => {
return item.dictLabel == this.newpresetName.trim();
});
if (obj) {
this.$modal.msgWarning("预置点名称重复!");
return;
}
presetDotDistribute({
patroldeviceCode: this.equipobj.patroldeviceCode,
channelCode: this.equipobj.channelCode,
presetCode: this.presetDotNum,
name: this.newpresetName,
}).then((res) => {
this.is_set = true;
this.$modal.msgSuccess("设置预置点成功!");
this.popovervisible = false;
// 重新获取预置点列表,并更新当前选中的预置点信息
getDeviceList(this.equipobj.patroldeviceCode, this.equipobj.channelCode).then(({ data }) => {
this.presetArr = data;
// 找到新添加的预置点并选中
const newPreset = data.find(item => item.dictLabel === this.newpresetName.trim());
if (newPreset) {
this.presetDotNum = newPreset.dictValue;
this.presetName = newPreset.dictLabel;
this.presetNumber = newPreset.dictValue;
// 更新 presetData 中的预置点名称
this.presetData.presetPosName = this.presetName;
this.presetData.presetPosCode = this.presetDotNum;
}
this.newpresetName = "";
});
});
},
//删除预置点
delPresetDot1() {
if (this.presetDotNum) {
this.$confirm("此操作将永久删除该预置点, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
this.$message({
type: "success",
message: "删除成功!",
});
this.popovervisible = false;
this.getDeviceList();
})
.catch(() => {});
} else {
this.$modal.msgWarning("请选择预置点!");
}
},
//编辑预置点
editPresetDot1() {
if (this.presetName.trim().length === 0) {
this.$modal.msgWarning("请先输入预置点名称!");
return;
}
var obj = this.presetArr.find((item) => {
return item.dictLabel == this.presetName.trim();
});
if (obj && obj.dictValue != this.presetDotNum) {
this.$modal.msgWarning("预置点名称重复!");
return;
}
issueUpdate({
patrolDeviceCode: this.equipobj.patroldeviceCode,
channelCode: this.equipobj.channelCode,
presetCode: this.presetDotNum,
name: this.presetName,
}).then((res) => {
this.is_set = true;
// 编辑后更新 presetData 中的预置点名称
this.presetData.presetPosName = this.presetName;
getDeviceList(this.equipobj.patroldeviceCode, this.equipobj.channelCode).then(({ data }) => {
this.presetArr = data;
});
this.$modal.msgSuccess("设置预置点成功!");
});
},
//上一步
prevStep() {
this.monitorShow = true;
this.monitorSecShow = false;
},
// 下一步弹窗显示
nextStep1() {
this.monitorShow = false;
this.monitorSecShow = true;
this.drawShapIndex = 0;
this.presetData.presetPosName = this.presetName;
this.handleOpenChannel(this.equipobj, true);
},
// 选择绘制方式
chooseDrawShape1(a1, a2) {
this.drawShapIndex = a1;
this.startDraw(0, a2);
},
// 追加点位数据
addDot1(shape) {
this.testobj = lodash.cloneDeep(this.testobjBackup);
this.testobj.measureAreaType = shape.type;
this.testobj.coordinate = shape.getData().data;
this.presetDataShow = true;
},
// 删除点位数据
deleteDot1(index) {
this.presetData.presetParamList.splice(index, 1);
},
// 保存点位数据
saveDot1() {
const invalidPhotoCount = this.presetData.presetActionList.some(
(item) =>
item.photoCount !== null &&
item.photoCount !== undefined &&
String(item.photoCount).trim() !== "" &&
!/^\d+$/.test(String(item.photoCount).trim())
);
const invalidPhotoInterval = this.presetData.presetActionList.some(
(item) =>
item.photoSeper !== null &&
item.photoSeper !== undefined &&
String(item.photoSeper).trim() !== "" &&
!/^\d+$/.test(String(item.photoSeper).trim())
);
if (invalidPhotoCount || invalidPhotoInterval) {
this.$message.error("\u62cd\u7167\u6b21\u6570\u548c\u62cd\u7167\u95f4\u9694\u53ea\u80fd\u8f93\u5165\u975e\u8d1f\u6574\u6570");
return;
}
// 确保 presetPosName 有值
if (!this.presetData.presetPosName) {
this.presetData.presetPosName = this.presetName || this.presetData.presetPosName || '';
}
// 如果是热成像模式,确保预置点名称正确传递
if (this.monitorType === 'ir' && !this.presetData.presetPosName) {
// 从 presetArr 中查找当前选中的预置点
const selectedPreset = this.presetArr.find(
item => item.dictValue === this.presetDotNum
);
if (selectedPreset) {
this.presetData.presetPosName = selectedPreset.dictLabel;
}
}
// 调试日志 - 测试完成后可以删除
console.log('保存预置点数据:', {
monitorType: this.monitorType,
presetPosName: this.presetData.presetPosName,
presetDotNum: this.presetDotNum,
presetName: this.presetName,
});
this.loadingBind = true;
bindingPreset(Object.assign({}, this.presetData))
.then((res) => {
this.presetData = {
presetPosCode: "",
channelId: "",
eqpbookId: "",
patrolpointId: "",
stdparolPointId: "",
deviceId: "",
presetPosName: "",
stopTime: "",
channelImg: "",
description: "",
presetActionList: [],
presetParamList: [],
};
this.testobj = lodash.cloneDeep(this.testobjBackup);
this.loadingBind = false;
this.currentRow.patrolpointId = res.data;
this.monitorSecShow = false;
this.monitorShow = false;
this.$message.success("绑定成功");
this.loadDevicePoints();
})
.catch(() => {
this.loadingBind = false;
});
},
// 下一步弹窗确定
aletrSecConfirm1() {
const me = this;
this.$refs.formTemptrue.validate((valid) => {
if (valid) {
me.presetData.presetParamList.push(lodash.cloneDeep(me.testobj));
me.presetDataShow = false;
}
});
},
// 下一步弹窗取消
aletrSecCancel1() {
this.measureName = "";
this.measureDistance = "";
this.radioRate = "";
this.warnMaxTemp = "";
this.warnMinTemp = "";
this.warnAverageTemp = "";
this.warnDifferenceTemp = "";
this.alarmMaxTemp = "";
this.alarmMinTemp = "";
this.alarmAverageTemp = "";
this.alarmDifferenceTemp = "";
this.presetDataShow = false;
},
// 绘板框和录像
startDraw(index, type) {
const drawRefs = linq
.from(this.$refs.playerPlayform2.$children)
.where((x) => x.$vnode.componentOptions.tag === "DrawShape")
.toArray();
const drawRef = drawRefs[index];
drawRef.show();
this.$nextTick(() => {
drawRef.init();
drawRef.startDraw(type || ShapeEnum.rect);
});
},
handleDrawComplete(scope, shape) {
setTimeout(() => {
this.addDot1(shape);
}, 100);
},
handleChangeIndex(e) {
this.currentPlayer = e.player;
},
getDeviceList() {
getDeviceList(this.patroldeviceCode, this.channelCode).then(
({ data }) => {
this.presetArr = data;
}
);
},
handleOpenChannel(row, next) {
this.patroldeviceCode = row.patroldeviceCode;
this.channelCode = row.channelCode;
this.presetName = "";
this.presetNumber = "";
this.getDeviceList();
this.$nextTick(() => {
this.$refs[next ? "playerPlayform2" : "playerPlayform"]
.openChannel({
deviceId: 1,
deviceCode: row.patroldeviceCode,
channelId: row.channelId,
channelCode: row.channelCode,
playerType: "zl",
})
.then(() => {});
});
},
},
};
</script>
<style lang="scss" scoped>
/* 原有样式保持不变 - 树结构相关样式 */
.vue-treeselect__menu-container .vue-treeselect__menu {
background: #274a58;
}
.vue-treeselect__option--highlight {
background: #222f3d;
}
.el-select-dropdown {
background-color: #274a58;
}
.el-picker-panel,
.el-select-dropdown,
.el-select .el-input__inner:focus,
.el-select .el-input.is-focus .el-input__inner {
border-color: #40486a;
}
.el-input__inner,
.el-textarea__inner,
.el-range-input {
border-color: #40486a;
}
.el-input.is-disabled .el-input__inner {
background-color: transparent;
}
.el-input-number--medium .el-input-number__increase,
.el-input-number--medium .el-input-number__decrease {
background-color: transparent;
}
.el-popover {
background-color: #222f3d;
border-color: #222f3dff;
}
/* 表格样式优化 */
.custom-table {
width: 100%;
border-radius: 4px;
overflow: hidden;
margin-top: 10px;
background: transparent !important;
}
/* 表格拖拽相关样式 */
.resizable-table .el-table__header th {
position: relative;
user-select: none;
}
.resizable-table .column-resizer {
position: absolute;
right: 0;
top: 0;
width: 16px;
height: 100%;
cursor: col-resize;
z-index: 100;
display: flex;
justify-content: flex-end;
align-items: center;
}
.resizable-table .column-resizer-line {
width: 3px;
height: 60%;
background-color: #1890ff;
opacity: 0;
transition: opacity 0.2s;
border-radius: 2px;
pointer-events: none;
}
.resizable-table .column-resizer:hover .column-resizer-line {
opacity: 0.8;
}
.resizable-table .column-resizer:active .column-resizer-line {
opacity: 1;
background-color: #40a9ff;
}
/* 拖拽时的全局遮罩 */
#resize-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
cursor: col-resize;
background-color: transparent;
}
/* 表格列拖拽时的样式 */
.resizable-table .el-table__header th.dragging {
background-color: #012773 !important;
}
.resizable-table .el-table__header th.dragging::after {
content: "";
position: absolute;
right: 0;
top: 0;
width: 2px;
height: 100%;
background-color: #1890ff;
opacity: 0.8;
}
/* 表格内输入框样式 */
.table-input .el-input__inner {
background-color: #1e3a4a;
border-color: #2a4a5a;
color: #ffffff;
height: 32px;
line-height: 32px;
width: 100%;
min-width: 80px;
}
.table-input .el-input__inner:focus {
border-color: #1890ff;
}
/* 表格内选择框样式 */
.table-select {
width: 100%;
min-width: 80px;
}
.table-select .el-input__inner {
background-color: #1e3a4a;
border-color: #2a4a5a;
color: #ffffff;
height: 32px;
line-height: 32px;
width: 100%;
}
.table-select .el-input__inner:focus {
border-color: #1890ff;
}
.table-select .el-input__suffix {
color: #ffffff;
}
/* 左侧树区域样式 - 修改为独立布局 */
.sec-left {
display: flex;
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.point-layout {
display: block;
position: relative;
padding: 10px;
box-sizing: border-box;
}
/* 左侧面板容器 - 独立宽度控制 */
.resize-container {
height: 100%;
position: relative;
overflow: hidden;
transition: width 0.1s ease;
flex-shrink: 0;
}
.point-layout > .resize-container {
height: 100%;
box-sizing: border-box;
}
/* 左侧内容区域 */
.sec-line {
width: 100%;
height: 100%;
top: 0;
bottom: 0;
margin-top: 0;
padding: 0 10px 10px;
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
position: relative;
padding-right: 14px;
background: transparent;
border: 1px solid rgba(63, 127, 216, 0.72);
border-radius: 2px;
}
.sec-a2 {
flex: 1;
min-height: 0;
position: relative;
box-sizing: border-box;
padding-right: 0;
overflow-y: auto;
overflow-x: auto;
background: transparent !important;
scrollbar-width: thin;
scrollbar-color: #2f7fe8 rgba(6, 27, 79, 0.7);
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
&::-webkit-scrollbar-track {
background: rgba(6, 27, 79, 0.7);
}
&::-webkit-scrollbar-thumb {
background: #2f7fe8;
border-radius: 4px;
border: 1px solid #0d327d;
}
&::-webkit-scrollbar-thumb:hover {
background: #54a5ff;
}
::v-deep .el-tree {
display: inline-block;
width: max-content;
min-width: 100%;
background-color: transparent !important;
color: #e0e0e0;
}
::v-deep .el-tree-node__content {
width: 100%;
min-width: 0;
height: 24px;
line-height: 24px;
background-color: transparent !important;
color: #e0e0e0;
font-size: 14px;
&:hover {
background: #0c225c !important;
}
}
::v-deep .el-tree-node.is-current > .el-tree-node__content {
background: linear-gradient(180deg, #174d99 0%, #0c225c 50%, #174d99 100%) !important;
}
::v-deep .custom-tree-node {
min-width: 0;
width: max-content;
display: flex;
align-items: center;
padding-right: 8px;
}
// 叶子节点没有展开小三角,把隐形三角占位收窄一半,
// 名称略向左靠(约12px),让各行的间距节奏看起来接近一致。
::v-deep .xg_tree .el-tree-node__expand-icon.is-leaf {
display: inline-block;
width: 12px;
}
::v-deep .child-count {
color: #909399;
font-size: 14px;
flex-shrink: 0;
margin-left: 4px;
white-space: nowrap;
}
}
/* 搜索框样式 */
.search {
width: 100%;
position: relative;
margin-top: 0;
margin-bottom: 10px;
.el-input__inner {
background-color: #1e3a4a;
border-color: #2a4a5a;
color: #ffffff;
height: 32px;
padding-right: 45px;
}
.button {
position: absolute;
top: 0;
right: 0;
height: 32px;
width: 40px;
padding: 0;
border-radius: 0 4px 4px 0;
background-color: #1890ff;
border-color: #1890ff;
&:hover {
background-color: #40a9ff;
}
}
}
/* 拖拽分隔条 */
.resize-handle {
position: absolute;
top: 46px;
right: 0;
width: 8px;
bottom: 10px;
height: auto;
background-color: transparent;
// border-right: 1px solid #3988ff;
border-left: 1px solid rgba(57, 136, 255, 0.45);
cursor: col-resize;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
flex-shrink: 0;
z-index: 10;
&:hover {
background-color: transparent;
.handle-line {
background-color: #1890ff;
}
.handle-dots span {
background-color: #1890ff;
}
}
&.resizing {
background-color: transparent;
.handle-line {
background-color: #40a9ff;
width: 4px;
}
.handle-dots span {
background-color: #40a9ff;
}
}
/* 垂直指示线 */
.handle-line {
width: 2px;
height: 60px;
background-color: #3a4a5a;
border-radius: 2px;
transition: all 0.2s;
margin-bottom: 8px;
}
/* 三点指示器 */
.handle-dots {
display: flex;
flex-direction: column;
gap: 4px;
span {
width: 4px;
height: 4px;
background-color: #3a4a5a;
border-radius: 50%;
transition: background-color 0.2s;
}
}
}
/* 中间内容区域 - 保持独立布局 */
.wid {
position: absolute;
top: 0;
right: 0;
bottom: 0;
overflow: hidden;
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
height: 100%;
z-index: 1;
box-sizing: border-box;
border: 1px solid rgba(63, 127, 216, 0.72);
background: rgba(8, 30, 86, 0.72);
}
.point-layout > .wid {
right: 10px;
margin-left: 10px;
width: auto;
top: 10px;
bottom: 10px;
height: auto;
min-width: 0;
}
.form {
width: 100%;
height: 100%;
overflow-y: auto;
padding: 0 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
min-width: 0;
padding-top: 0 !important;
}
/* Reserve the overlay's footprint so the main table never renders beneath it. */
.wid.has-right-panel .form {
width: auto;
margin-right: clamp(420px, 32%, 520px);
}
.line {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-shrink: 0;
min-height: 34px;
padding-top: 3px;
box-sizing: border-box;
}
/* 右侧面板样式 - 完全独立不受影响 */
.right {
/* Keep the binding table wide enough for its columns when the tree is resized. */
width: clamp(420px, 32%, 520px);
min-width: 420px;
max-width: 52%;
overflow-x: auto;
overflow-y: hidden;
border: 2px solid #0b374e;
position: absolute;
right: 0;
top: 0;
height: 100%;
z-index: 20;
.collect {
position: absolute;
top: 40%;
left: 0;
cursor: pointer;
z-index: 30;
transform: none;
}
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
&::-webkit-scrollbar-track { background: #071b44; }
&::-webkit-scrollbar-thumb { background: #3988ff; border-radius: 4px; }
}
/* 添加重置按钮样式 */
.reset-width-btn {
display: flex;
position: absolute;
bottom: 10px;
right: 10px;
width: 24px;
height: 24px;
background-color: #1e3a4a;
border-radius: 4px;
align-items: center;
justify-content: center;
cursor: pointer;
color: #ffffff;
z-index: 10;
transition: all 0.2s;
&:hover {
background-color: #1890ff;
transform: rotate(180deg);
}
i {
font-size: 14px;
}
}
/* 确保表格在宽度变化时能正确调整 */
.el-table {
width: 100% !important;
background: transparent !important;
::v-deep .el-table__body-wrapper {
overflow-x: auto !important;
background: transparent !important;
scrollbar-width: thin;
scrollbar-color: #3988ff #071b44;
}
::v-deep .el-table__body-wrapper::-webkit-scrollbar {
width: 8px;
height: 8px;
display: block;
}
::v-deep .el-table__header-wrapper {
overflow-x: hidden !important;
}
}
/* Keep the header and body at the same minimum width. The body wrapper then
* owns the single horizontal scrollbar and Element UI synchronizes header scrollLeft. */
.custom-table {
::v-deep .el-table__header,
::v-deep .el-table__body {
min-width: 1055px !important;
}
::v-deep .el-table__body-wrapper::-webkit-scrollbar-track {
background: #071b44;
}
::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb {
background: #3988ff;
border-radius: 4px;
}
}
/* 响应式处理 */
@media screen and (max-width: 1200px) {
.resize-container {
max-width: 400px !important;
}
.right {
width: 35%;
}
}
@media screen and (max-width: 768px) {
.resize-container {
max-width: 300px !important;
}
.right {
width: 45%;
}
}
/* 右侧面板样式 */
.eq {
min-width: 400px;
padding-top: 30px;
padding-left: 10px;
box-sizing: border-box;
background-color: #111452;
color: #e0e0e0;
height: 100%;
}
.eq-img {
display: block;
margin: 0 auto;
width: 110px;
}
.eq-a1 {
text-align: center;
padding-top: 30px;
}
.eq-a1 .eq-a1-d1 {
font-size: 16px;
margin-bottom: 10px;
color: #ffffff;
}
.eq-a2 {
text-align: center;
margin-bottom: 10px;
.el-button {
display: inline-block;
background-color: #1e3a4a;
border-color: #2a4a5a;
color: #e0e0e0;
&:hover {
background-color: #2a4a5a;
border-color: #3a5a6a;
color: #ffffff;
}
&:disabled {
background-color: #1a2a3a;
border-color: #2a3a4a;
color: #808080;
}
}
}
/* 右侧表格样式 */
.right-table {
width: 100%;
min-width: 400px;
table-layout: fixed;
overflow: visible;
border-radius: 4px;
overflow: hidden;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif !important;
::v-deep .el-table__header th {
background-color: #012773 !important;
color: #ffffff !important;
border-bottom: 2px solid #0b374e !important;
font-weight: 600 !important;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif !important;
white-space: nowrap !important;
}
::v-deep .el-table__header th .cell {
white-space: nowrap !important;
word-break: keep-all;
}
::v-deep .el-table__row td {
background-color: #E0EAFF !important;
color: #000000 !important;
border-bottom: 1px solid #acb5e3 !important;
font-weight: 400 !important;
}
::v-deep .el-table__row--striped td {
background-color: #EBF1FF !important;
}
::v-deep .el-table__row td .cell {
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif !important;
font-weight: 400 !important;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 站名列强制居中(覆盖可能的对齐/宽度冲突样式) */
::v-deep .el-table th.station-name-col .cell,
::v-deep .el-table td.station-name-col .cell {
text-align: center !important;
}
::v-deep .el-table__row td.operation-column .cell,
::v-deep .el-table__fixed-right td.operation-column .cell {
overflow: visible;
text-overflow: clip;
white-space: nowrap;
}
::v-deep td.operation-column .el-button {
flex: 0 0 auto;
white-space: nowrap;
}
::v-deep td.operation-column .cell {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
gap: 6px;
}
::v-deep .el-table__fixed-right td.operation-column .cell {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
gap: 6px;
overflow: visible;
}
::v-deep .el-table__row:hover td {
background-color: #d6e4ff !important;
}
::v-deep .el-button--text {
color: #1890ff;
&:hover {
color: #40a9ff;
}
&:disabled {
color: #808080;
}
}
::v-deep .el-loading-mask {
background: rgba(34, 47, 61, 0.92) !important;
}
::v-deep .el-loading-spinner .circular {
stroke: #3988ff !important;
}
::v-deep .el-loading-spinner .el-loading-text {
color: #43c0ff !important;
}
/* Allow narrow panels to scroll horizontally instead of clipping columns. */
::v-deep .el-table__body-wrapper {
overflow-x: auto !important;
overflow-y: hidden !important;
scrollbar-width: thin;
scrollbar-color: #3988ff #071b44;
}
::v-deep .el-table__header-wrapper {
overflow-x: auto !important;
scrollbar-width: thin;
scrollbar-color: #3988ff #071b44;
}
::v-deep .el-table__header-wrapper::-webkit-scrollbar {
width: 8px;
height: 8px;
display: block;
}
::v-deep .el-table__body-wrapper::-webkit-scrollbar {
width: 8px;
height: 8px;
display: block;
}
::v-deep .el-table__body-wrapper::-webkit-scrollbar-track {
background: #071b44;
}
::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb {
background: #3988ff;
border-radius: 4px;
}
}
/* Keep the image preview stage proportional to the viewport. */
.image-preview-dialog {
::v-deep .el-dialog__body {
height: calc(100vh - 180px) !important;
min-height: 420px;
padding: 12px 18px;
box-sizing: border-box;
overflow: hidden;
}
::v-deep .flex-row {
height: 100%;
min-height: 0;
}
.image-preview-stage {
height: 100%;
min-height: 420px;
}
}
/* 右侧选项卡样式 */
.right-tabs {
::v-deep .el-tabs__item {
color: #e0e0e0;
&:hover {
color: #1890ff;
}
&.is-active {
color: #1890ff;
}
}
::v-deep .el-tabs__active-bar {
background-color: #1890ff;
}
::v-deep .el-tabs__nav-wrap::after {
background-color: #1e3a4a;
}
}
/* 弹窗样式 */
.el-dialog__body {
height: 500px;
}
.el-dialog__body .video-box-warp {
height: 390px;
}
.alert1_img1 {
width: 24px;
height: 24px;
position: relative;
top: 6px;
cursor: pointer;
}
.input_slot_a1 {
line-height: 36px;
color: #999;
margin-right: 10px;
}
.card-page .el-table td {
text-align: center !important;
}
.card-page ::v-deep .el-table,
.card-page ::v-deep .card-table {
width: 100% !important;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif !important;
background: transparent !important;
color: #000000;
border-color: transparent !important;
}
.card-page ::v-deep .custom-table.el-table::before,
.card-page ::v-deep .custom-table.el-table::after,
.card-page ::v-deep .custom-table.el-table--border::after,
.card-page ::v-deep .custom-table.el-table--group::after {
background-color: transparent !important;
}
.card-page ::v-deep .el-table__header-wrapper,
.card-page ::v-deep .el-table__fixed-header-wrapper {
background: #012773 !important;
border: 0 !important;
box-sizing: border-box;
}
.card-page ::v-deep .el-table__body-wrapper,
.card-page ::v-deep .el-table__fixed,
.card-page ::v-deep .el-table__fixed-right {
background: transparent !important;
}
.card-page ::v-deep .el-table th.el-table__cell {
background: #012773 !important;
border-top: 0 !important;
border-bottom: 0 !important;
border-left: 0 !important;
border-right: 1px solid #acb5e3 !important;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif !important;
font-weight: 700 !important;
color: #ffffff !important;
}
.card-page ::v-deep .el-table th.el-table__cell .cell {
color: #ffffff !important;
}
.card-page ::v-deep .el-table__header-wrapper th.el-table__cell:last-child,
.card-page ::v-deep .el-table__fixed-header-wrapper th.el-table__cell:last-child {
border-right: 0 !important;
}
.card-page ::v-deep .el-table__resize-proxy {
border-left: 2px solid #acb5e3 !important;
}
.card-page ::v-deep .el-table td.el-table__cell {
background: transparent !important;
border-bottom: 1px solid #c7d7f4 !important;
border-right-color: #c7d7f4 !important;
color: #000000 !important;
}
.card-page ::v-deep .el-table td.el-table__cell .cell {
color: #000000 !important;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif !important;
font-weight: 400 !important;
}
.card-page ::v-deep .el-table__row {
background: #E0EAFF !important;
}
.card-page ::v-deep .el-table__body tbody tr:nth-child(odd) td.el-table__cell {
background: #E0EAFF !important;
}
.card-page ::v-deep .el-table__body tbody tr:nth-child(even) td.el-table__cell {
background: #EBF1FF !important;
}
.card-page ::v-deep .el-table__row--striped td.el-table__cell {
background: #EBF1FF !important;
}
.card-page ::v-deep .custom-table.el-table,
.card-page ::v-deep .custom-table .el-table__body-wrapper,
.card-page ::v-deep .custom-table .el-table__fixed-body-wrapper,
.card-page ::v-deep .custom-table .el-table__body,
.card-page ::v-deep .custom-table .el-table__body tbody,
.card-page ::v-deep .custom-table .el-table__empty-block,
.card-page ::v-deep .custom-table .el-table__append-wrapper {
background: transparent !important;
background-color: transparent !important;
min-height: 0 !important;
}
.card-page ::v-deep .custom-table > .el-loading-mask {
background: rgba(34, 47, 61, 0.92) !important;
background-color: rgba(34, 47, 61, 0.92) !important;
}
.card-page ::v-deep .custom-table > .el-loading-mask .circular {
stroke: #3988ff !important;
}
.card-page ::v-deep .el-table__empty-text {
color: #5f7394 !important;
}
::v-deep .monitor-config-dialog {
height: 88vh;
max-height: 88vh;
margin-bottom: 0;
display: flex;
flex-direction: column;
}
::v-deep .monitor-config-dialog .el-dialog__body {
height: calc(88vh - 135px) !important;
flex: 1 1 auto;
overflow: auto;
padding: 24px;
box-sizing: border-box;
}
::v-deep .monitor-config-dialog,
::v-deep .monitor-config-dialog * {
scrollbar-width: none;
-ms-overflow-style: none;
}
::v-deep .monitor-config-dialog::-webkit-scrollbar,
::v-deep .monitor-config-dialog *::-webkit-scrollbar {
width: 0;
height: 0;
display: none;
}
::v-deep .monitor-config-dialog .horizontal-tabs-container {
min-height: 650px;
}
::v-deep .monitor-config-dialog .el-dialog__footer {
flex-shrink: 0;
}
::v-deep .monitor-config-dialog .el-button {
border: 1px solid #3988ff !important;
background: rgba(3, 81, 198, 0.78) !important;
color: #43c0ff !important;
}
::v-deep .monitor-config-dialog .el-button:hover,
::v-deep .monitor-config-dialog .el-button:focus {
border-color: #43c0ff !important;
background: rgba(57, 136, 255, 0.86) !important;
color: #ffffff !important;
}
::v-deep .monitor-config-dialog .el-button--text {
border-color: transparent !important;
background: transparent !important;
color: #43c0ff !important;
}
::v-deep .monitor-config-dialog .el-button--text:hover,
::v-deep .monitor-config-dialog .el-button--text:focus {
background: rgba(57, 136, 255, 0.14) !important;
color: #43c0ff !important;
}
::v-deep .monitor-config-dialog .el-button.is-disabled,
::v-deep .monitor-config-dialog .el-button.is-disabled:hover,
::v-deep .monitor-config-dialog .el-button.is-disabled:focus {
border-color: rgba(57, 136, 255, 0.35) !important;
background: rgba(3, 81, 198, 0.28) !important;
color: rgba(67, 192, 255, 0.45) !important;
}
::v-deep .monitor-config-popover,
::v-deep .monitor-config-popover * {
scrollbar-width: none;
-ms-overflow-style: none;
}
::v-deep .monitor-config-popover::-webkit-scrollbar,
::v-deep .monitor-config-popover *::-webkit-scrollbar {
width: 0;
height: 0;
display: none;
}
::v-deep .monitor-config-popover .el-button {
border: 1px solid #3988ff !important;
background: rgba(3, 81, 198, 0.78) !important;
color: #43c0ff !important;
}
::v-deep .monitor-config-popover .el-button:hover,
::v-deep .monitor-config-popover .el-button:focus {
border-color: #43c0ff !important;
background: rgba(57, 136, 255, 0.86) !important;
color: #ffffff !important;
}
.card-page ::v-deep .el-table__body tr:hover > td.el-table__cell {
background: #c5d9f6 !important;
}
.card-page ::v-deep .el-table__body tr.current-row > td.el-table__cell {
background-color: #c5d9f6 !important;
color: #000000 !important;
}
.card-page ::v-deep .el-table__body tr.current-row > td.el-table__cell .el-button.is-disabled {
color: #808080;
}
.task-toolbar-btn,
.card-page ::v-deep .el-table .task-row-btn {
border: 1px solid transparent !important;
background:
linear-gradient(#01016b, #01016b) padding-box,
linear-gradient(135deg, #3988ff, #0351c6) border-box !important;
color: #43c0ff !important;
}
.task-toolbar-btn:hover,
.task-toolbar-btn:focus,
.card-page ::v-deep .el-table .task-row-btn:hover,
.card-page ::v-deep .el-table .task-row-btn:focus {
border: 1px solid transparent !important;
background:
linear-gradient(#01016b, #01016b) padding-box,
linear-gradient(135deg, #3988ff, #0351c6) border-box !important;
color: #43c0ff !important;
opacity: 0.9;
}
.task-toolbar-btn.is-disabled,
.task-toolbar-btn.is-disabled:hover,
.task-toolbar-btn.is-disabled:focus {
border: 1px solid transparent !important;
background:
linear-gradient(#01016b, #01016b) padding-box,
linear-gradient(135deg, #3988ff, #0351c6) border-box !important;
color: #43c0ff !important;
opacity: 0.45;
}
::v-deep .vue-treeselect__control {
background: #274a58;
}
::v-deep .vue-treeselect__multi-value-item {
background: #274a58;
}
::v-deep .vue-treeselect__menu-container .vue-treeselect__menu {
background: #274a58;
}
.xg_image_viewer {
position: fixed;
bottom: 0;
left: 0;
width: 50px;
height: 50px;
}
.input_group_position {
display: flex;
width: 100%;
.input_group_item {
flex: 1;
text-align: center;
}
}
.page-content {
width: 100%;
height: 100%;
min-height: 0;
overflow: hidden;
}
::v-deep .el-table__fixed::before {
height: 0;
}
::v-deep .el-table__fixed-right::before {
height: 0;
}
::v-deep .el-tabs__item {
color: #ffffff;
}
::v-deep .el-tabs__item.is-active {
color: #1890ff;
}
::v-deep .el-input__count {
background: none;
}
.centered-input ::v-deep.el-input__inner {
text-align: center;
}
// 修改添加监控点位行的间隔
.el-form-item {
margin-bottom: 40px;
}
/* 云台控制样式 */
.r-ti1 {
line-height: 30px;
padding: 0 20px;
color: #ffffff;
}
.r-box {
// padding: 10px 5px;
// padding-bottom: 32px;
height: calc(100% - 30px); /* 让r-box占满剩余高度 */
// overflow-y: auto; /* 内容过多时滚动 */
}
.r-ti2 {
width: 170px;
position: relative;
margin: 0 auto;
}
.r-ti2 img {
width: 100%;
}
.r-ti2-pos {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.r-ti2-pos div {
width: 20px;
height: 15px;
position: absolute;
cursor: pointer;
z-index: 2;
}
.r-ti2-pos div:nth-child(1) {
top: 45px;
left: 75px;
}
.r-ti2-pos div:nth-child(2) {
top: 57px;
left: 99px;
}
.r-ti2-pos div:nth-child(3) {
top: 79px;
right: 42px;
}
.r-ti2-pos div:nth-child(4) {
bottom: 59px;
left: 99px;
}
.r-ti2-pos div:nth-child(5) {
bottom: 47px;
left: 75px;
}
.r-ti2-pos div:nth-child(6) {
bottom: 59px;
right: 99px;
}
.r-ti2-pos div:nth-child(7) {
bottom: 80px;
left: 42px;
}
.r-ti2-pos div:nth-child(8) {
top: 57px;
right: 99px;
}
.r-ti2-pos div:nth-child(9) {
width: 28px;
height: 28px;
top: 71px;
right: 72px;
}
.r-ti3 {
width: 100%;
display: flex;
padding-top: 20px;
justify-content: space-between;
}
.r-ti3 .r-ti3-d1 {
width: 34;
position: relative;
}
.r-ti3 .r-ti3-d1 img {
width: 100%;
}
.r-ti3-d2 {
position: absolute;
top: 0;
left: 0;
width: 97%;
height: 83%;
}
.r-ti3-d2 div:nth-child(1) {
width: 50%;
height: 100%;
position: absolute;
top: 0;
left: 0;
cursor: pointer;
}
.r-ti3-d2 div:nth-child(2) {
width: 50%;
height: 100%;
position: absolute;
top: 0;
right: 0;
cursor: pointer;
}
.r-ti4 {
padding-top: 5px;
}
.r-ti4 .el-select {
width: 82%;
margin-bottom: 10px;
}
.r-ti4 span {
font-size: 22px;
margin-left: 3px;
color: #999;
cursor: pointer;
}
.right .add-bth1 {
margin-top: 20px;
}
.fr .add-bth1 {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
.fl {
width: 68%;
}
.fr {
width: 30%;
margin-left: 2%;
}
.fr-ti1 {
color: #e0e0e0;
font-size: 16px;
margin-bottom: 10px;
}
.fr-ti2 {
color: #999;
font-size: 14px;
margin-bottom: 12px;
}
.fss {
font-size: 16px;
cursor: pointer;
margin-right: 10px;
color: #e0e0e0;
&:hover {
color: #ff6b6b;
}
}
.position {
width: 100%;
}
.position .position-a1 {
width: 30%;
margin-right: 10px;
}
.position .position-a1:last-child {
margin-right: 0;
}
.position-ti1 {
margin-bottom: 10px;
}
.position-a1 {
font-size: 16px;
color: #e0e0e0;
}
.position-a1 div {
margin-bottom: 10px;
}
.position-a1 div span {
color: #fa3239;
}
.el-form-item__label {
white-space: nowrap;
}
.toollist1-a1 {
width: 100%;
height: 40px;
background: rgb(68, 68, 68);
overflow: hidden;
}
.toollist1-a1 > div {
width: 60px;
height: 40px;
float: right;
position: relative;
cursor: pointer;
&:hover {
background-color: black;
}
}
.toollist1-a1 img {
max-width: 16px;
max-height: 16px;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
/* 新增筛选相关样式 - 保持原有位置 */
.search-group {
display: flex;
gap: 10px;
align-items: center;
}
.algorithm-filter,
.device-type-filter {
width: 200px;
::v-deep .el-input__inner {
background-color: #1e3a4a;
border-color: #2a4a5a;
color: #ffffff;
height: 32px;
line-height: 32px;
}
::v-deep .el-input__suffix {
color: #ffffff;
}
::v-deep .el-select-dropdown__item {
color: #ffffff;
&.selected {
color: #1890ff;
}
&:hover {
background-color: #1e3a4a;
}
}
}
.filter-stats {
color: #1890ff;
font-size: 12px;
margin-bottom: 10px;
padding-left: 5px;
.el-button--text {
color: #1890ff;
font-size: 12px;
margin-left: 10px;
&:hover {
color: #40a9ff;
}
}
}
.left-buttons {
display: flex;
gap: 8px;
flex-shrink: 0;
}
.right-filters {
display: flex;
gap: 10px;
align-items: center;
flex-shrink: 0;
}
.nowarp.is-disabled {
color: #000000 !important;
cursor: not-allowed;
}
.algorithm-setting-link {
color: #1890ff;
}
</style>
<style lang="scss">
/* The dialog is appended to body, so these sizing rules must be global. */
.image-preview-dialog {
height: 90vh;
margin-top: 5vh !important;
display: flex;
flex-direction: column;
overflow: hidden;
.el-dialog__header {
flex: 0 0 42px;
box-sizing: border-box;
}
.el-dialog__body {
flex: 1 1 auto;
min-height: 0;
height: auto !important;
padding: 12px 18px;
overflow: hidden;
}
.image-preview-stage,
.image-preview-stage > .flex-row {
width: 100%;
height: 100%;
min-height: 0;
}
}
/* 添加/修改监控点位弹窗(append-to-body):整屏水平垂直居中。
选择器带 .el-dialog 提高优先级,覆盖全局 ruoyi.scss 的 margin-top:6vh!important
与 element-ui.scss 的 transform:none/left:0/position:relative */
.el-dialog.site-point-add-dialog:not(.is-fullscreen) {
display: flex;
flex-direction: column;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin: 0 !important;
max-height: calc(100vh - 40px);
overflow: hidden;
.el-dialog__body {
max-height: calc(100vh - 160px);
overflow-y: auto;
}
}
</style>