|
@@ -1,855 +1,559 @@
|
|
|
-<!--物联感知设备管理-->
|
|
|
|
|
|
|
+<!-- 物联感知设备管理 -->
|
|
|
<template>
|
|
<template>
|
|
|
- <div class="prototype-container">
|
|
|
|
|
-
|
|
|
|
|
- <div class="main-layout">
|
|
|
|
|
- <!-- 左侧:查询与统计面板 -->
|
|
|
|
|
- <div class="left-panel">
|
|
|
|
|
- <div class="card">
|
|
|
|
|
- <div class="card-header">🔍 设施查询</div>
|
|
|
|
|
- <div class="card-body">
|
|
|
|
|
- <div class="form-group">
|
|
|
|
|
- <label class="form-label">设施名称</label>
|
|
|
|
|
- <input
|
|
|
|
|
- type="text"
|
|
|
|
|
- v-model="searchParams.name"
|
|
|
|
|
- class="form-input"
|
|
|
|
|
- placeholder="模糊匹配"
|
|
|
|
|
- @keyup.enter="handleSearch"
|
|
|
|
|
- >
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-group">
|
|
|
|
|
- <label class="form-label">设施类型</label>
|
|
|
|
|
- <select v-model="searchParams.type" class="form-select">
|
|
|
|
|
- <option value="">全部</option>
|
|
|
|
|
- <option value="管网路线">管网路线</option>
|
|
|
|
|
- <option value="燃气设施">燃气设施</option>
|
|
|
|
|
- <option value="排水设施">排水设施</option>
|
|
|
|
|
- <option value="供水设施">供水设施</option>
|
|
|
|
|
- <option value="桥梁">桥梁</option>
|
|
|
|
|
- </select>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-group">
|
|
|
|
|
- <label class="form-label">设施等级</label>
|
|
|
|
|
- <select v-model="searchParams.level" class="form-select">
|
|
|
|
|
- <option value="">全部</option>
|
|
|
|
|
- <option value="一级">一级</option>
|
|
|
|
|
- <option value="二级">二级</option>
|
|
|
|
|
- <option value="三级">三级</option>
|
|
|
|
|
- </select>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-group">
|
|
|
|
|
- <label class="form-label">管养单位</label>
|
|
|
|
|
- <input
|
|
|
|
|
- type="text"
|
|
|
|
|
- v-model="searchParams.unit"
|
|
|
|
|
- class="form-input"
|
|
|
|
|
- placeholder="管养单位"
|
|
|
|
|
- >
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="search-group">
|
|
|
|
|
- <button @click="handleSearch" class="btn-primary">🔍 查询</button>
|
|
|
|
|
- <button @click="handleReset" class="btn-outline">重置</button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="app-container">
|
|
|
|
|
+ <el-form ref="queryRef" :model="queryParams" :inline="true" v-show="showSearch">
|
|
|
|
|
+ <el-form-item label="设备编码" prop="equipmentCode">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.equipmentCode"
|
|
|
|
|
+ placeholder="请输入设备编码"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ style="width: 220px"
|
|
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="设备名称" prop="equipmentName">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.equipmentName"
|
|
|
|
|
+ placeholder="请输入设备名称"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ style="width: 220px"
|
|
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="设备型号" prop="equipmentModel">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.equipmentModel"
|
|
|
|
|
+ placeholder="请输入设备型号"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ style="width: 220px"
|
|
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="制造商" prop="manufacturer">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.manufacturer"
|
|
|
|
|
+ placeholder="请输入制造商"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ style="width: 220px"
|
|
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item>
|
|
|
|
|
+ <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
|
|
|
+ <el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <el-row :gutter="10" class="mb8">
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
|
|
|
|
|
+ 修改
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table v-loading="loading" :data="equipmentList" @selection-change="handleSelectionChange">
|
|
|
|
|
+ <el-table-column type="selection" width="55" align="center" />
|
|
|
|
|
+ <el-table-column label="设备编码" align="center" prop="equipmentCode" min-width="140" />
|
|
|
|
|
+ <el-table-column label="设备名称" align="center" prop="equipmentName" min-width="160" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="设备型号" align="center" prop="equipmentModel" min-width="120" />
|
|
|
|
|
+ <el-table-column label="设备类型" align="center" min-width="140">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ {{ typeNameMap[scope.row.equipmentTypeId] || scope.row.equipmentTypeId || "-" }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="当前状态" align="center" width="110">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-tag :type="getStatusTagType(getRowStatus(scope.row.equipmentId))">
|
|
|
|
|
+ {{ getStatusText(getRowStatus(scope.row.equipmentId)) }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="制造商" align="center" prop="manufacturer" min-width="140" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="设备位置" align="center" prop="equipmentLocation" min-width="180" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="维护人员" align="center" prop="maintainer" min-width="100" />
|
|
|
|
|
+ <el-table-column label="操作" align="center" width="300" class-name="small-padding fixed-width">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <el-button link type="primary" icon="View" @click="handleView(scope.row)">详情</el-button>
|
|
|
|
|
+ <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)">修改</el-button>
|
|
|
|
|
+ <el-button link type="warning" icon="Tools" @click="openStatusDialog(scope.row)">状态维护</el-button>
|
|
|
|
|
+ <el-button link type="success" icon="Aim" @click="openTestDialog(scope.row)">测试验证</el-button>
|
|
|
|
|
+ <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)">删除</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+
|
|
|
|
|
+ <pagination
|
|
|
|
|
+ v-show="total > 0"
|
|
|
|
|
+ :total="total"
|
|
|
|
|
+ v-model:page="queryParams.pageNum"
|
|
|
|
|
+ v-model:limit="queryParams.pageSize"
|
|
|
|
|
+ @pagination="getList"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <el-dialog :title="title" v-model="open" width="860px" append-to-body>
|
|
|
|
|
+ <el-form ref="equipmentRef" :model="form" :rules="rules" label-width="110px">
|
|
|
|
|
+ <el-row :gutter="16">
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="设备编码" prop="equipmentCode">
|
|
|
|
|
+ <el-input v-model="form.equipmentCode" placeholder="请输入设备编码" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="设备名称" prop="equipmentName">
|
|
|
|
|
+ <el-input v-model="form.equipmentName" placeholder="请输入设备名称" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="设备型号" prop="equipmentModel">
|
|
|
|
|
+ <el-input v-model="form.equipmentModel" placeholder="请输入设备型号" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="设备类型" prop="equipmentTypeId">
|
|
|
|
|
+ <el-select v-model="form.equipmentTypeId" placeholder="请选择设备类型" style="width: 100%">
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in equipmentTypeOptions"
|
|
|
|
|
+ :key="item.typeId"
|
|
|
|
|
+ :label="item.typeName"
|
|
|
|
|
+ :value="item.typeId"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="制造商" prop="manufacturer">
|
|
|
|
|
+ <el-input v-model="form.manufacturer" placeholder="请输入制造商" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="生产日期" prop="productionDate">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="form.productionDate"
|
|
|
|
|
+ type="date"
|
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
|
+ placeholder="请选择生产日期"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="24">
|
|
|
|
|
+ <el-form-item label="规格参数" prop="equipmentSpec">
|
|
|
|
|
+ <el-input v-model="form.equipmentSpec" placeholder="请输入设备规格参数" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="资产价值(元)" prop="assetValue">
|
|
|
|
|
+ <el-input-number v-model="form.assetValue" :precision="2" :step="100" style="width: 100%" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="使用年限(年)" prop="useLife">
|
|
|
|
|
+ <el-input-number v-model="form.useLife" :precision="0" :step="1" style="width: 100%" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="24">
|
|
|
|
|
+ <el-form-item label="设备位置" prop="equipmentLocation">
|
|
|
|
|
+ <el-input v-model="form.equipmentLocation" placeholder="请输入设备位置" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="经度" prop="longitude">
|
|
|
|
|
+ <el-input-number v-model="form.longitude" :precision="6" :step="0.000001" style="width: 100%" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="纬度" prop="latitude">
|
|
|
|
|
+ <el-input-number v-model="form.latitude" :precision="6" :step="0.000001" style="width: 100%" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="安装时间" prop="installTime">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="form.installTime"
|
|
|
|
|
+ type="datetime"
|
|
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
+ placeholder="请选择安装时间"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="维护人员" prop="maintainer">
|
|
|
|
|
+ <el-input v-model="form.maintainer" placeholder="请输入维护人员" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-form-item label="联系电话" prop="maintainerPhone">
|
|
|
|
|
+ <el-input v-model="form.maintainerPhone" placeholder="请输入联系电话" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="24">
|
|
|
|
|
+ <el-form-item label="备注" prop="remark">
|
|
|
|
|
+ <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
|
+ <el-button type="primary" @click="submitForm">确 定</el-button>
|
|
|
|
|
+ <el-button @click="cancel">取 消</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
- <div class="card">
|
|
|
|
|
- <div class="card-header">📊 分类统计</div>
|
|
|
|
|
- <div class="card-body">
|
|
|
|
|
- <div v-for="item in typeStats" :key="item.type" class="stat-item">
|
|
|
|
|
- <span class="stat-label">{{ item.type }}</span>
|
|
|
|
|
- <span class="stat-value">{{ item.count }}</span>
|
|
|
|
|
- <div class="stat-bar">
|
|
|
|
|
- <div class="stat-fill" :style="{ width: getStatPercent(item.count) + '%' }"></div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 右侧:设施列表 -->
|
|
|
|
|
- <div class="right-panel">
|
|
|
|
|
- <div class="card">
|
|
|
|
|
- <div class="card-header">
|
|
|
|
|
- 📋 基础设施列表
|
|
|
|
|
- <div class="header-actions">
|
|
|
|
|
- <button @click="openAddModal" class="btn-primary btn-sm">+ 新增设施</button>
|
|
|
|
|
- <button @click="handleImport" class="btn-outline btn-sm">📂 导入</button>
|
|
|
|
|
- <button @click="handleExport" class="btn-outline btn-sm">📎 导出</button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="card-body">
|
|
|
|
|
- <div class="table-wrapper">
|
|
|
|
|
- <table class="data-table">
|
|
|
|
|
- <thead>
|
|
|
|
|
- <tr>
|
|
|
|
|
- <th>设施名称</th>
|
|
|
|
|
- <th>设施类型</th>
|
|
|
|
|
- <th>设施等级</th>
|
|
|
|
|
- <th>设施编码</th>
|
|
|
|
|
- <th>管养单位</th>
|
|
|
|
|
- <th>位置</th>
|
|
|
|
|
- <th>状态</th>
|
|
|
|
|
- <th>操作</th>
|
|
|
|
|
- </tr>
|
|
|
|
|
- </thead>
|
|
|
|
|
- <tbody>
|
|
|
|
|
- <tr v-if="paginatedList.length === 0">
|
|
|
|
|
- <td colspan="8" style="text-align:center;">暂无数据</td>
|
|
|
|
|
- </tr>
|
|
|
|
|
- <tr v-for="item in paginatedList" :key="item.id">
|
|
|
|
|
- <td><strong>{{ item.name }}</strong></td>
|
|
|
|
|
- <td><span class="type-badge" :class="getTypeClass(item.type)">{{ item.type }}</span></td>
|
|
|
|
|
- <td>{{ item.level || '-' }}</td>
|
|
|
|
|
- <td><code>{{ item.code }}</code></td>
|
|
|
|
|
- <td>{{ item.maintenanceUnit || '-' }}</td>
|
|
|
|
|
- <td>{{ item.location || '-' }}</td>
|
|
|
|
|
- <td>
|
|
|
|
|
- <span class="status-badge" :class="getStatusClass(item.status)">
|
|
|
|
|
- {{ getStatusText(item.status) }}
|
|
|
|
|
- </span>
|
|
|
|
|
- </td>
|
|
|
|
|
- <td class="action-icons">
|
|
|
|
|
- <button class="icon-btn" @click="viewDetail(item)" title="查看">👁️</button>
|
|
|
|
|
- <button class="icon-btn" @click="openEditModal(item)" title="编辑">✏️</button>
|
|
|
|
|
- <button class="icon-btn" @click="deleteItem(item)" title="删除">🗑️</button>
|
|
|
|
|
- </td>
|
|
|
|
|
- </tr>
|
|
|
|
|
- </tbody>
|
|
|
|
|
- </table>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="pagination" v-if="totalPages > 1">
|
|
|
|
|
- <button
|
|
|
|
|
- v-for="page in totalPages"
|
|
|
|
|
- :key="page"
|
|
|
|
|
- class="page-btn"
|
|
|
|
|
- :class="{ active: currentPage === page }"
|
|
|
|
|
- @click="currentPage = page"
|
|
|
|
|
- >
|
|
|
|
|
- {{ page }}
|
|
|
|
|
- </button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <el-dialog title="设备详情" v-model="detailOpen" width="980px" append-to-body>
|
|
|
|
|
+ <el-descriptions :column="2" border v-if="detailForm.equipmentInfo">
|
|
|
|
|
+ <el-descriptions-item label="设备编码">{{ detailForm.equipmentInfo.equipmentCode || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="设备名称">{{ detailForm.equipmentInfo.equipmentName || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="设备型号">{{ detailForm.equipmentInfo.equipmentModel || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="设备类型">{{ detailForm.typeInfo?.typeName || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="制造商">{{ detailForm.equipmentInfo.manufacturer || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="当前状态">{{ getStatusText(detailForm.statusInfo?.currentStatus) }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="设备位置" :span="2">{{ detailForm.equipmentInfo.equipmentLocation || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="规格参数" :span="2">{{ detailForm.equipmentInfo.equipmentSpec || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="维护人员">{{ detailForm.equipmentInfo.maintainer || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="联系电话">{{ detailForm.equipmentInfo.maintainerPhone || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="安装时间">{{ detailForm.equipmentInfo.installTime || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="生产日期">{{ detailForm.equipmentInfo.productionDate || "-" }}</el-descriptions-item>
|
|
|
|
|
+ <el-descriptions-item label="备注" :span="2">{{ detailForm.equipmentInfo.remark || "-" }}</el-descriptions-item>
|
|
|
|
|
+ </el-descriptions>
|
|
|
|
|
+
|
|
|
|
|
+ <el-divider content-position="left">关联监测点</el-divider>
|
|
|
|
|
+ <el-table :data="detailForm.pointRelList || []" border>
|
|
|
|
|
+ <el-table-column label="监测点ID" prop="pointId" min-width="200" show-overflow-tooltip />
|
|
|
|
|
+ <el-table-column label="设备ID" prop="equipmentId" min-width="200" show-overflow-tooltip />
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+
|
|
|
|
|
+ <el-divider content-position="left">测试记录</el-divider>
|
|
|
|
|
+ <el-table :data="detailForm.testList || []" border>
|
|
|
|
|
+ <el-table-column label="测试时间" prop="testTime" min-width="170" />
|
|
|
|
|
+ <el-table-column label="测试类型" prop="testType" min-width="120" />
|
|
|
|
|
+ <el-table-column label="测试结果" prop="testResult" width="100" />
|
|
|
|
|
+ <el-table-column label="测试人员" prop="testUser" width="120" />
|
|
|
|
|
+ <el-table-column label="测试详情" prop="testDetail" min-width="220" show-overflow-tooltip />
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <el-dialog title="设备状态维护" v-model="statusOpen" width="420px" append-to-body>
|
|
|
|
|
+ <el-form label-width="90px">
|
|
|
|
|
+ <el-form-item label="设备名称">
|
|
|
|
|
+ <span>{{ currentRow.equipmentName || "-" }}</span>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="当前状态">
|
|
|
|
|
+ <el-select v-model="statusForm.currentStatus" style="width: 100%">
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in equipmentStatusOptions"
|
|
|
|
|
+ :key="item.value"
|
|
|
|
|
+ :label="item.label"
|
|
|
|
|
+ :value="item.value"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
|
+ <el-button type="primary" @click="submitStatus">确 定</el-button>
|
|
|
|
|
+ <el-button @click="statusOpen = false">取 消</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 详情查看模态框 -->
|
|
|
|
|
- <div v-if="detailModalVisible" class="modal-overlay" @click.self="closeDetailModal">
|
|
|
|
|
- <div class="modal-content">
|
|
|
|
|
- <h3 style="margin-bottom:16px;">📋 设施详细信息</h3>
|
|
|
|
|
- <div class="detail-row"><span class="detail-label">设施名称:</span>{{ currentItem?.name }}</div>
|
|
|
|
|
- <div class="detail-row"><span class="detail-label">设施类型:</span>{{ currentItem?.type }}</div>
|
|
|
|
|
- <div class="detail-row"><span class="detail-label">设施等级:</span>{{ currentItem?.level || '-' }}</div>
|
|
|
|
|
- <div class="detail-row"><span class="detail-label">设施编码:</span>{{ currentItem?.code }}</div>
|
|
|
|
|
- <div class="detail-row"><span class="detail-label">管养单位:</span>{{ currentItem?.maintenanceUnit || '-' }}</div>
|
|
|
|
|
- <div class="detail-row"><span class="detail-label">具体位置:</span>{{ currentItem?.location || '-' }}</div>
|
|
|
|
|
- <div class="detail-row"><span class="detail-label">建设年份:</span>{{ currentItem?.buildYear || '-' }}</div>
|
|
|
|
|
- <div class="detail-row"><span class="detail-label">设计年限:</span>{{ currentItem?.designLife || '-' }} 年</div>
|
|
|
|
|
- <div class="detail-row"><span class="detail-label">上次巡检:</span>{{ currentItem?.lastInspection || '-' }}</div>
|
|
|
|
|
- <div class="detail-row"><span class="detail-label">设施状态:</span>
|
|
|
|
|
- <span class="status-badge" :class="getStatusClass(currentItem?.status)">
|
|
|
|
|
- {{ getStatusText(currentItem?.status) }}
|
|
|
|
|
- </span>
|
|
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <el-dialog title="设备测试验证" v-model="testOpen" width="520px" append-to-body>
|
|
|
|
|
+ <el-form ref="testRef" :model="testForm" label-width="90px">
|
|
|
|
|
+ <el-form-item label="设备名称">
|
|
|
|
|
+ <span>{{ currentRow.equipmentName || "-" }}</span>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="测试类型">
|
|
|
|
|
+ <el-input v-model="testForm.testType" placeholder="例如:通信测试" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="测试人员">
|
|
|
|
|
+ <el-input v-model="testForm.testUser" placeholder="请输入测试人员" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="测试详情">
|
|
|
|
|
+ <el-input v-model="testForm.testDetail" type="textarea" :rows="4" placeholder="请输入测试详情" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
|
+ <el-button type="primary" @click="submitTest">确 定</el-button>
|
|
|
|
|
+ <el-button @click="testOpen = false">取 消</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="detail-row"><span class="detail-label">备注:</span>{{ currentItem?.remark || '-' }}</div>
|
|
|
|
|
- <div style="margin-top:20px; display:flex; gap:10px; justify-content:flex-end;">
|
|
|
|
|
- <button @click="closeDetailModal" class="btn-outline">关闭</button>
|
|
|
|
|
- <button @click="openEditFromDetail" class="btn-primary">编辑设施</button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 新增/编辑模态框 -->
|
|
|
|
|
- <div v-if="editModalVisible" class="modal-overlay" @click.self="closeEditModal">
|
|
|
|
|
- <div class="modal-content" style="width:560px;">
|
|
|
|
|
- <h3 style="margin-bottom:16px;">{{ isEdit ? '编辑设施信息' : '新增设施' }}</h3>
|
|
|
|
|
- <form @submit.prevent="saveItem">
|
|
|
|
|
- <div class="form-row">
|
|
|
|
|
- <div class="form-group half">
|
|
|
|
|
- <label class="form-label">设施名称 *</label>
|
|
|
|
|
- <input v-model="formData.name" class="form-input" required>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-group half">
|
|
|
|
|
- <label class="form-label">设施类型 *</label>
|
|
|
|
|
- <select v-model="formData.type" class="form-select" required>
|
|
|
|
|
- <option value="管网路线">管网路线</option>
|
|
|
|
|
- <option value="燃气设施">燃气设施</option>
|
|
|
|
|
- <option value="排水设施">排水设施</option>
|
|
|
|
|
- <option value="供水设施">供水设施</option>
|
|
|
|
|
- <option value="桥梁">桥梁</option>
|
|
|
|
|
- </select>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-row">
|
|
|
|
|
- <div class="form-group half">
|
|
|
|
|
- <label class="form-label">设施等级</label>
|
|
|
|
|
- <select v-model="formData.level" class="form-select">
|
|
|
|
|
- <option value="">无</option>
|
|
|
|
|
- <option value="一级">一级</option>
|
|
|
|
|
- <option value="二级">二级</option>
|
|
|
|
|
- <option value="三级">三级</option>
|
|
|
|
|
- </select>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-group half">
|
|
|
|
|
- <label class="form-label">设施编码 *</label>
|
|
|
|
|
- <input v-model="formData.code" class="form-input" required placeholder="唯一编码">
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-row">
|
|
|
|
|
- <div class="form-group half">
|
|
|
|
|
- <label class="form-label">管养单位</label>
|
|
|
|
|
- <input v-model="formData.maintenanceUnit" class="form-input" placeholder="管养单位">
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-group half">
|
|
|
|
|
- <label class="form-label">具体位置</label>
|
|
|
|
|
- <input v-model="formData.location" class="form-input" placeholder="经纬度或地址">
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-row">
|
|
|
|
|
- <div class="form-group half">
|
|
|
|
|
- <label class="form-label">建设年份</label>
|
|
|
|
|
- <input v-model="formData.buildYear" type="number" class="form-input" placeholder="YYYY">
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-group half">
|
|
|
|
|
- <label class="form-label">设计年限(年)</label>
|
|
|
|
|
- <input v-model="formData.designLife" type="number" class="form-input" placeholder="设计使用年限">
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-row">
|
|
|
|
|
- <div class="form-group half">
|
|
|
|
|
- <label class="form-label">上次巡检</label>
|
|
|
|
|
- <input v-model="formData.lastInspection" type="date" class="form-input">
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-group half">
|
|
|
|
|
- <label class="form-label">设施状态</label>
|
|
|
|
|
- <select v-model="formData.status" class="form-select">
|
|
|
|
|
- <option value="normal">正常</option>
|
|
|
|
|
- <option value="warning">需关注</option>
|
|
|
|
|
- <option value="danger">存在风险</option>
|
|
|
|
|
- <option value="maintenance">维修中</option>
|
|
|
|
|
- </select>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="form-group">
|
|
|
|
|
- <label class="form-label">备注</label>
|
|
|
|
|
- <textarea v-model="formData.remark" rows="2" class="form-textarea" placeholder="其他说明"></textarea>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div style="margin-top:20px; display:flex; gap:10px; justify-content:flex-end;">
|
|
|
|
|
- <button type="button" @click="closeEditModal" class="btn-outline">取消</button>
|
|
|
|
|
- <button type="submit" class="btn-primary">保存</button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </form>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { ref, computed } from 'vue'
|
|
|
|
|
-
|
|
|
|
|
-// 设施数据类型定义
|
|
|
|
|
-const facilities = ref([
|
|
|
|
|
- {
|
|
|
|
|
- id: 1, name: "城东燃气管网", type: "燃气设施", level: "一级", code: "GAS-001",
|
|
|
|
|
- maintenanceUnit: "市燃气集团", location: "城东区龙蟠路", buildYear: "2018",
|
|
|
|
|
- designLife: "30", lastInspection: "2025-03-15", status: "normal", remark: "主要供气干线"
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 2, name: "城南雨水管网", type: "排水设施", level: "二级", code: "DRN-002",
|
|
|
|
|
- maintenanceUnit: "市政排水公司", location: "城南新区", buildYear: "2020",
|
|
|
|
|
- designLife: "25", lastInspection: "2025-02-10", status: "normal", remark: ""
|
|
|
|
|
|
|
+import { computed, getCurrentInstance, onMounted, reactive, ref, toRefs } from "vue";
|
|
|
|
|
+import {
|
|
|
|
|
+ addEquipment,
|
|
|
|
|
+ delEquipment,
|
|
|
|
|
+ getEquipment,
|
|
|
|
|
+ getEquipmentDetail,
|
|
|
|
|
+ listEquipment,
|
|
|
|
|
+ listEquipmentStatusAll,
|
|
|
|
|
+ listEquipmentTypeAll,
|
|
|
|
|
+ testEquipment,
|
|
|
|
|
+ updateEquipment,
|
|
|
|
|
+ updateEquipmentStatus
|
|
|
|
|
+} from "@/api/pipeNetwork/pdeviceInform";
|
|
|
|
|
+
|
|
|
|
|
+const { proxy } = getCurrentInstance();
|
|
|
|
|
+
|
|
|
|
|
+const equipmentList = ref([]);
|
|
|
|
|
+const equipmentTypeOptions = ref([]);
|
|
|
|
|
+const equipmentStatusList = ref([]);
|
|
|
|
|
+const loading = ref(false);
|
|
|
|
|
+const open = ref(false);
|
|
|
|
|
+const detailOpen = ref(false);
|
|
|
|
|
+const statusOpen = ref(false);
|
|
|
|
|
+const testOpen = ref(false);
|
|
|
|
|
+const showSearch = ref(true);
|
|
|
|
|
+const ids = ref([]);
|
|
|
|
|
+const single = ref(true);
|
|
|
|
|
+const multiple = ref(true);
|
|
|
|
|
+const total = ref(0);
|
|
|
|
|
+const title = ref("");
|
|
|
|
|
+const currentRow = ref({});
|
|
|
|
|
+
|
|
|
|
|
+const equipmentStatusOptions = [
|
|
|
|
|
+ { label: "在用", value: 1 },
|
|
|
|
|
+ { label: "闲置", value: 2 },
|
|
|
|
|
+ { label: "维修", value: 3 },
|
|
|
|
|
+ { label: "报废", value: 4 },
|
|
|
|
|
+ { label: "待入库", value: 5 }
|
|
|
|
|
+];
|
|
|
|
|
+
|
|
|
|
|
+const typeNameMap = computed(() => {
|
|
|
|
|
+ return equipmentTypeOptions.value.reduce((acc, item) => {
|
|
|
|
|
+ acc[item.typeId] = item.typeName;
|
|
|
|
|
+ return acc;
|
|
|
|
|
+ }, {});
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const statusMap = computed(() => {
|
|
|
|
|
+ return equipmentStatusList.value.reduce((acc, item) => {
|
|
|
|
|
+ acc[item.equipmentId] = item;
|
|
|
|
|
+ return acc;
|
|
|
|
|
+ }, {});
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const data = reactive({
|
|
|
|
|
+ queryParams: {
|
|
|
|
|
+ pageNum: 1,
|
|
|
|
|
+ pageSize: 10,
|
|
|
|
|
+ equipmentCode: undefined,
|
|
|
|
|
+ equipmentName: undefined,
|
|
|
|
|
+ equipmentModel: undefined,
|
|
|
|
|
+ manufacturer: undefined
|
|
|
},
|
|
},
|
|
|
- {
|
|
|
|
|
- id: 3, name: "城北供水主管", type: "供水设施", level: "一级", code: "WTR-003",
|
|
|
|
|
- maintenanceUnit: "水务集团", location: "城北大道", buildYear: "2015",
|
|
|
|
|
- designLife: "30", lastInspection: "2025-01-20", status: "warning", remark: "部分管段老化"
|
|
|
|
|
|
|
+ form: {},
|
|
|
|
|
+ detailForm: {},
|
|
|
|
|
+ statusForm: {
|
|
|
|
|
+ currentStatus: 1
|
|
|
},
|
|
},
|
|
|
- {
|
|
|
|
|
- id: 4, name: "江汉大桥", type: "桥梁", level: "一级", code: "BRG-004",
|
|
|
|
|
- maintenanceUnit: "市桥梁管理处", location: "江汉路与长江大道交叉口", buildYear: "2010",
|
|
|
|
|
- designLife: "50", lastInspection: "2024-12-05", status: "normal", remark: "定期维护中"
|
|
|
|
|
|
|
+ testForm: {
|
|
|
|
|
+ testType: undefined,
|
|
|
|
|
+ testDetail: undefined,
|
|
|
|
|
+ testUser: undefined
|
|
|
},
|
|
},
|
|
|
- {
|
|
|
|
|
- id: 5, name: "城西天然气管线", type: "燃气设施", level: "二级", code: "GAS-005",
|
|
|
|
|
- maintenanceUnit: "市燃气集团", location: "城西工业区", buildYear: "2019",
|
|
|
|
|
- designLife: "30", lastInspection: "2025-02-28", status: "normal", remark: ""
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 6, name: "城北排水管网", type: "排水设施", level: "三级", code: "DRN-006",
|
|
|
|
|
- maintenanceUnit: "市政排水公司", location: "北环路", buildYear: "2021",
|
|
|
|
|
- designLife: "25", lastInspection: "2025-03-01", status: "normal", remark: ""
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 7, name: "东区供水管网", type: "供水设施", level: "二级", code: "WTR-007",
|
|
|
|
|
- maintenanceUnit: "水务集团", location: "东开发区", buildYear: "2017",
|
|
|
|
|
- designLife: "30", lastInspection: "2024-11-15", status: "maintenance", remark: "管道维修中"
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- id: 8, name: "南湖大桥", type: "桥梁", level: "二级", code: "BRG-008",
|
|
|
|
|
- maintenanceUnit: "市桥梁管理处", location: "南湖路", buildYear: "2012",
|
|
|
|
|
- designLife: "50", lastInspection: "2024-10-20", status: "warning", remark: "桥面需修复"
|
|
|
|
|
|
|
+ rules: {
|
|
|
|
|
+ equipmentCode: [{ required: true, message: "设备编码不能为空", trigger: "blur" }],
|
|
|
|
|
+ equipmentName: [{ required: true, message: "设备名称不能为空", trigger: "blur" }],
|
|
|
|
|
+ equipmentTypeId: [{ required: true, message: "设备类型不能为空", trigger: "change" }]
|
|
|
}
|
|
}
|
|
|
-])
|
|
|
|
|
-
|
|
|
|
|
-// 搜索参数
|
|
|
|
|
-const searchParams = ref({
|
|
|
|
|
- name: '',
|
|
|
|
|
- type: '',
|
|
|
|
|
- level: '',
|
|
|
|
|
- unit: ''
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-// 分页
|
|
|
|
|
-const currentPage = ref(1)
|
|
|
|
|
-const pageSize = 8
|
|
|
|
|
-
|
|
|
|
|
-// 模态框状态
|
|
|
|
|
-const detailModalVisible = ref(false)
|
|
|
|
|
-const editModalVisible = ref(false)
|
|
|
|
|
-const currentItem = ref(null)
|
|
|
|
|
-const isEdit = ref(false)
|
|
|
|
|
-const formData = ref({
|
|
|
|
|
- name: '', type: '管网路线', level: '', code: '',
|
|
|
|
|
- maintenanceUnit: '', location: '', buildYear: '', designLife: '',
|
|
|
|
|
- lastInspection: '', status: 'normal', remark: ''
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-// 计算总数
|
|
|
|
|
-const totalCount = computed(() => facilities.value.length)
|
|
|
|
|
-
|
|
|
|
|
-// 分类统计
|
|
|
|
|
-const typeStats = computed(() => {
|
|
|
|
|
- const types = ['管网路线', '燃气设施', '排水设施', '供水设施', '桥梁']
|
|
|
|
|
- return types.map(type => ({
|
|
|
|
|
- type,
|
|
|
|
|
- count: facilities.value.filter(f => f.type === type).length
|
|
|
|
|
- }))
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-// 获取统计百分比
|
|
|
|
|
-const getStatPercent = (count) => {
|
|
|
|
|
- if (totalCount.value === 0) return 0
|
|
|
|
|
- return (count / totalCount.value) * 100
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 过滤后的列表
|
|
|
|
|
-const filteredList = computed(() => {
|
|
|
|
|
- let result = [...facilities.value]
|
|
|
|
|
- const { name, type, level, unit } = searchParams.value
|
|
|
|
|
-
|
|
|
|
|
- if (name) {
|
|
|
|
|
- const kw = name.toLowerCase()
|
|
|
|
|
- result = result.filter(f => f.name.toLowerCase().includes(kw))
|
|
|
|
|
- }
|
|
|
|
|
- if (type) {
|
|
|
|
|
- result = result.filter(f => f.type === type)
|
|
|
|
|
- }
|
|
|
|
|
- if (level) {
|
|
|
|
|
- result = result.filter(f => f.level === level)
|
|
|
|
|
- }
|
|
|
|
|
- if (unit) {
|
|
|
|
|
- const kw = unit.toLowerCase()
|
|
|
|
|
- result = result.filter(f => f.maintenanceUnit?.toLowerCase().includes(kw))
|
|
|
|
|
- }
|
|
|
|
|
- return result
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-// 分页后的列表
|
|
|
|
|
-const paginatedList = computed(() => {
|
|
|
|
|
- const start = (currentPage.value - 1) * pageSize
|
|
|
|
|
- return filteredList.value.slice(start, start + pageSize)
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-// 总页数
|
|
|
|
|
-const totalPages = computed(() => {
|
|
|
|
|
- return Math.ceil(filteredList.value.length / pageSize)
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-// 获取类型样式
|
|
|
|
|
-const getTypeClass = (type) => {
|
|
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const { queryParams, form, detailForm, statusForm, testForm, rules } = toRefs(data);
|
|
|
|
|
+
|
|
|
|
|
+function createEmptyForm() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ equipmentId: undefined,
|
|
|
|
|
+ equipmentCode: undefined,
|
|
|
|
|
+ equipmentName: undefined,
|
|
|
|
|
+ equipmentModel: undefined,
|
|
|
|
|
+ equipmentSpec: undefined,
|
|
|
|
|
+ manufacturer: undefined,
|
|
|
|
|
+ productionDate: undefined,
|
|
|
|
|
+ equipmentTypeId: undefined,
|
|
|
|
|
+ assetValue: undefined,
|
|
|
|
|
+ useLife: undefined,
|
|
|
|
|
+ longitude: undefined,
|
|
|
|
|
+ latitude: undefined,
|
|
|
|
|
+ equipmentLocation: undefined,
|
|
|
|
|
+ installTime: undefined,
|
|
|
|
|
+ maintainer: undefined,
|
|
|
|
|
+ maintainerPhone: undefined,
|
|
|
|
|
+ remark: undefined
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getRowStatus(equipmentId) {
|
|
|
|
|
+ return statusMap.value[equipmentId]?.currentStatus;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getStatusText(status) {
|
|
|
|
|
+ const found = equipmentStatusOptions.find((item) => item.value === status);
|
|
|
|
|
+ return found?.label || "未维护";
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getStatusTagType(status) {
|
|
|
const map = {
|
|
const map = {
|
|
|
- '管网路线': 'type-pipeline',
|
|
|
|
|
- '燃气设施': 'type-gas',
|
|
|
|
|
- '排水设施': 'type-drain',
|
|
|
|
|
- '供水设施': 'type-water',
|
|
|
|
|
- '桥梁': 'type-bridge'
|
|
|
|
|
- }
|
|
|
|
|
- return map[type] || ''
|
|
|
|
|
|
|
+ 1: "success",
|
|
|
|
|
+ 2: "info",
|
|
|
|
|
+ 3: "warning",
|
|
|
|
|
+ 4: "danger",
|
|
|
|
|
+ 5: ""
|
|
|
|
|
+ };
|
|
|
|
|
+ return map[status] || "";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 状态相关
|
|
|
|
|
-const getStatusClass = (status) => {
|
|
|
|
|
- const map = {
|
|
|
|
|
- normal: 'status-normal',
|
|
|
|
|
- warning: 'status-warning',
|
|
|
|
|
- danger: 'status-danger',
|
|
|
|
|
- maintenance: 'status-maintenance'
|
|
|
|
|
- }
|
|
|
|
|
- return map[status] || ''
|
|
|
|
|
|
|
+function getList() {
|
|
|
|
|
+ loading.value = true;
|
|
|
|
|
+ listEquipment(queryParams.value)
|
|
|
|
|
+ .then((response) => {
|
|
|
|
|
+ equipmentList.value = response.records || [];
|
|
|
|
|
+ total.value = response.total || 0;
|
|
|
|
|
+ })
|
|
|
|
|
+ .finally(() => {
|
|
|
|
|
+ loading.value = false;
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const getStatusText = (status) => {
|
|
|
|
|
- const map = {
|
|
|
|
|
- normal: '● 正常',
|
|
|
|
|
- warning: '● 需关注',
|
|
|
|
|
- danger: '● 存在风险',
|
|
|
|
|
- maintenance: '● 维修中'
|
|
|
|
|
- }
|
|
|
|
|
- return map[status] || status
|
|
|
|
|
|
|
+function loadAssistData() {
|
|
|
|
|
+ return Promise.all([listEquipmentTypeAll(), listEquipmentStatusAll()]).then(([typeRes, statusRes]) => {
|
|
|
|
|
+ equipmentTypeOptions.value = typeRes.data || [];
|
|
|
|
|
+ equipmentStatusList.value = statusRes.data || [];
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 搜索操作
|
|
|
|
|
-const handleSearch = () => {
|
|
|
|
|
- currentPage.value = 1
|
|
|
|
|
|
|
+function reset() {
|
|
|
|
|
+ form.value = createEmptyForm();
|
|
|
|
|
+ proxy.resetForm("equipmentRef");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const handleReset = () => {
|
|
|
|
|
- searchParams.value = { name: '', type: '', level: '', unit: '' }
|
|
|
|
|
- currentPage.value = 1
|
|
|
|
|
|
|
+function handleQuery() {
|
|
|
|
|
+ queryParams.value.pageNum = 1;
|
|
|
|
|
+ getList();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 查看详情
|
|
|
|
|
-const viewDetail = (item) => {
|
|
|
|
|
- currentItem.value = item
|
|
|
|
|
- detailModalVisible.value = true
|
|
|
|
|
|
|
+function resetQuery() {
|
|
|
|
|
+ proxy.resetForm("queryRef");
|
|
|
|
|
+ handleQuery();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const closeDetailModal = () => {
|
|
|
|
|
- detailModalVisible.value = false
|
|
|
|
|
- currentItem.value = null
|
|
|
|
|
|
|
+function handleSelectionChange(selection) {
|
|
|
|
|
+ ids.value = selection.map((item) => item.equipmentId);
|
|
|
|
|
+ single.value = selection.length !== 1;
|
|
|
|
|
+ multiple.value = !selection.length;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const openEditFromDetail = () => {
|
|
|
|
|
- closeDetailModal()
|
|
|
|
|
- openEditModal(currentItem.value)
|
|
|
|
|
|
|
+function handleAdd() {
|
|
|
|
|
+ reset();
|
|
|
|
|
+ open.value = true;
|
|
|
|
|
+ title.value = "新增物联感知设备";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 新增/编辑
|
|
|
|
|
-const openAddModal = () => {
|
|
|
|
|
- isEdit.value = false
|
|
|
|
|
- formData.value = {
|
|
|
|
|
- name: '', type: '管网路线', level: '', code: '',
|
|
|
|
|
- maintenanceUnit: '', location: '', buildYear: '', designLife: '',
|
|
|
|
|
- lastInspection: '', status: 'normal', remark: ''
|
|
|
|
|
|
|
+function handleUpdate(row) {
|
|
|
|
|
+ reset();
|
|
|
|
|
+ const equipmentId = row?.equipmentId || ids.value[0];
|
|
|
|
|
+ if (!equipmentId) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
- editModalVisible.value = true
|
|
|
|
|
|
|
+ getEquipment(equipmentId).then((response) => {
|
|
|
|
|
+ form.value = { ...createEmptyForm(), ...(response.data || {}) };
|
|
|
|
|
+ open.value = true;
|
|
|
|
|
+ title.value = "修改物联感知设备";
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const openEditModal = (item) => {
|
|
|
|
|
- isEdit.value = true
|
|
|
|
|
- formData.value = { ...item }
|
|
|
|
|
- editModalVisible.value = true
|
|
|
|
|
|
|
+function handleView(row) {
|
|
|
|
|
+ getEquipmentDetail(row.equipmentId).then((response) => {
|
|
|
|
|
+ detailForm.value = response.data || {};
|
|
|
|
|
+ detailOpen.value = true;
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const closeEditModal = () => {
|
|
|
|
|
- editModalVisible.value = false
|
|
|
|
|
|
|
+function cancel() {
|
|
|
|
|
+ open.value = false;
|
|
|
|
|
+ reset();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const saveItem = () => {
|
|
|
|
|
- if (!formData.value.name || !formData.value.type || !formData.value.code) {
|
|
|
|
|
- alert("请填写设施名称、类型和编码")
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 检查编码唯一性(编辑时排除自身)
|
|
|
|
|
- const exists = facilities.value.some(f =>
|
|
|
|
|
- f.code === formData.value.code && f.id !== formData.value.id
|
|
|
|
|
- )
|
|
|
|
|
- if (exists) {
|
|
|
|
|
- alert("设施编码已存在,请使用唯一编码")
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (isEdit.value) {
|
|
|
|
|
- const index = facilities.value.findIndex(f => f.id === formData.value.id)
|
|
|
|
|
- if (index !== -1) {
|
|
|
|
|
- facilities.value[index] = { ...formData.value }
|
|
|
|
|
|
|
+function submitForm() {
|
|
|
|
|
+ proxy.$refs.equipmentRef.validate((valid) => {
|
|
|
|
|
+ if (!valid) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
- alert("设施信息已更新")
|
|
|
|
|
- } else {
|
|
|
|
|
- const newId = Math.max(...facilities.value.map(f => f.id), 0) + 1
|
|
|
|
|
- facilities.value.push({ ...formData.value, id: newId })
|
|
|
|
|
- alert("设施已添加")
|
|
|
|
|
|
|
+ const request = form.value.equipmentId ? updateEquipment(form.value) : addEquipment(form.value);
|
|
|
|
|
+ request.then(() => {
|
|
|
|
|
+ proxy.$modal.msgSuccess(form.value.equipmentId ? "修改成功" : "新增成功");
|
|
|
|
|
+ open.value = false;
|
|
|
|
|
+ Promise.all([getList(), loadAssistData()]);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleDelete(row) {
|
|
|
|
|
+ const deleteIds = row?.equipmentId ? [row.equipmentId] : ids.value;
|
|
|
|
|
+ if (!deleteIds.length) {
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
- closeEditModal()
|
|
|
|
|
- handleReset()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// 删除
|
|
|
|
|
-const deleteItem = (item) => {
|
|
|
|
|
- if (confirm(`确定删除设施 "${item.name}" 吗?`)) {
|
|
|
|
|
- const index = facilities.value.findIndex(f => f.id === item.id)
|
|
|
|
|
- if (index !== -1) {
|
|
|
|
|
- facilities.value.splice(index, 1)
|
|
|
|
|
|
|
+ proxy.$modal
|
|
|
|
|
+ .confirm(`是否确认删除选中的 ${deleteIds.length} 台设备?`)
|
|
|
|
|
+ .then(() => Promise.all(deleteIds.map((id) => delEquipment(id))))
|
|
|
|
|
+ .then(() => {
|
|
|
|
|
+ proxy.$modal.msgSuccess("删除成功");
|
|
|
|
|
+ Promise.all([getList(), loadAssistData()]);
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function openStatusDialog(row) {
|
|
|
|
|
+ currentRow.value = row;
|
|
|
|
|
+ statusForm.value.currentStatus = getRowStatus(row.equipmentId) || 1;
|
|
|
|
|
+ statusOpen.value = true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function submitStatus() {
|
|
|
|
|
+ updateEquipmentStatus(currentRow.value.equipmentId, statusForm.value.currentStatus).then(() => {
|
|
|
|
|
+ proxy.$modal.msgSuccess("状态维护成功");
|
|
|
|
|
+ statusOpen.value = false;
|
|
|
|
|
+ loadAssistData();
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function openTestDialog(row) {
|
|
|
|
|
+ currentRow.value = row;
|
|
|
|
|
+ testForm.value = {
|
|
|
|
|
+ testType: "通信测试",
|
|
|
|
|
+ testDetail: undefined,
|
|
|
|
|
+ testUser: undefined
|
|
|
|
|
+ };
|
|
|
|
|
+ testOpen.value = true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function submitTest() {
|
|
|
|
|
+ testEquipment(currentRow.value.equipmentId, testForm.value).then(() => {
|
|
|
|
|
+ proxy.$modal.msgSuccess("测试记录已提交");
|
|
|
|
|
+ testOpen.value = false;
|
|
|
|
|
+ if (detailOpen.value && detailForm.value?.equipmentInfo?.equipmentId === currentRow.value.equipmentId) {
|
|
|
|
|
+ handleView(currentRow.value);
|
|
|
}
|
|
}
|
|
|
- handleReset()
|
|
|
|
|
- alert("设施已删除")
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 导入导出
|
|
|
|
|
-const handleImport = () => {
|
|
|
|
|
- alert("📂 导入功能\n支持 Excel/CSV 格式导入管网基础设施数据。\n请准备包含:名称、类型、等级、编码、管养单位等列的文件。")
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const handleExport = () => {
|
|
|
|
|
- const headers = ['设施名称', '设施类型', '设施等级', '设施编码', '管养单位', '具体位置', '建设年份', '设计年限', '上次巡检', '设施状态', '备注']
|
|
|
|
|
- const rows = filteredList.value.map(f => [
|
|
|
|
|
- f.name, f.type, f.level || '', f.code, f.maintenanceUnit || '', f.location || '',
|
|
|
|
|
- f.buildYear || '', f.designLife || '', f.lastInspection || '', f.status, f.remark || ''
|
|
|
|
|
- ])
|
|
|
|
|
-
|
|
|
|
|
- const csvContent = [headers, ...rows].map(row => row.join(',')).join('\n')
|
|
|
|
|
- const blob = new Blob(["\uFEFF" + csvContent], { type: 'text/csv;charset=utf-8;' })
|
|
|
|
|
- const link = document.createElement('a')
|
|
|
|
|
- link.href = URL.createObjectURL(blob)
|
|
|
|
|
- link.download = `管网基础设施_${new Date().toISOString().slice(0,10)}.csv`
|
|
|
|
|
- link.click()
|
|
|
|
|
- URL.revokeObjectURL(link.href)
|
|
|
|
|
- alert("导出成功!")
|
|
|
|
|
-}
|
|
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ reset();
|
|
|
|
|
+ Promise.all([getList(), loadAssistData()]);
|
|
|
|
|
+});
|
|
|
</script>
|
|
</script>
|
|
|
-
|
|
|
|
|
-<style scoped>
|
|
|
|
|
-* {
|
|
|
|
|
- margin: 0;
|
|
|
|
|
- padding: 0;
|
|
|
|
|
- box-sizing: border-box;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.prototype-container {
|
|
|
|
|
- max-width: 1600px;
|
|
|
|
|
- margin: 0 auto;
|
|
|
|
|
- background: #ffffff;
|
|
|
|
|
- border-radius: 28px;
|
|
|
|
|
- box-shadow: 0 20px 35px -12px rgba(0, 0, 0, 0.15);
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 头部 */
|
|
|
|
|
-.header {
|
|
|
|
|
- background: #0a2b3c;
|
|
|
|
|
- padding: 18px 28px;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
- gap: 12px;
|
|
|
|
|
- border-bottom: 3px solid #2ecc71;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.logo h2 {
|
|
|
|
|
- color: white;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- font-size: 1.4rem;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.logo p {
|
|
|
|
|
- color: #bdd8e8;
|
|
|
|
|
- font-size: 0.7rem;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.badge-stats {
|
|
|
|
|
- background: #2ecc71;
|
|
|
|
|
- padding: 6px 18px;
|
|
|
|
|
- border-radius: 30px;
|
|
|
|
|
- color: white;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- font-size: 0.8rem;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 主体布局 */
|
|
|
|
|
-.main-layout {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.left-panel {
|
|
|
|
|
- width: 320px;
|
|
|
|
|
- background: #f9fbfd;
|
|
|
|
|
- border-right: 1px solid #e2edf2;
|
|
|
|
|
- padding: 20px 18px;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- gap: 20px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.right-panel {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- background: #ffffff;
|
|
|
|
|
- padding: 20px 24px;
|
|
|
|
|
- min-width: 600px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 卡片 */
|
|
|
|
|
-.card {
|
|
|
|
|
- background: white;
|
|
|
|
|
- border-radius: 20px;
|
|
|
|
|
- border: 1px solid #e6edf3;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.card-header {
|
|
|
|
|
- background: #f9fafc;
|
|
|
|
|
- padding: 14px 18px;
|
|
|
|
|
- border-bottom: 1px solid #eef2f6;
|
|
|
|
|
- font-weight: 700;
|
|
|
|
|
- font-size: 0.9rem;
|
|
|
|
|
- color: #1d5a78;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.card-body {
|
|
|
|
|
- padding: 16px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 表单 */
|
|
|
|
|
-.form-group {
|
|
|
|
|
- margin-bottom: 16px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.form-label {
|
|
|
|
|
- font-size: 0.7rem;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- margin-bottom: 6px;
|
|
|
|
|
- display: block;
|
|
|
|
|
- color: #2c5a72;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.form-input, .form-select, .form-textarea {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- padding: 8px 12px;
|
|
|
|
|
- border-radius: 12px;
|
|
|
|
|
- border: 1px solid #cbdde6;
|
|
|
|
|
- font-size: 0.75rem;
|
|
|
|
|
- background: white;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.form-row {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- gap: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.form-row .form-group {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.search-group {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- margin-top: 8px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 按钮 */
|
|
|
|
|
-.btn-primary {
|
|
|
|
|
- background: #2ecc71;
|
|
|
|
|
- border: none;
|
|
|
|
|
- color: white;
|
|
|
|
|
- padding: 8px 16px;
|
|
|
|
|
- border-radius: 30px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- font-size: 0.75rem;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
- transition: 0.2s;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.btn-primary:hover {
|
|
|
|
|
- background: #27ae60;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.btn-outline {
|
|
|
|
|
- background: white;
|
|
|
|
|
- border: 1px solid #2ecc71;
|
|
|
|
|
- color: #2ecc71;
|
|
|
|
|
- padding: 6px 12px;
|
|
|
|
|
- border-radius: 30px;
|
|
|
|
|
- font-size: 0.7rem;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.btn-sm {
|
|
|
|
|
- padding: 6px 12px;
|
|
|
|
|
- font-size: 0.7rem;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.header-actions {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 统计 */
|
|
|
|
|
-.stat-item {
|
|
|
|
|
- margin-bottom: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.stat-label {
|
|
|
|
|
- font-size: 0.7rem;
|
|
|
|
|
- color: #2c5a72;
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- width: 70px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.stat-value {
|
|
|
|
|
- font-size: 0.75rem;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- color: #2ecc71;
|
|
|
|
|
- margin-left: 8px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.stat-bar {
|
|
|
|
|
- margin-top: 4px;
|
|
|
|
|
- background: #e2e9ef;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- height: 6px;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.stat-fill {
|
|
|
|
|
- background: #2ecc71;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- width: 0%;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 表格 */
|
|
|
|
|
-.table-wrapper {
|
|
|
|
|
- overflow-x: auto;
|
|
|
|
|
- border-radius: 16px;
|
|
|
|
|
- border: 1px solid #eef2f6;
|
|
|
|
|
- margin-bottom: 20px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.data-table {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- border-collapse: collapse;
|
|
|
|
|
- font-size: 0.7rem;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.data-table th {
|
|
|
|
|
- background: #f8fafc;
|
|
|
|
|
- padding: 10px 8px;
|
|
|
|
|
- text-align: left;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- color: #2c5a72;
|
|
|
|
|
- border-bottom: 1px solid #e2edf2;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.data-table td {
|
|
|
|
|
- padding: 8px;
|
|
|
|
|
- border-bottom: 1px solid #f0f4f7;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.data-table tr:hover {
|
|
|
|
|
- background: #fafeff;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 类型标签 */
|
|
|
|
|
-.type-badge {
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- padding: 2px 8px;
|
|
|
|
|
- border-radius: 20px;
|
|
|
|
|
- font-size: 0.65rem;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.type-pipeline { background: #e8f4fd; color: #2980b9; }
|
|
|
|
|
-.type-gas { background: #fef3e2; color: #e67e22; }
|
|
|
|
|
-.type-drain { background: #e0f2f1; color: #16a085; }
|
|
|
|
|
-.type-water { background: #e3f2fd; color: #2196f3; }
|
|
|
|
|
-.type-bridge { background: #f3e5f5; color: #9b59b6; }
|
|
|
|
|
-
|
|
|
|
|
-/* 状态标签 */
|
|
|
|
|
-.status-badge {
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- padding: 2px 8px;
|
|
|
|
|
- border-radius: 20px;
|
|
|
|
|
- font-size: 0.65rem;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.status-normal { background: #d5f5e3; color: #27ae60; }
|
|
|
|
|
-.status-warning { background: #fdebd0; color: #e67e22; }
|
|
|
|
|
-.status-danger { background: #fadbd8; color: #e74c3c; }
|
|
|
|
|
-.status-maintenance { background: #e8daef; color: #8e44ad; }
|
|
|
|
|
-
|
|
|
|
|
-/* 分页 */
|
|
|
|
|
-.pagination {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: flex-end;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- margin-top: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.page-btn {
|
|
|
|
|
- background: white;
|
|
|
|
|
- border: 1px solid #cbdde6;
|
|
|
|
|
- padding: 4px 10px;
|
|
|
|
|
- border-radius: 20px;
|
|
|
|
|
- font-size: 0.7rem;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.page-btn.active {
|
|
|
|
|
- background: #2ecc71;
|
|
|
|
|
- color: white;
|
|
|
|
|
- border-color: #2ecc71;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 模态框 */
|
|
|
|
|
-.modal-overlay {
|
|
|
|
|
- position: fixed;
|
|
|
|
|
- top: 0;
|
|
|
|
|
- left: 0;
|
|
|
|
|
- right: 0;
|
|
|
|
|
- bottom: 0;
|
|
|
|
|
- background: rgba(0, 0, 0, 0.5);
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- z-index: 1000;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.modal-content {
|
|
|
|
|
- background: white;
|
|
|
|
|
- border-radius: 24px;
|
|
|
|
|
- width: 500px;
|
|
|
|
|
- max-width: 90%;
|
|
|
|
|
- padding: 24px;
|
|
|
|
|
- max-height: 85vh;
|
|
|
|
|
- overflow-y: auto;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.detail-row {
|
|
|
|
|
- margin-bottom: 12px;
|
|
|
|
|
- font-size: 0.75rem;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.detail-label {
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- width: 100px;
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- color: #2c5a72;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.action-icons {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.icon-btn {
|
|
|
|
|
- background: none;
|
|
|
|
|
- border: none;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
- font-size: 1rem;
|
|
|
|
|
- padding: 4px;
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.icon-btn:hover {
|
|
|
|
|
- background: #eef2f5;
|
|
|
|
|
-}
|
|
|
|
|
-</style>
|
|
|