|
|
@@ -1,35 +1,75 @@
|
|
|
<template>
|
|
|
<div class="repair-container">
|
|
|
- <el-card class="search-card">
|
|
|
- <el-form :inline="true" :model="searchForm">
|
|
|
- <el-form-item label="管网ID">
|
|
|
- <el-input v-model="searchForm.networkId" placeholder="请输入管网ID" clearable />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="维修类型">
|
|
|
- <el-select v-model="searchForm.repairType" placeholder="请选择" clearable style="width:160px">
|
|
|
- <el-option v-for="d in repairTypeDict" :key="d.dictValue" :label="d.dictLabel" :value="d.dictValue" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-button type="primary" @click="handleSearch">查询</el-button>
|
|
|
- <el-button @click="handleReset">重置</el-button>
|
|
|
- <el-button type="success" @click="handleAdd">新增</el-button>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- </el-card>
|
|
|
+ <!-- 上半部分:百度地图(左) + 图表(右) -->
|
|
|
+ <div class="top-section">
|
|
|
+ <el-card class="topo-card">
|
|
|
+ <template #header>
|
|
|
+ <div class="topo-header">
|
|
|
+ <span class="card-title">燃气管网维修记录拓扑图</span>
|
|
|
+ <div class="topo-legend">
|
|
|
+ <span class="legend-item"><i class="legend-line high"></i> 高压</span>
|
|
|
+ <span class="legend-item"><i class="legend-line medium"></i> 中压</span>
|
|
|
+ <span class="legend-item"><i class="legend-line low"></i> 低压</span>
|
|
|
+ <span class="legend-item"><i class="legend-dot repaired"></i> 有维修</span>
|
|
|
+ <span class="legend-item"><i class="legend-dot point"></i> 管点</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div class="map-wrapper">
|
|
|
+ <div id="repairBaiduMap" class="baidu-map"></div>
|
|
|
+ <div v-if="mapError" class="map-error">{{ mapError }}</div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
|
|
|
+ <div class="charts-column">
|
|
|
+ <el-card class="chart-card">
|
|
|
+ <template #header><span class="card-title">维修统计</span></template>
|
|
|
+ <div id="riskPieChart" style="height:260px"></div>
|
|
|
+ </el-card>
|
|
|
+ <el-card class="chart-card">
|
|
|
+ <template #header><span class="card-title">维修趋势</span></template>
|
|
|
+ <div id="repairTrendChart" style="height:260px"></div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 下半部分:搜索 + 表格 -->
|
|
|
<el-card class="table-card">
|
|
|
- <el-table :data="tableData" border stripe v-loading="loading">
|
|
|
- <el-table-column prop="networkId" label="管网ID" min-width="130" />
|
|
|
- <el-table-column prop="repairDate" label="维修日期" width="110" />
|
|
|
+ <div class="table-toolbar">
|
|
|
+ <div class="table-toolbar-left">
|
|
|
+ <el-form :inline="true" :model="searchForm" class="search-form">
|
|
|
+ <el-form-item label="管网ID">
|
|
|
+ <el-select v-model="searchForm.networkId" placeholder="请选择" clearable style="width:190px" @change="onFilterChange">
|
|
|
+ <el-option v-for="p in pipelineData" :key="p.id" :label="p.id + ' - ' + p.name" :value="p.id" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="维修类型">
|
|
|
+ <el-select v-model="searchForm.repairType" placeholder="请选择" clearable style="width:160px">
|
|
|
+ <el-option v-for="d in repairTypeDict" :key="d.dictValue" :label="d.dictLabel" :value="d.dictValue" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="handleSearch">查询</el-button>
|
|
|
+ <el-button @click="handleReset">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <div class="table-toolbar-right">
|
|
|
+ <el-button type="success" @click="handleAdd">新增维修记录</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-table :data="paginatedData" border stripe v-loading="loading" max-height="380" size="small">
|
|
|
+ <el-table-column prop="networkId" label="管网ID" width="100" />
|
|
|
+ <el-table-column prop="repairDate" label="维修日期" width="120" />
|
|
|
<el-table-column prop="repairType" label="维修类型" width="120">
|
|
|
<template #default="{ row }">{{ getDictLabel(repairTypeDict, row.repairType) }}</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="repairContent" label="维修内容" min-width="200" show-overflow-tooltip />
|
|
|
- <el-table-column prop="repairCompany" label="维修单位" min-width="150" />
|
|
|
- <el-table-column prop="repairCost" label="费用(元)" width="110" />
|
|
|
+ <el-table-column prop="repairContent" label="维修内容" min-width="230" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="repairCompany" label="维修单位" min-width="160" show-overflow-tooltip />
|
|
|
+ <el-table-column prop="repairCost" label="费用(元)" width="110" align="right" />
|
|
|
<el-table-column prop="repairPerson" label="负责人" width="100" />
|
|
|
- <el-table-column prop="createTime" label="创建时间" width="160" />
|
|
|
+ <el-table-column prop="createTime" label="创建时间" width="170" />
|
|
|
<el-table-column label="操作" width="150" fixed="right">
|
|
|
<template #default="{ row }">
|
|
|
<el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
|
|
|
@@ -40,38 +80,31 @@
|
|
|
<el-pagination
|
|
|
v-model:current-page="pageNum" v-model:page-size="pageSize" :total="total"
|
|
|
layout="total, prev, pager, next, sizes" :page-sizes="[10, 20, 50]"
|
|
|
- @change="loadData" style="margin-top: 16px; justify-content: flex-end"
|
|
|
+ style="margin-top:16px;justify-content:flex-end"
|
|
|
/>
|
|
|
</el-card>
|
|
|
|
|
|
+ <!-- 详情对话框 -->
|
|
|
<el-dialog :title="dialogTitle" v-model="dialogVisible" width="600px" destroy-on-close>
|
|
|
<el-form :model="formData" ref="formRef" label-width="100px">
|
|
|
<el-form-item label="管网ID" prop="networkId">
|
|
|
- <el-input v-model="formData.networkId" placeholder="请输入管网设施ID" />
|
|
|
+ <el-select v-model="formData.networkId" style="width:100%">
|
|
|
+ <el-option v-for="p in pipelineData" :key="p.id" :label="p.id + ' - ' + p.name" :value="p.id" />
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item label="维修日期" prop="repairDate">
|
|
|
- <el-date-picker v-model="formData.repairDate" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width: 100%" />
|
|
|
+ <el-date-picker v-model="formData.repairDate" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width:100%" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="维修类型" prop="repairType">
|
|
|
- <el-select v-model="formData.repairType" style="width: 100%">
|
|
|
+ <el-select v-model="formData.repairType" style="width:100%">
|
|
|
<el-option v-for="d in repairTypeDict" :key="d.dictValue" :label="d.dictLabel" :value="d.dictValue" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="维修内容">
|
|
|
- <el-input v-model="formData.repairContent" type="textarea" :rows="3" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="维修单位">
|
|
|
- <el-input v-model="formData.repairCompany" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="费用(元)">
|
|
|
- <el-input-number v-model="formData.repairCost" :precision="2" :step="100" style="width: 100%" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="负责人">
|
|
|
- <el-input v-model="formData.repairPerson" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="备注">
|
|
|
- <el-input v-model="formData.remark" type="textarea" :rows="2" />
|
|
|
- </el-form-item>
|
|
|
+ <el-form-item label="维修内容"><el-input v-model="formData.repairContent" type="textarea" :rows="3" /></el-form-item>
|
|
|
+ <el-form-item label="维修单位"><el-input v-model="formData.repairCompany" /></el-form-item>
|
|
|
+ <el-form-item label="费用(元)"><el-input-number v-model="formData.repairCost" :precision="2" :step="100" style="width:100%" /></el-form-item>
|
|
|
+ <el-form-item label="负责人"><el-input v-model="formData.repairPerson" /></el-form-item>
|
|
|
+ <el-form-item label="备注"><el-input v-model="formData.remark" type="textarea" :rows="2" /></el-form-item>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
|
@@ -82,68 +115,437 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, reactive, onMounted } from 'vue'
|
|
|
+import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
|
|
+import * as echarts from 'echarts'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
-import { getRepairRecordPage, addRepairRecord, updateRepairRecord, deleteRepairRecord } from '@/api/pipeNetwork/basic'
|
|
|
-import { getDicts } from '@/api/system/dict/data'
|
|
|
|
|
|
+// ==================== 静态数据 ====================
|
|
|
const loading = ref(false)
|
|
|
-const tableData = ref([])
|
|
|
const pageNum = ref(1)
|
|
|
const pageSize = ref(10)
|
|
|
-const total = ref(0)
|
|
|
const searchForm = reactive({ networkId: '', repairType: '' })
|
|
|
const dialogVisible = ref(false)
|
|
|
const dialogTitle = ref('新增')
|
|
|
const formRef = ref(null)
|
|
|
-const formData = reactive({
|
|
|
- repairId: '', networkId: '', repairDate: '', repairType: '',
|
|
|
- repairContent: '', repairCompany: '', repairCost: null, repairPerson: '', remark: ''
|
|
|
-})
|
|
|
+const formData = reactive({ repairId: '', networkId: '', repairDate: '', repairType: '', repairContent: '', repairCompany: '', repairCost: null, repairPerson: '', remark: '' })
|
|
|
|
|
|
-const repairTypeDict = ref([])
|
|
|
+const repairTypeDict = ref([
|
|
|
+ { dictValue: '1', dictLabel: '抢修' },
|
|
|
+ { dictValue: '2', dictLabel: '计划维修' },
|
|
|
+ { dictValue: '3', dictLabel: '防腐层修复' },
|
|
|
+ { dictValue: '4', dictLabel: '管件更换' },
|
|
|
+ { dictValue: '5', dictLabel: '焊缝修补' },
|
|
|
+ { dictValue: '6', dictLabel: '阀门维修' }
|
|
|
+])
|
|
|
function getDictLabel(dict, value) {
|
|
|
const item = dict.find(d => d.dictValue === String(value))
|
|
|
return item ? item.dictLabel : value
|
|
|
}
|
|
|
|
|
|
-async function loadDicts() {
|
|
|
- const res = await getDicts('pipe_repair_type')
|
|
|
- repairTypeDict.value = res.data
|
|
|
+// ==================== 百度地图配置 ====================
|
|
|
+const centerPoint = { lng: 110.3937, lat: 28.4640 }
|
|
|
+const MAP_BOUNDS = { southWest: { lng: 110.365, lat: 28.452 }, northEast: { lng: 110.420, lat: 28.482 } }
|
|
|
+const MAP_MIN_ZOOM = 13
|
|
|
+const MAP_MAX_ZOOM = 19
|
|
|
+const MAP_DEFAULT_ZOOM = 15
|
|
|
+
|
|
|
+const mapError = ref('')
|
|
|
+let mapInstance = null
|
|
|
+let activeInfoWindow = null
|
|
|
+let overlayClicked = false
|
|
|
+let mapOverlays = []
|
|
|
+
|
|
|
+// ==================== 管网数据 (沅陵县真实坐标) ====================
|
|
|
+const pipelineData = [
|
|
|
+ // 高压管线 — 辰州北路/辰州中路 (拆分为5段 net001-net003)
|
|
|
+ { id: 'net001', name: '辰州北路高压段', pressure: 'high',
|
|
|
+ points: [{lng:110.3918,lat:28.4793},{lng:110.3920,lat:28.4765},{lng:110.3924,lat:28.4743},{lng:110.3927,lat:28.4721},{lng:110.3931,lat:28.4698}] },
|
|
|
+ { id: 'net002', name: '辰州中路高压A段', pressure: 'high',
|
|
|
+ points: [{lng:110.3931,lat:28.4698},{lng:110.3934,lat:28.4675},{lng:110.3936,lat:28.4652},{lng:110.3937,lat:28.4630}] },
|
|
|
+ { id: 'net003', name: '辰州中路高压B段', pressure: 'high',
|
|
|
+ points: [{lng:110.3937,lat:28.4630},{lng:110.3935,lat:28.4607},{lng:110.3934,lat:28.4585},{lng:110.3935,lat:28.4576}] },
|
|
|
+ // 次高压/中压 — 古城路 (拆分为4段 net004-net007)
|
|
|
+ { id: 'net004', name: '古城北路中压段', pressure: 'medium',
|
|
|
+ points: [{lng:110.3980,lat:28.4790},{lng:110.3979,lat:28.4765},{lng:110.3977,lat:28.4741},{lng:110.3975,lat:28.4717}] },
|
|
|
+ { id: 'net005', name: '古城中路中压A段', pressure: 'medium',
|
|
|
+ points: [{lng:110.3975,lat:28.4717},{lng:110.3973,lat:28.4693},{lng:110.3970,lat:28.4669}] },
|
|
|
+ { id: 'net006', name: '古城中路中压B段', pressure: 'medium',
|
|
|
+ points: [{lng:110.3970,lat:28.4669},{lng:110.3968,lat:28.4645},{lng:110.3965,lat:28.4622}] },
|
|
|
+ { id: 'net007', name: '古城南路中压段', pressure: 'medium',
|
|
|
+ points: [{lng:110.3965,lat:28.4622},{lng:110.3963,lat:28.4598},{lng:110.3962,lat:28.4575}] },
|
|
|
+ // 中压 — 迎宾路 (拆分为3段 net008-net010)
|
|
|
+ { id: 'net008', name: '迎宾西路中压段', pressure: 'medium',
|
|
|
+ points: [{lng:110.3732,lat:28.4650},{lng:110.3778,lat:28.4648},{lng:110.3824,lat:28.4645},{lng:110.3870,lat:28.4642}] },
|
|
|
+ { id: 'net009', name: '县政府迎宾路中压段', pressure: 'medium',
|
|
|
+ points: [{lng:110.3870,lat:28.4642},{lng:110.3916,lat:28.4640},{lng:110.3962,lat:28.4637}] },
|
|
|
+ { id: 'net010', name: '迎宾东路中压段', pressure: 'medium',
|
|
|
+ points: [{lng:110.3962,lat:28.4637},{lng:110.4008,lat:28.4634},{lng:110.4054,lat:28.4630},{lng:110.4098,lat:28.4627},{lng:110.4120,lat:28.4625}] },
|
|
|
+ // 中压 — 建设路 (拆分为3段 net011-net013)
|
|
|
+ { id: 'net011', name: '建设西路中压段', pressure: 'medium',
|
|
|
+ points: [{lng:110.3748,lat:28.4583},{lng:110.3794,lat:28.4586},{lng:110.3840,lat:28.4588},{lng:110.3886,lat:28.4589}] },
|
|
|
+ { id: 'net012', name: '建设路中段', pressure: 'medium',
|
|
|
+ points: [{lng:110.3886,lat:28.4589},{lng:110.3912,lat:28.4586},{lng:110.3937,lat:28.4582},{lng:110.3966,lat:28.4579}] },
|
|
|
+ { id: 'net013', name: '建设东路中压段', pressure: 'medium',
|
|
|
+ points: [{lng:110.3966,lat:28.4579},{lng:110.4002,lat:28.4581},{lng:110.4038,lat:28.4578},{lng:110.4076,lat:28.4575}] },
|
|
|
+ // 低压 — 滨江路 (拆分为2段 net014-net015)
|
|
|
+ { id: 'net014', name: '滨江路西低压段', pressure: 'low',
|
|
|
+ points: [{lng:110.3762,lat:28.4569},{lng:110.3804,lat:28.4570},{lng:110.3846,lat:28.4571},{lng:110.3882,lat:28.4568},{lng:110.3918,lat:28.4565}] },
|
|
|
+ { id: 'net015', name: '滨江路东低压段', pressure: 'low',
|
|
|
+ points: [{lng:110.3918,lat:28.4565},{lng:110.3954,lat:28.4562},{lng:110.3990,lat:28.4560},{lng:110.4028,lat:28.4559},{lng:110.4066,lat:28.4557},{lng:110.4092,lat:28.4556}] },
|
|
|
+ // 低压 — 天宁路 (拆分为2段 net016-net017)
|
|
|
+ { id: 'net016', name: '天宁西路低压段', pressure: 'low',
|
|
|
+ points: [{lng:110.3828,lat:28.4704},{lng:110.3870,lat:28.4702},{lng:110.3912,lat:28.4700},{lng:110.3954,lat:28.4697}] },
|
|
|
+ { id: 'net017', name: '天宁东路低压段', pressure: 'low',
|
|
|
+ points: [{lng:110.3954,lat:28.4697},{lng:110.3996,lat:28.4695},{lng:110.4038,lat:28.4693},{lng:110.4078,lat:28.4691},{lng:110.4092,lat:28.4690}] },
|
|
|
+]
|
|
|
+
|
|
|
+// 管点数据 (管网节点位置)
|
|
|
+const pipePointData = [
|
|
|
+ { name: '辰州北路阀门', lng: 110.3924, lat: 28.4743, pipelineId: 'net001' },
|
|
|
+ { name: '辰州中路阀门', lng: 110.3936, lat: 28.4652, pipelineId: 'net002' },
|
|
|
+ { name: '辰州路调压站', lng: 110.3934, lat: 28.4585, pipelineId: 'net003' },
|
|
|
+ { name: '古城北路阀门', lng: 110.3977, lat: 28.4741, pipelineId: 'net004' },
|
|
|
+ { name: '古城路调压站', lng: 110.3963, lat: 28.4598, pipelineId: 'net007' },
|
|
|
+ { name: '迎宾西路站', lng: 110.3824, lat: 28.4645, pipelineId: 'net008' },
|
|
|
+ { name: '迎宾东路阀门', lng: 110.4054, lat: 28.4630, pipelineId: 'net010' },
|
|
|
+ { name: '建设路点', lng: 110.3840, lat: 28.4588, pipelineId: 'net011' },
|
|
|
+ { name: '县政府终端', lng: 110.3937, lat: 28.4582, pipelineId: 'net012' },
|
|
|
+ { name: '建设东路点', lng: 110.4038, lat: 28.4578, pipelineId: 'net013' },
|
|
|
+ { name: '天宁东路终端', lng: 110.4092, lat: 28.4690, pipelineId: 'net017' },
|
|
|
+]
|
|
|
+
|
|
|
+// ==================== 维修记录 ====================
|
|
|
+const tableData = ref([
|
|
|
+ { repairId: '1', networkId: 'net001', repairDate: '2026-06-10', repairType: '1', repairContent: '辰州北路高压段焊缝渗漏,管道焊接接头处出现裂纹,停气抢修并更换受损管段1.2m', repairCompany: '沅陵燃气工程有限公司', repairCost: 12500, repairPerson: '张建国', createTime: '2026-06-10 14:30:22', remark: '夜间施工,影响200户居民用气3小时' },
|
|
|
+ { repairId: '2', networkId: 'net005', repairDate: '2026-06-05', repairType: '3', repairContent: '古城中路中压A段防腐层老化脱落,管体局部锈蚀深度1.8mm,防腐层整体修复', repairCompany: '湖南城市燃气技术公司', repairCost: 38600, repairPerson: '李志强', createTime: '2026-06-05 09:15:40', remark: '修复长度约85m' },
|
|
|
+ { repairId: '3', networkId: 'net008', repairDate: '2026-05-28', repairType: '4', repairContent: '迎宾西路中压段三通管件更换,原管件因地基沉降导致应力开裂,更换为加强型管件', repairCompany: '怀化兴燃工程有限公司', repairCost: 8200, repairPerson: '王立新', createTime: '2026-05-28 16:45:10', remark: '' },
|
|
|
+ { repairId: '4', networkId: 'net012', repairDate: '2026-05-20', repairType: '1', repairContent: '建设路中段第三方施工破坏,DN110 PE管被挖掘机铲断,紧急停气抢修', repairCompany: '沅陵燃气工程有限公司', repairCost: 15000, repairPerson: '赵明辉', createTime: '2026-05-20 03:22:55', remark: '第三方施工未报备' },
|
|
|
+ { repairId: '5', networkId: 'net003', repairDate: '2026-05-15', repairType: '2', repairContent: '辰州中路高压B段计划停气检修,更换老化阀门2台,管道内检测并清管', repairCompany: '湖南省特种设备检测院', repairCost: 52000, repairPerson: '刘伟东', createTime: '2026-05-15 08:00:00', remark: '年度计划检修,提前72小时通知用户' },
|
|
|
+ { repairId: '6', networkId: 'net009', repairDate: '2026-05-08', repairType: '5', repairContent: '县政府迎宾路中压段环焊缝异常,超声检测发现焊缝内部缺陷,焊缝重新焊接', repairCompany: '湖南城市燃气技术公司', repairCost: 18600, repairPerson: '李志强', createTime: '2026-05-08 11:20:18', remark: '焊缝缺陷深度2.3mm' },
|
|
|
+ { repairId: '7', networkId: 'net015', repairDate: '2026-04-30', repairType: '6', repairContent: '滨江路东低压段阀门操作卡涩,阀杆填料泄漏,拆解阀门更换填料及O型密封圈', repairCompany: '怀化兴燃工程有限公司', repairCost: 4500, repairPerson: '王立新', createTime: '2026-04-30 14:10:33', remark: '' },
|
|
|
+ { repairId: '8', networkId: 'net011', repairDate: '2026-04-22', repairType: '3', repairContent: '建设西路中压段管道外防腐层破损,杂散电流干扰导致电化学腐蚀,修复防腐层并加装牺牲阳极', repairCompany: '湖南城市燃气技术公司', repairCost: 28500, repairPerson: '李志强', createTime: '2026-04-22 10:05:00', remark: '腐蚀深度1.5mm' },
|
|
|
+ { repairId: '9', networkId: 'net002', repairDate: '2026-04-15', repairType: '1', repairContent: '辰州中路高压A段管道泄漏报警,法兰连接处密封垫老化泄漏,紧急更换金属缠绕垫', repairCompany: '沅陵燃气工程有限公司', repairCost: 6300, repairPerson: '张建国', createTime: '2026-04-15 21:08:42', remark: '泄漏量约0.3m³' },
|
|
|
+ { repairId: '10', networkId: 'net006', repairDate: '2026-04-08', repairType: '2', repairContent: '古城中路中压B段年度维护,全线管道壁厚检测,阴极保护系统检测,更换测试桩3个', repairCompany: '湖南省特种设备检测院', repairCost: 22000, repairPerson: '刘伟东', createTime: '2026-04-08 09:00:00', remark: '壁厚检测结果符合GB50028' },
|
|
|
+ { repairId: '11', networkId: 'net014', repairDate: '2026-03-28', repairType: '4', repairContent: '滨江路西低压段PE管老化更换,原管道使用年限已到设计寿命,整体更换DN160 PE100新管', repairCompany: '沅陵燃气工程有限公司', repairCost: 78500, repairPerson: '赵明辉', createTime: '2026-03-28 07:30:00', remark: '更换长度约210m' },
|
|
|
+ { repairId: '12', networkId: 'net004', repairDate: '2026-03-18', repairType: '6', repairContent: '古城北路中压段阀门井内积水导致阀门锈蚀,清理井室并更换锈蚀螺栓及附件', repairCompany: '怀化兴燃工程有限公司', repairCost: 3500, repairPerson: '王立新', createTime: '2026-03-18 13:25:10', remark: '' },
|
|
|
+ { repairId: '13', networkId: 'net010', repairDate: '2026-03-10', repairType: '5', repairContent: '迎宾东路中压段直缝焊管焊缝补强,焊缝余高不符合规范,打磨处理后进行补焊', repairCompany: '湖南城市燃气技术公司', repairCost: 9800, repairPerson: '李志强', createTime: '2026-03-10 15:40:05', remark: '焊缝长度28mm' },
|
|
|
+ { repairId: '14', networkId: 'net016', repairDate: '2026-02-25', repairType: '3', repairContent: '天宁西路低压段沿河侧管道外防腐保温层修复,河水浸泡导致保温层脱落约40m', repairCompany: '沅陵燃气工程有限公司', repairCost: 33500, repairPerson: '张建国', createTime: '2026-02-25 10:20:30', remark: '枯水期施工' },
|
|
|
+ { repairId: '15', networkId: 'net007', repairDate: '2026-02-18', repairType: '2', repairContent: '古城南路中压段穿越路口管段套管更换,原钢套管腐蚀穿孔,换用加强级3PE防腐套管', repairCompany: '湖南省特种设备检测院', repairCost: 16500, repairPerson: '刘伟东', createTime: '2026-02-18 08:50:15', remark: '' },
|
|
|
+ { repairId: '16', networkId: 'net017', repairDate: '2026-02-08', repairType: '4', repairContent: '天宁东路低压段凝水缸更换,冬季冻裂导致凝水缸壳体破损,更换为抗冻型凝水缸', repairCompany: '怀化兴燃工程有限公司', repairCost: 7200, repairPerson: '王立新', createTime: '2026-02-08 11:15:00', remark: '' },
|
|
|
+ { repairId: '17', networkId: 'net013', repairDate: '2026-01-22', repairType: '1', repairContent: '建设东路中压段地基下沉导致管道变形,开挖后重新敷设管基并加装波纹补偿器', repairCompany: '沅陵燃气工程有限公司', repairCost: 25600, repairPerson: '赵明辉', createTime: '2026-01-22 16:35:48', remark: '沉降量约80mm' },
|
|
|
+ { repairId: '18', networkId: 'net001', repairDate: '2026-01-10', repairType: '2', repairContent: '辰州北路高压段阴极保护系统维护,更换阳极地床,测试保护电位', repairCompany: '湖南省特种设备检测院', repairCost: 11200, repairPerson: '刘伟东', createTime: '2026-01-10 09:30:00', remark: '保护电位-1.15V~-1.22V' },
|
|
|
+])
|
|
|
+
|
|
|
+const pressureColors = { high: '#e63946', medium: '#f77f00', low: '#2a9d8f' }
|
|
|
+const repairedNetworkIds = computed(() => { const s = new Set(); tableData.value.forEach(r => s.add(r.networkId)); return s })
|
|
|
+
|
|
|
+function getPipelineName(id) { const p = pipelineData.find(p => p.id === id); return p ? p.name : id }
|
|
|
+
|
|
|
+// ==================== 过滤分页 ====================
|
|
|
+const filteredData = computed(() => {
|
|
|
+ let data = tableData.value
|
|
|
+ if (searchForm.networkId) data = data.filter(r => r.networkId === searchForm.networkId)
|
|
|
+ if (searchForm.repairType) data = data.filter(r => r.repairType === searchForm.repairType)
|
|
|
+ return data.sort((a, b) => b.createTime.localeCompare(a.createTime))
|
|
|
+})
|
|
|
+const paginatedData = computed(() => filteredData.value.slice((pageNum.value - 1) * pageSize.value, pageNum.value * pageSize.value))
|
|
|
+const total = computed(() => filteredData.value.length)
|
|
|
+
|
|
|
+// ==================== 百度地图 ====================
|
|
|
+function waitForBMapGL() {
|
|
|
+ if (typeof BMapGL !== 'undefined') { initMap(); return }
|
|
|
+ let attempts = 0
|
|
|
+ const timer = setInterval(() => {
|
|
|
+ attempts++
|
|
|
+ if (typeof BMapGL !== 'undefined') { clearInterval(timer); initMap(); return }
|
|
|
+ if (attempts >= 50) { clearInterval(timer); mapError.value = '百度地图脚本加载超时,请刷新页面重试。' }
|
|
|
+ }, 200)
|
|
|
+}
|
|
|
+
|
|
|
+function closeInfoWindow() {
|
|
|
+ if (activeInfoWindow) { try { activeInfoWindow.close(); mapInstance.removeOverlay(activeInfoWindow) } catch (_) {}; activeInfoWindow = null }
|
|
|
}
|
|
|
|
|
|
-async function loadData() {
|
|
|
- loading.value = true
|
|
|
+function getMidPoint(points) {
|
|
|
+ let lng = 0, lat = 0
|
|
|
+ points.forEach(p => { lng += p.lng; lat += p.lat })
|
|
|
+ return { lng: lng / points.length, lat: lat / points.length }
|
|
|
+}
|
|
|
+
|
|
|
+function initMap() {
|
|
|
+ const el = document.getElementById('repairBaiduMap')
|
|
|
+ if (!el) { mapError.value = '地图容器未找到。'; return }
|
|
|
+
|
|
|
+ try { mapInstance = new BMapGL.Map('repairBaiduMap', { enableMapClick: true }) } catch (e) {
|
|
|
+ console.error('创建地图实例失败:', e); mapError.value = '百度地图初始化失败,请检查AK和网络连接。'; return
|
|
|
+ }
|
|
|
+
|
|
|
+ try { mapInstance.centerAndZoom(new BMapGL.Point(centerPoint.lng, centerPoint.lat), MAP_DEFAULT_ZOOM); mapInstance.enableScrollWheelZoom(true) } catch (e) { console.error('设置中心失败:', e) }
|
|
|
+
|
|
|
+ try { if (typeof mapInstance.setMinZoom === 'function') mapInstance.setMinZoom(MAP_MIN_ZOOM); if (typeof mapInstance.setMaxZoom === 'function') mapInstance.setMaxZoom(MAP_MAX_ZOOM) } catch (e) {}
|
|
|
+
|
|
|
try {
|
|
|
- const res = await getRepairRecordPage(pageNum.value, pageSize.value, searchForm)
|
|
|
- tableData.value = res.data.records || []
|
|
|
- total.value = res.data.total || 0
|
|
|
- } catch (e) { console.error(e) } finally { loading.value = false }
|
|
|
+ if (typeof mapInstance.setMaxBounds === 'function') {
|
|
|
+ const sw = new BMapGL.Point(MAP_BOUNDS.southWest.lng, MAP_BOUNDS.southWest.lat)
|
|
|
+ const ne = new BMapGL.Point(MAP_BOUNDS.northEast.lng, MAP_BOUNDS.northEast.lat)
|
|
|
+ mapInstance.setMaxBounds(new BMapGL.Bounds(sw, ne))
|
|
|
+ }
|
|
|
+ } catch (e) {}
|
|
|
+
|
|
|
+ mapInstance.addEventListener('click', () => {
|
|
|
+ if (overlayClicked) { overlayClicked = false; return }
|
|
|
+ closeInfoWindow()
|
|
|
+ })
|
|
|
+
|
|
|
+ try { renderMapScene() } catch (e) { console.error('渲染地图失败:', e); mapError.value = '地图要素渲染失败。' }
|
|
|
+}
|
|
|
+
|
|
|
+function renderMapScene() {
|
|
|
+ if (!mapInstance || typeof BMapGL === 'undefined') return
|
|
|
+ closeInfoWindow()
|
|
|
+ try { mapInstance.clearOverlays() } catch (_) {}
|
|
|
+ mapOverlays = []
|
|
|
+
|
|
|
+ // 绘制管线
|
|
|
+ pipelineData.forEach(pipe => {
|
|
|
+ try {
|
|
|
+ const hasRepair = repairedNetworkIds.value.has(pipe.id)
|
|
|
+ const pts = pipe.points.map(p => new BMapGL.Point(p.lng, p.lat))
|
|
|
+ const polyline = new BMapGL.Polyline(pts, {
|
|
|
+ strokeColor: hasRepair ? '#f59e0b' : pressureColors[pipe.pressure],
|
|
|
+ strokeWeight: hasRepair ? 5 : 3.5,
|
|
|
+ strokeOpacity: 0.9
|
|
|
+ })
|
|
|
+ mapInstance.addOverlay(polyline)
|
|
|
+ mapOverlays.push({ overlay: polyline, pipe })
|
|
|
+
|
|
|
+ polyline.addEventListener('click', (e) => {
|
|
|
+ overlayClicked = true
|
|
|
+ if (e && e.domEvent) e.domEvent.stopPropagation()
|
|
|
+ closeInfoWindow()
|
|
|
+ const mid = getMidPoint(pipe.points)
|
|
|
+ const count = tableData.value.filter(r => r.networkId === pipe.id).length
|
|
|
+ const repairList = tableData.value.filter(r => r.networkId === pipe.id).slice(0, 3).map(r => `${r.repairDate} ${getDictLabel(repairTypeDict, r.repairType)}`).join('<br>')
|
|
|
+ const html = `<div style="font-size:13px;max-width:300px;"><b>${pipe.id}</b> ${pipe.name}<br>
|
|
|
+ <span style="color:${pressureColors[pipe.pressure]};font-weight:bold;">${pipe.pressure === 'high' ? '高压' : pipe.pressure === 'medium' ? '中压' : '低压'}</span>
|
|
|
+ 累计维修 <b style="color:#e63946;">${count}</b> 次
|
|
|
+ ${count > 0 ? '<br><span style="color:#909399;font-size:11px;">' + repairList + '</span>' : ''}</div>`
|
|
|
+ activeInfoWindow = new BMapGL.InfoWindow(html, { width: 260 })
|
|
|
+ mapInstance.openInfoWindow(activeInfoWindow, new BMapGL.Point(mid.lng, mid.lat))
|
|
|
+ // 筛选表格
|
|
|
+ searchForm.networkId = pipe.id
|
|
|
+ pageNum.value = 1
|
|
|
+ })
|
|
|
+ } catch (_) {}
|
|
|
+ })
|
|
|
+
|
|
|
+ // 绘制管点
|
|
|
+ pipePointData.forEach(point => {
|
|
|
+ try {
|
|
|
+ const hasRepair = repairedNetworkIds.value.has(point.pipelineId)
|
|
|
+ const pt = new BMapGL.Point(point.lng, point.lat)
|
|
|
+ const color = hasRepair ? '#f59e0b' : '#3b82f6'
|
|
|
+ const size = hasRepair ? 10 : 7
|
|
|
+ const label = new BMapGL.Label(point.name, {
|
|
|
+ position: pt,
|
|
|
+ offset: new BMapGL.Size(-22, -22)
|
|
|
+ })
|
|
|
+ label.setStyle({
|
|
|
+ color: '#fff', fontSize: '10px', padding: '2px 6px',
|
|
|
+ backgroundColor: color, borderRadius: '3px', whiteSpace: 'nowrap',
|
|
|
+ border: hasRepair ? '2px solid #fff' : 'none', boxShadow: hasRepair ? '0 0 6px rgba(245,158,11,0.6)' : 'none'
|
|
|
+ })
|
|
|
+ mapInstance.addOverlay(label)
|
|
|
+ mapOverlays.push({ overlay: label, point })
|
|
|
+
|
|
|
+ label.addEventListener('click', (e) => {
|
|
|
+ overlayClicked = true
|
|
|
+ if (e && e.domEvent) e.domEvent.stopPropagation()
|
|
|
+ closeInfoWindow()
|
|
|
+ const pipeName = getPipelineName(point.pipelineId)
|
|
|
+ const count = tableData.value.filter(r => r.networkId === point.pipelineId).length
|
|
|
+ const html = `<div style="font-size:13px;"><b>${point.name}</b><br>所属管线: ${point.pipelineId} ${pipeName}<br>维修记录: <b style="color:#e63946;">${count}</b> 次</div>`
|
|
|
+ activeInfoWindow = new BMapGL.InfoWindow(html, { width: 240 })
|
|
|
+ mapInstance.openInfoWindow(activeInfoWindow, pt)
|
|
|
+ searchForm.networkId = point.pipelineId
|
|
|
+ pageNum.value = 1
|
|
|
+ })
|
|
|
+ } catch (_) {}
|
|
|
+ })
|
|
|
+
|
|
|
+ // 维修标记点 (有维修记录的管线中点位置)
|
|
|
+ pipelineData.forEach(pipe => {
|
|
|
+ if (!repairedNetworkIds.value.has(pipe.id)) return
|
|
|
+ try {
|
|
|
+ const mid = getMidPoint(pipe.points)
|
|
|
+ const pt = new BMapGL.Point(mid.lng, mid.lat)
|
|
|
+ const count = tableData.value.filter(r => r.networkId === pipe.id).length
|
|
|
+ const marker = new BMapGL.Label(String(count), {
|
|
|
+ position: pt,
|
|
|
+ offset: new BMapGL.Size(-12, -12)
|
|
|
+ })
|
|
|
+ marker.setStyle({
|
|
|
+ color: '#fff', fontSize: '11px', fontWeight: 'bold',
|
|
|
+ backgroundColor: '#e63946', borderRadius: '12px',
|
|
|
+ padding: '2px 7px', border: '2px solid #fff',
|
|
|
+ boxShadow: '0 0 8px rgba(230,57,70,0.5)'
|
|
|
+ })
|
|
|
+ mapInstance.addOverlay(marker)
|
|
|
+ mapOverlays.push({ overlay: marker, pipe })
|
|
|
+ } catch (_) {}
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// ==================== ECharts ====================
|
|
|
+let riskPieChart = null
|
|
|
+let repairTrendChart = null
|
|
|
+
|
|
|
+function initRiskPie() {
|
|
|
+ const dom = document.getElementById('riskPieChart')
|
|
|
+ if (!dom) return
|
|
|
+ if (riskPieChart) riskPieChart.dispose()
|
|
|
+ riskPieChart = echarts.init(dom)
|
|
|
+ riskPieChart.setOption({
|
|
|
+ tooltip: { trigger: 'item', formatter: '{b}: {c} 次 ({d}%)' },
|
|
|
+ legend: { orient: 'vertical', right: 6, top: 'center', itemWidth: 10, itemHeight: 10, textStyle: { color: '#606a78', fontSize: 11 }, itemGap: 14 },
|
|
|
+ series: [{
|
|
|
+ type: 'pie', radius: ['50%', '76%'], center: ['38%', '50%'],
|
|
|
+ avoidLabelOverlap: false, itemStyle: { borderRadius: 6, borderColor: '#fff', borderWidth: 3 },
|
|
|
+ label: { show: false }, emphasis: { scale: true, scaleSize: 6 },
|
|
|
+ data: [
|
|
|
+ { value: 5, name: '抢修', itemStyle: { color: '#e63946' } },
|
|
|
+ { value: 5, name: '计划维修', itemStyle: { color: '#457b9d' } },
|
|
|
+ { value: 4, name: '防腐层修复', itemStyle: { color: '#f77f00' } },
|
|
|
+ { value: 4, name: '管件更换', itemStyle: { color: '#2a9d8f' } },
|
|
|
+ { value: 2, name: '焊缝修补', itemStyle: { color: '#6c757d' } },
|
|
|
+ { value: 2, name: '阀门维修', itemStyle: { color: '#b08968' } }
|
|
|
+ ]
|
|
|
+ }]
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function initRepairTrend() {
|
|
|
+ const dom = document.getElementById('repairTrendChart')
|
|
|
+ if (!dom) return
|
|
|
+ if (repairTrendChart) repairTrendChart.dispose()
|
|
|
+ repairTrendChart = echarts.init(dom)
|
|
|
+ const months = ['1月', '2月', '3月', '4月', '5月', '6月']
|
|
|
+ repairTrendChart.setOption({
|
|
|
+ tooltip: { trigger: 'axis' },
|
|
|
+ legend: { top: 0, textStyle: { color: '#606a78', fontSize: 11 } },
|
|
|
+ grid: { left: 44, right: 20, top: 36, bottom: 32 },
|
|
|
+ xAxis: { type: 'category', data: months, boundaryGap: false, axisLabel: { color: '#9098a6', fontSize: 11 }, axisLine: { lineStyle: { color: '#d0d5dd' } } },
|
|
|
+ yAxis: { type: 'value', name: '次数', minInterval: 1, axisLabel: { color: '#9098a6', fontSize: 11 }, splitLine: { lineStyle: { color: '#f0f2f6', type: 'dashed' } } },
|
|
|
+ series: [
|
|
|
+ { name: '高压', type: 'line', data: [2, 0, 0, 1, 1, 2], smooth: true, symbol: 'circle', symbolSize: 7, lineStyle: { width: 3, color: '#e63946' }, itemStyle: { color: '#e63946', borderColor: '#fff', borderWidth: 2 }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(230,57,70,0.12)' }, { offset: 1, color: 'rgba(230,57,70,0)' }]) } },
|
|
|
+ { name: '中压', type: 'line', data: [2, 1, 1, 3, 2, 1], smooth: true, symbol: 'circle', symbolSize: 7, lineStyle: { width: 3, color: '#f77f00' }, itemStyle: { color: '#f77f00', borderColor: '#fff', borderWidth: 2 }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(247,127,0,0.12)' }, { offset: 1, color: 'rgba(247,127,0,0)' }]) } },
|
|
|
+ { name: '低压', type: 'line', data: [1, 2, 1, 2, 1, 2], smooth: true, symbol: 'circle', symbolSize: 7, lineStyle: { width: 3, color: '#2a9d8f' }, itemStyle: { color: '#2a9d8f', borderColor: '#fff', borderWidth: 2 }, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: 'rgba(42,157,143,0.12)' }, { offset: 1, color: 'rgba(42,157,143,0)' }]) } }
|
|
|
+ ]
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function initCharts() { try { initRiskPie() } catch (e) { console.warn('饼图失败:', e) }; try { initRepairTrend() } catch (e) { console.warn('趋势图失败:', e) } }
|
|
|
+function resizeCharts() { riskPieChart?.resize(); repairTrendChart?.resize() }
|
|
|
+
|
|
|
+// ==================== 操作 ====================
|
|
|
+function onFilterChange() { pageNum.value = 1 }
|
|
|
+function handleSearch() { pageNum.value = 1 }
|
|
|
+function handleReset() {
|
|
|
+ searchForm.networkId = ''; searchForm.repairType = ''; pageNum.value = 1
|
|
|
+ nextTick(() => renderMapScene())
|
|
|
}
|
|
|
-function handleSearch() { pageNum.value = 1; loadData() }
|
|
|
-function handleReset() { Object.assign(searchForm, { networkId: '', repairType: '' }); handleSearch() }
|
|
|
function handleAdd() {
|
|
|
- dialogTitle.value = '新增'
|
|
|
+ dialogTitle.value = '新增'; dialogVisible.value = true
|
|
|
Object.assign(formData, { repairId: '', networkId: '', repairDate: '', repairType: '', repairContent: '', repairCompany: '', repairCost: null, repairPerson: '', remark: '' })
|
|
|
- dialogVisible.value = true
|
|
|
}
|
|
|
-function handleEdit(row) { dialogTitle.value = '编辑'; Object.assign(formData, row); dialogVisible.value = true }
|
|
|
-async function handleSubmit() {
|
|
|
- try {
|
|
|
- if (formData.repairId) { await updateRepairRecord(formData); ElMessage.success('修改成功') }
|
|
|
- else { await addRepairRecord(formData); ElMessage.success('新增成功') }
|
|
|
- dialogVisible.value = false; loadData()
|
|
|
- } catch (e) { console.error(e) }
|
|
|
+function handleEdit(row) { dialogTitle.value = '编辑'; dialogVisible.value = true; Object.assign(formData, { ...row }) }
|
|
|
+function handleSubmit() {
|
|
|
+ if (formData.repairId) {
|
|
|
+ const idx = tableData.value.findIndex(r => r.repairId === formData.repairId)
|
|
|
+ if (idx >= 0) tableData.value[idx] = { ...formData }
|
|
|
+ ElMessage.success('修改成功')
|
|
|
+ } else {
|
|
|
+ formData.repairId = String(Date.now())
|
|
|
+ formData.createTime = new Date().toLocaleString('zh-CN').replace(/\//g, '-')
|
|
|
+ tableData.value.unshift({ ...formData })
|
|
|
+ ElMessage.success('新增成功')
|
|
|
+ }
|
|
|
+ dialogVisible.value = false
|
|
|
+ nextTick(() => { renderMapScene(); initCharts() })
|
|
|
}
|
|
|
function handleDelete(row) {
|
|
|
- ElMessageBox.confirm('确认删除该维修记录?', '提示', { type: 'warning' }).then(async () => {
|
|
|
- await deleteRepairRecord(row.repairId); ElMessage.success('删除成功'); loadData()
|
|
|
+ ElMessageBox.confirm('确认删除该维修记录?', '提示', { type: 'warning' }).then(() => {
|
|
|
+ tableData.value = tableData.value.filter(r => r.repairId !== row.repairId)
|
|
|
+ ElMessage.success('删除成功')
|
|
|
+ nextTick(() => { renderMapScene(); initCharts() })
|
|
|
}).catch(() => {})
|
|
|
}
|
|
|
-onMounted(async () => { await loadDicts(); loadData() })
|
|
|
+
|
|
|
+// ==================== 生命周期 ====================
|
|
|
+let resizeTimer = null
|
|
|
+onMounted(async () => {
|
|
|
+ await nextTick()
|
|
|
+ waitForBMapGL()
|
|
|
+ initCharts()
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
+ clearTimeout(resizeTimer)
|
|
|
+ resizeTimer = setTimeout(() => {
|
|
|
+ resizeCharts()
|
|
|
+ if (mapInstance) {
|
|
|
+ try { mapInstance.resize() } catch (_) {}
|
|
|
+ try { mapInstance.setViewport([new BMapGL.Point(centerPoint.lng, centerPoint.lat)], { zoom: mapInstance.getZoom() }) } catch (_) {}
|
|
|
+ }
|
|
|
+ }, 200)
|
|
|
+ })
|
|
|
+})
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ window.removeEventListener('resize', resizeCharts)
|
|
|
+ riskPieChart?.dispose(); repairTrendChart?.dispose()
|
|
|
+ if (mapInstance) { try { mapInstance.clearOverlays() } catch (_) {}; mapInstance = null }
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.repair-container { padding: 16px; }
|
|
|
-.search-card { margin-bottom: 16px; }
|
|
|
+.repair-container { padding: 16px; display: flex; flex-direction: column; gap: 16px; background: #f0f2f6; min-height: calc(100vh - 84px); }
|
|
|
+
|
|
|
+/* ========== 上半部分 ========== */
|
|
|
+.top-section { display: flex; gap: 16px; align-items: stretch; }
|
|
|
+.topo-card { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
|
|
+.charts-column { width: 340px; flex-shrink: 0; display: flex; flex-direction: column; gap: 16px; }
|
|
|
+.chart-card { flex: 1; }
|
|
|
+.card-title { font-size: 15px; font-weight: 700; color: #1f2d3d; }
|
|
|
+
|
|
|
+.topo-header { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; }
|
|
|
+.topo-legend { display: flex; gap: 14px; flex-wrap: wrap; }
|
|
|
+.legend-item { font-size: 11px; color: #606a78; display: flex; align-items: center; gap: 5px; }
|
|
|
+.legend-line { display: inline-block; width: 24px; height: 3px; border-radius: 2px; }
|
|
|
+.legend-line.high { background: #e63946; }
|
|
|
+.legend-line.medium { background: #f77f00; }
|
|
|
+.legend-line.low { background: #2a9d8f; }
|
|
|
+.legend-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }
|
|
|
+.legend-dot.repaired { background: #f59e0b; border: 2px solid #fff; box-shadow: 0 0 4px rgba(245,158,11,0.5); }
|
|
|
+.legend-dot.point { background: #3b82f6; border: 2px solid #fff; }
|
|
|
+
|
|
|
+.map-wrapper { position: relative; width: 100%; height: 100%; min-height: 460px; border-radius: 6px; overflow: hidden; }
|
|
|
+.baidu-map { width: 100%; height: 100%; min-height: 460px; }
|
|
|
+.map-error { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: #fafbfc; color: #e63946; font-size: 14px; font-weight: 600; }
|
|
|
+.topo-card { display: flex; flex-direction: column; }
|
|
|
+:deep(.topo-card .el-card__body) { flex: 1; display: flex; padding: 0; overflow: hidden; }
|
|
|
+
|
|
|
+/* ========== 表格 ========== */
|
|
|
+.table-toolbar { display: flex; align-items: flex-start; justify-content: space-between; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
|
|
|
+.search-form { margin-bottom: 0; }
|
|
|
+:deep(.search-form .el-form-item) { margin-bottom: 0; }
|
|
|
+
|
|
|
+:deep(.el-card__header) { padding: 12px 18px; border-bottom: 1px solid #f0f2f6; flex-shrink: 0; }
|
|
|
+:deep(.topo-card .el-card__body) { flex: 1; display: flex; padding: 0; overflow: hidden; }
|
|
|
+:deep(.chart-card .el-card__body) { padding: 12px; }
|
|
|
+:deep(.table-card .el-card__body) { padding: 12px; }
|
|
|
+
|
|
|
+@media (max-width: 1400px) {
|
|
|
+ .top-section { flex-direction: column; }
|
|
|
+ .map-wrapper { height: 400px; }
|
|
|
+ .charts-column { width: 100%; flex-direction: row; }
|
|
|
+ .charts-column .chart-card { flex: 1; min-width: 300px; }
|
|
|
+}
|
|
|
</style>
|