| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735 |
- <!-- 管网基础信息管理 -->
- <template>
- <div class="app-container pipe-network-page">
- <div class="summary-grid">
- <el-card shadow="hover" class="summary-card primary">
- <div class="summary-label">设施总数</div>
- <div class="summary-value">{{ summary.totalNetworks }}</div>
- <div class="summary-desc">已纳入平台的管网基础设施</div>
- </el-card>
- <el-card shadow="hover" class="summary-card success">
- <div class="summary-label">正常运行</div>
- <div class="summary-value">{{ summary.activeNetworks }}</div>
- <div class="summary-desc">状态为正常或启用的设施</div>
- </el-card>
- <el-card shadow="hover" class="summary-card warning">
- <div class="summary-label">设施类型</div>
- <div class="summary-value">{{ summary.typeCount }}</div>
- <div class="summary-desc">当前已覆盖的设施分类数</div>
- </el-card>
- <el-card shadow="hover" class="summary-card info">
- <div class="summary-label">关联点位</div>
- <div class="summary-value">{{ summary.totalPoints }}</div>
- <div class="summary-desc">关键监控点位总量</div>
- </el-card>
- </div>
- <el-card shadow="never" class="query-card">
- <el-form ref="queryRef" :model="queryParams" :inline="true" v-show="showSearch">
- <el-form-item label="设施编码" prop="networkCode">
- <el-input
- v-model="queryParams.networkCode"
- placeholder="请输入设施编码"
- clearable
- style="width: 220px"
- @keyup.enter="handleQuery"
- />
- </el-form-item>
- <el-form-item label="设施名称" prop="networkName">
- <el-input
- v-model="queryParams.networkName"
- placeholder="请输入设施名称"
- clearable
- style="width: 220px"
- @keyup.enter="handleQuery"
- />
- </el-form-item>
- <el-form-item label="设施类型" prop="networkType">
- <el-select
- v-model="queryParams.networkType"
- placeholder="请选择设施类型"
- clearable
- filterable
- allow-create
- default-first-option
- style="width: 220px"
- >
- <el-option v-for="item in networkTypeOptions" :key="item" :label="item" :value="item" />
- </el-select>
- </el-form-item>
- <el-form-item label="状态" prop="status">
- <el-select
- v-model="queryParams.status"
- placeholder="请选择状态"
- clearable
- filterable
- allow-create
- default-first-option
- style="width: 180px"
- >
- <el-option v-for="item in statusOptions" :key="item" :label="item" :value="item" />
- </el-select>
- </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-card>
- <el-row :gutter="16" class="main-row">
- <el-col :xs="24" :lg="15">
- <el-card shadow="never" class="table-card">
- <template #header>
- <div class="card-header-row">
- <div>
- <div class="card-title">设施台账</div>
- <div class="card-subtitle">支持查询、新增、编辑、删除以及设施与点位联动查看</div>
- </div>
- <div class="card-actions">
- <el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
- <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">修改</el-button>
- <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">删除</el-button>
- <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
- </div>
- </div>
- </template>
- <el-table
- v-loading="loading"
- :data="networkList"
- highlight-current-row
- @selection-change="handleSelectionChange"
- @current-change="handleCurrentChange"
- @row-click="handleRowClick"
- >
- <el-table-column type="selection" width="55" align="center" />
- <el-table-column label="设施编码" align="center" prop="networkCode" min-width="130" />
- <el-table-column label="设施名称" align="center" prop="networkName" min-width="180" show-overflow-tooltip />
- <el-table-column label="设施类型" align="center" prop="networkType" min-width="120" />
- <el-table-column label="等级" align="center" prop="networkLevel" width="90" />
- <el-table-column label="管养单位" align="center" prop="managementUnit" min-width="150" show-overflow-tooltip />
- <el-table-column label="状态" align="center" width="100">
- <template #default="scope">
- <el-tag :type="getStatusTagType(scope.row.status)">{{ scope.row.status || "-" }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="操作" align="center" width="170" class-name="small-padding fixed-width">
- <template #default="scope">
- <el-button link type="primary" icon="View" @click.stop="selectNetwork(scope.row)">详情</el-button>
- <el-button link type="primary" icon="Edit" @click.stop="handleUpdate(scope.row)">修改</el-button>
- <el-button link type="danger" icon="Delete" @click.stop="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-card>
- </el-col>
- <el-col :xs="24" :lg="9">
- <el-card shadow="never" class="detail-card">
- <template #header>
- <div class="card-header-row">
- <div>
- <div class="card-title">设施详情</div>
- <div class="card-subtitle">选中设施后可查看基础信息及下挂监测点位</div>
- </div>
- <el-tag v-if="selectedNetwork.networkId" :type="getStatusTagType(selectedNetwork.status)">
- {{ selectedNetwork.status || "未设置" }}
- </el-tag>
- </div>
- </template>
- <el-empty v-if="!selectedNetwork.networkId" description="请从左侧选择一条管网设施" />
- <template v-else>
- <div class="detail-hero">
- <div class="hero-title">{{ selectedNetwork.networkName }}</div>
- <div class="hero-meta">
- <span>{{ selectedNetwork.networkCode }}</span>
- <span>{{ selectedNetwork.networkType || "未分类" }}</span>
- <span>{{ selectedNetwork.managementUnit || "未维护管养单位" }}</span>
- </div>
- </div>
- <el-descriptions :column="1" border size="small" class="detail-descriptions">
- <el-descriptions-item label="设施等级">{{ selectedNetwork.networkLevel || "-" }}</el-descriptions-item>
- <el-descriptions-item label="位置描述">{{ selectedNetwork.location || "-" }}</el-descriptions-item>
- <el-descriptions-item label="经纬度">
- {{ selectedNetwork.longitude ?? "-" }} / {{ selectedNetwork.latitude ?? "-" }}
- </el-descriptions-item>
- <el-descriptions-item label="长度/管径">
- {{ selectedNetwork.length ?? "-" }} m / {{ selectedNetwork.diameter ?? "-" }} mm
- </el-descriptions-item>
- <el-descriptions-item label="材质">{{ selectedNetwork.material || "-" }}</el-descriptions-item>
- <el-descriptions-item label="建设日期">{{ selectedNetwork.buildDate || "-" }}</el-descriptions-item>
- <el-descriptions-item label="备注">{{ selectedNetwork.remark || "-" }}</el-descriptions-item>
- </el-descriptions>
- <div class="section-block">
- <div class="section-title">关联监测点位</div>
- <div class="section-tip">
- 共 {{ relatedPoints.length }} 个点位,异常/需关注 {{ abnormalPointCount }} 个
- </div>
- <el-table v-loading="pointLoading" :data="relatedPoints" size="small" max-height="300">
- <el-table-column label="点位名称" prop="pointName" min-width="140" show-overflow-tooltip />
- <el-table-column label="类型" prop="pointType" min-width="100" />
- <el-table-column label="状态" width="90">
- <template #default="scope">
- <el-tag :type="getStatusTagType(scope.row.status)" size="small">
- {{ scope.row.status || "-" }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="安装日期" prop="installationDate" width="110" />
- </el-table>
- </div>
- <div class="side-actions">
- <el-button type="primary" icon="Edit" @click="handleUpdate(selectedNetwork)">编辑设施</el-button>
- <el-button type="danger" plain icon="Delete" @click="handleDelete(selectedNetwork)">删除设施</el-button>
- </div>
- </template>
- </el-card>
- </el-col>
- </el-row>
- <el-dialog :title="title" v-model="open" width="760px" append-to-body>
- <el-form ref="networkRef" :model="form" :rules="rules" label-width="110px">
- <el-row :gutter="16">
- <el-col :span="12">
- <el-form-item label="设施编码" prop="networkCode">
- <el-input v-model="form.networkCode" placeholder="请输入设施编码" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="设施名称" prop="networkName">
- <el-input v-model="form.networkName" placeholder="请输入设施名称" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="设施类型" prop="networkType">
- <el-select
- v-model="form.networkType"
- placeholder="请选择或输入设施类型"
- filterable
- allow-create
- default-first-option
- style="width: 100%"
- >
- <el-option v-for="item in networkTypeOptions" :key="item" :label="item" :value="item" />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="设施等级" prop="networkLevel">
- <el-input v-model="form.networkLevel" placeholder="请输入设施等级" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="管养单位" prop="managementUnit">
- <el-input v-model="form.managementUnit" placeholder="请输入管养单位" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="状态" prop="status">
- <el-select
- v-model="form.status"
- placeholder="请选择或输入状态"
- filterable
- allow-create
- default-first-option
- style="width: 100%"
- >
- <el-option v-for="item in statusOptions" :key="item" :label="item" :value="item" />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="24">
- <el-form-item label="位置描述" prop="location">
- <el-input v-model="form.location" 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="length">
- <el-input-number v-model="form.length" :precision="2" :step="1" style="width: 100%" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="管径(mm)" prop="diameter">
- <el-input-number v-model="form.diameter" :precision="2" :step="1" style="width: 100%" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="材质" prop="material">
- <el-input v-model="form.material" placeholder="请输入材质" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="建设日期" prop="buildDate">
- <el-date-picker
- v-model="form.buildDate"
- 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="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>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup>
- import { computed, getCurrentInstance, onMounted, reactive, ref, toRefs } from "vue";
- import {
- addPipeNetwork,
- delPipeNetwork,
- getPipeNetwork,
- listMonitorPointAll,
- listPipeNetwork,
- listPipeNetworkAll,
- listPointsByNetwork,
- updatePipeNetwork
- } from "@/api/pipeNetwork/pdeviceInform";
- const { proxy } = getCurrentInstance();
- const networkList = ref([]);
- const relatedPoints = ref([]);
- const loading = ref(false);
- const pointLoading = ref(false);
- const open = 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 selectedNetwork = ref({});
- const networkTypeOptions = ["管网路线", "燃气设施", "排水设施", "供水设施", "桥梁"];
- const statusOptions = ["正常", "启用", "停用", "维修中", "预警"];
- const summary = reactive({
- totalNetworks: 0,
- activeNetworks: 0,
- typeCount: 0,
- totalPoints: 0
- });
- const abnormalPointCount = computed(() => {
- return relatedPoints.value.filter((item) => !["正常", "启用"].includes(item.status)).length;
- });
- const data = reactive({
- queryParams: {
- pageNum: 1,
- pageSize: 10,
- networkCode: undefined,
- networkName: undefined,
- networkType: undefined,
- status: undefined
- },
- form: {},
- rules: {
- networkCode: [{ required: true, message: "设施编码不能为空", trigger: "blur" }],
- networkName: [{ required: true, message: "设施名称不能为空", trigger: "blur" }],
- networkType: [{ required: true, message: "设施类型不能为空", trigger: "change" }]
- }
- });
- const { queryParams, form, rules } = toRefs(data);
- function createEmptyForm() {
- return {
- networkId: undefined,
- networkCode: undefined,
- networkName: undefined,
- networkType: undefined,
- networkLevel: undefined,
- managementUnit: undefined,
- location: undefined,
- longitude: undefined,
- latitude: undefined,
- length: undefined,
- diameter: undefined,
- material: undefined,
- buildDate: undefined,
- status: "正常",
- remark: undefined
- };
- }
- function getStatusTagType(status) {
- if (["正常", "启用"].includes(status)) {
- return "success";
- }
- if (status === "预警") {
- return "warning";
- }
- if (["停用", "禁用"].includes(status)) {
- return "info";
- }
- if (["故障", "异常"].includes(status)) {
- return "danger";
- }
- return "";
- }
- function loadRelatedPoints(networkId) {
- if (!networkId) {
- relatedPoints.value = [];
- return Promise.resolve();
- }
- pointLoading.value = true;
- return listPointsByNetwork(networkId)
- .then((response) => {
- relatedPoints.value = response.data || [];
- })
- .finally(() => {
- pointLoading.value = false;
- });
- }
- function selectNetwork(row) {
- if (!row?.networkId) {
- return;
- }
- selectedNetwork.value = row;
- return loadRelatedPoints(row.networkId);
- }
- function loadSummary() {
- return Promise.all([listPipeNetworkAll(), listMonitorPointAll()]).then(([networkRes, pointRes]) => {
- const allNetworks = networkRes.data || [];
- const allPoints = pointRes.data || [];
- summary.totalNetworks = allNetworks.length;
- summary.activeNetworks = allNetworks.filter((item) => ["正常", "启用"].includes(item.status)).length;
- summary.typeCount = new Set(allNetworks.map((item) => item.networkType).filter(Boolean)).size;
- summary.totalPoints = allPoints.length;
- if (selectedNetwork.value.networkId) {
- const refreshed = allNetworks.find((item) => item.networkId === selectedNetwork.value.networkId);
- if (refreshed) {
- selectedNetwork.value = refreshed;
- }
- } else if (allNetworks.length) {
- selectedNetwork.value = allNetworks[0];
- }
- });
- }
- function getList() {
- loading.value = true;
- return listPipeNetwork(queryParams.value)
- .then((response) => {
- networkList.value = response.records || [];
- total.value = response.total || 0;
- if (!selectedNetwork.value.networkId && networkList.value.length) {
- return selectNetwork(networkList.value[0]);
- }
- return undefined;
- })
- .finally(() => {
- loading.value = false;
- });
- }
- function reset() {
- form.value = createEmptyForm();
- proxy.resetForm("networkRef");
- }
- function handleQuery() {
- queryParams.value.pageNum = 1;
- getList();
- }
- function resetQuery() {
- proxy.resetForm("queryRef");
- handleQuery();
- }
- function handleSelectionChange(selection) {
- ids.value = selection.map((item) => item.networkId);
- single.value = selection.length !== 1;
- multiple.value = !selection.length;
- }
- function handleCurrentChange(currentRow) {
- if (currentRow?.networkId) {
- selectNetwork(currentRow);
- }
- }
- function handleRowClick(row) {
- selectNetwork(row);
- }
- function handleAdd() {
- reset();
- open.value = true;
- title.value = "新增管网基础信息";
- }
- function handleUpdate(row) {
- reset();
- const networkId = row?.networkId || ids.value[0];
- if (!networkId) {
- return;
- }
- getPipeNetwork(networkId).then((response) => {
- form.value = { ...createEmptyForm(), ...(response.data || {}) };
- open.value = true;
- title.value = "修改管网基础信息";
- });
- }
- function cancel() {
- open.value = false;
- reset();
- }
- function submitForm() {
- proxy.$refs.networkRef.validate((valid) => {
- if (!valid) {
- return;
- }
- const request = form.value.networkId ? updatePipeNetwork(form.value) : addPipeNetwork(form.value);
- request.then(() => {
- proxy.$modal.msgSuccess(form.value.networkId ? "修改成功" : "新增成功");
- open.value = false;
- Promise.all([getList(), loadSummary()]).then(() => {
- if (form.value.networkId) {
- getPipeNetwork(form.value.networkId).then((response) => {
- selectedNetwork.value = response.data || {};
- loadRelatedPoints(selectedNetwork.value.networkId);
- });
- }
- });
- });
- });
- }
- function handleDelete(row) {
- const deleteIds = row?.networkId ? [row.networkId] : ids.value;
- if (!deleteIds.length) {
- return;
- }
- proxy.$modal
- .confirm(`是否确认删除选中的 ${deleteIds.length} 条管网设施数据?`)
- .then(() => Promise.all(deleteIds.map((id) => delPipeNetwork(id))))
- .then(() => {
- proxy.$modal.msgSuccess("删除成功");
- if (deleteIds.includes(selectedNetwork.value.networkId)) {
- selectedNetwork.value = {};
- relatedPoints.value = [];
- }
- return Promise.all([getList(), loadSummary()]);
- });
- }
- onMounted(() => {
- reset();
- Promise.all([getList(), loadSummary()]).then(() => {
- if (selectedNetwork.value.networkId) {
- loadRelatedPoints(selectedNetwork.value.networkId);
- }
- });
- });
- </script>
- <style scoped>
- .pipe-network-page {
- background: #f5f8fc;
- }
- .summary-grid {
- display: grid;
- grid-template-columns: repeat(4, minmax(0, 1fr));
- gap: 16px;
- margin-bottom: 16px;
- }
- .summary-card {
- border: none;
- color: #fff;
- }
- .summary-card :deep(.el-card__body) {
- padding: 20px;
- }
- .summary-card.primary {
- background: linear-gradient(135deg, #2b73ff, #5da2ff);
- }
- .summary-card.success {
- background: linear-gradient(135deg, #16a34a, #41c878);
- }
- .summary-card.warning {
- background: linear-gradient(135deg, #f59e0b, #f8bf52);
- }
- .summary-card.info {
- background: linear-gradient(135deg, #0f766e, #2bb2a6);
- }
- .summary-label {
- font-size: 14px;
- opacity: 0.9;
- }
- .summary-value {
- margin: 8px 0 10px;
- font-size: 30px;
- font-weight: 700;
- line-height: 1;
- }
- .summary-desc {
- font-size: 12px;
- opacity: 0.9;
- }
- .query-card,
- .table-card,
- .detail-card {
- border-radius: 16px;
- }
- .query-card {
- margin-bottom: 16px;
- }
- .main-row {
- align-items: stretch;
- }
- .card-header-row {
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 12px;
- }
- .card-title {
- font-size: 16px;
- font-weight: 600;
- color: #1f2937;
- }
- .card-subtitle {
- margin-top: 4px;
- font-size: 12px;
- color: #6b7280;
- }
- .card-actions {
- display: flex;
- align-items: center;
- gap: 8px;
- flex-wrap: wrap;
- }
- .detail-hero {
- margin-bottom: 16px;
- padding: 16px;
- border-radius: 14px;
- background: linear-gradient(135deg, #eff6ff, #f8fbff);
- border: 1px solid #dbeafe;
- }
- .hero-title {
- font-size: 18px;
- font-weight: 700;
- color: #1d4ed8;
- }
- .hero-meta {
- display: flex;
- gap: 10px;
- flex-wrap: wrap;
- margin-top: 8px;
- font-size: 12px;
- color: #4b5563;
- }
- .detail-descriptions {
- margin-bottom: 16px;
- }
- .section-block {
- margin-top: 12px;
- }
- .section-title {
- font-size: 15px;
- font-weight: 600;
- color: #1f2937;
- }
- .section-tip {
- margin: 6px 0 12px;
- font-size: 12px;
- color: #6b7280;
- }
- .side-actions {
- display: flex;
- gap: 10px;
- margin-top: 16px;
- }
- @media (max-width: 1200px) {
- .summary-grid {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- }
- }
- @media (max-width: 768px) {
- .summary-grid {
- grid-template-columns: 1fr;
- }
- .card-header-row {
- align-items: flex-start;
- flex-direction: column;
- }
- .side-actions {
- flex-direction: column;
- }
- }
- </style>
|