Pbasicinfo.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. <!-- 管网基础信息管理 -->
  2. <template>
  3. <div class="app-container pipe-network-page">
  4. <div class="summary-grid">
  5. <el-card shadow="hover" class="summary-card primary">
  6. <div class="summary-label">设施总数</div>
  7. <div class="summary-value">{{ summary.totalNetworks }}</div>
  8. <div class="summary-desc">已纳入平台的管网基础设施</div>
  9. </el-card>
  10. <el-card shadow="hover" class="summary-card success">
  11. <div class="summary-label">正常运行</div>
  12. <div class="summary-value">{{ summary.activeNetworks }}</div>
  13. <div class="summary-desc">状态为正常的设施</div>
  14. </el-card>
  15. <el-card shadow="hover" class="summary-card warning">
  16. <div class="summary-label">设施类型</div>
  17. <div class="summary-value">{{ summary.typeCount }}</div>
  18. <div class="summary-desc">当前已覆盖的设施分类数</div>
  19. </el-card>
  20. <el-card shadow="hover" class="summary-card info">
  21. <div class="summary-label">关联点位</div>
  22. <div class="summary-value">{{ summary.totalPoints }}</div>
  23. <div class="summary-desc">关键监控点位总量</div>
  24. </el-card>
  25. </div>
  26. <el-card shadow="never" class="query-card">
  27. <el-form
  28. ref="queryRef"
  29. :model="queryParams"
  30. :inline="true"
  31. v-show="showSearch"
  32. >
  33. <el-form-item label="设施编码" prop="networkCode">
  34. <el-input
  35. v-model="queryParams.networkCode"
  36. placeholder="请输入设施编码"
  37. clearable
  38. style="width: 220px"
  39. @keyup.enter="handleQuery"
  40. />
  41. </el-form-item>
  42. <el-form-item label="设施名称" prop="networkName">
  43. <el-input
  44. v-model="queryParams.networkName"
  45. placeholder="请输入设施名称"
  46. clearable
  47. style="width: 220px"
  48. @keyup.enter="handleQuery"
  49. />
  50. </el-form-item>
  51. <el-form-item label="设施类型" prop="networkType">
  52. <el-select
  53. v-model="queryParams.networkType"
  54. placeholder="请选择设施类型"
  55. clearable
  56. filterable
  57. allow-create
  58. default-first-option
  59. style="width: 220px"
  60. >
  61. <el-option
  62. v-for="item in networkTypeOptions"
  63. :key="item.value"
  64. :label="item.label"
  65. :value="item.value"
  66. />
  67. </el-select>
  68. </el-form-item>
  69. <el-form-item label="状态" prop="status">
  70. <el-select
  71. v-model="queryParams.status"
  72. placeholder="请选择状态"
  73. clearable
  74. filterable
  75. default-first-option
  76. style="width: 180px"
  77. >
  78. <el-option
  79. v-for="item in statusOptions"
  80. :key="item.value"
  81. :label="item.label"
  82. :value="item.value"
  83. />
  84. </el-select>
  85. </el-form-item>
  86. <el-form-item>
  87. <el-button type="primary" icon="Search" @click="handleQuery"
  88. >搜索</el-button
  89. >
  90. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  91. </el-form-item>
  92. </el-form>
  93. </el-card>
  94. <el-row :gutter="16" class="main-row">
  95. <el-col :xs="24" :lg="15">
  96. <el-card shadow="never" class="table-card">
  97. <template #header>
  98. <div class="card-header-row">
  99. <div>
  100. <div class="card-title">设施台账</div>
  101. <div class="card-subtitle">
  102. 支持查询、新增、编辑、删除以及设施与点位联动查看
  103. </div>
  104. </div>
  105. <div class="card-actions">
  106. <el-button type="primary" plain icon="Plus" @click="handleAdd"
  107. >新增</el-button
  108. >
  109. <el-button
  110. type="danger"
  111. plain
  112. icon="Delete"
  113. :disabled="multiple"
  114. @click="handleDelete()"
  115. >删除</el-button
  116. >
  117. <right-toolbar
  118. v-model:showSearch="showSearch"
  119. @queryTable="getList"
  120. />
  121. </div>
  122. </div>
  123. </template>
  124. <el-table
  125. v-loading="loading"
  126. :data="networkList"
  127. highlight-current-row
  128. @selection-change="handleSelectionChange"
  129. @current-change="handleCurrentChange"
  130. @row-click="handleRowClick"
  131. >
  132. <el-table-column type="selection" width="55" align="center" />
  133. <el-table-column
  134. label="设施编码"
  135. align="center"
  136. prop="networkCode"
  137. min-width="130"
  138. />
  139. <el-table-column
  140. label="设施名称"
  141. align="center"
  142. prop="networkName"
  143. min-width="180"
  144. show-overflow-tooltip
  145. />
  146. <el-table-column label="设施类型" align="center" min-width="120">
  147. <template #default="scope">
  148. {{ getNetworkTypeText(scope.row.networkType) }}
  149. </template>
  150. </el-table-column>
  151. <el-table-column
  152. label="等级"
  153. align="center"
  154. prop="networkLevel"
  155. width="90"
  156. />
  157. <el-table-column
  158. label="管养单位"
  159. align="center"
  160. prop="managementUnit"
  161. min-width="150"
  162. show-overflow-tooltip
  163. />
  164. <el-table-column label="状态" align="center" width="100">
  165. <template #default="scope">
  166. <el-tag :type="getStatusTagType(scope.row.status)">{{
  167. getStatusText(scope.row.status)
  168. }}</el-tag>
  169. </template>
  170. </el-table-column>
  171. <el-table-column
  172. label="操作"
  173. align="center"
  174. width="140"
  175. class-name="small-padding fixed-width"
  176. >
  177. <template #default="scope">
  178. <el-button
  179. link
  180. type="primary"
  181. icon="Edit"
  182. @click.stop="handleUpdate(scope.row)"
  183. >修改</el-button
  184. >
  185. <el-button
  186. link
  187. type="danger"
  188. icon="Delete"
  189. @click.stop="handleDelete(scope.row)"
  190. >删除</el-button
  191. >
  192. </template>
  193. </el-table-column>
  194. </el-table>
  195. <pagination
  196. v-show="total > 0"
  197. :total="total"
  198. v-model:page="queryParams.pageNum"
  199. v-model:limit="queryParams.pageSize"
  200. @pagination="getList"
  201. />
  202. </el-card>
  203. </el-col>
  204. <el-col :xs="24" :lg="9">
  205. <el-card shadow="never" class="detail-card">
  206. <template #header>
  207. <div class="card-header-row">
  208. <div>
  209. <div class="card-title">设施详情</div>
  210. <div class="card-subtitle">
  211. 选中设施后可查看基础信息及下挂监测点位
  212. </div>
  213. </div>
  214. <el-tag
  215. v-if="selectedNetwork.networkId"
  216. :type="getStatusTagType(selectedNetwork.status)"
  217. >
  218. {{ getStatusText(selectedNetwork.status) }}
  219. </el-tag>
  220. </div>
  221. </template>
  222. <el-empty
  223. v-if="!selectedNetwork.networkId"
  224. description="请从左侧选择一条管网设施"
  225. />
  226. <template v-else>
  227. <div class="detail-hero">
  228. <div class="hero-title">{{ selectedNetwork.networkName }}</div>
  229. <div class="hero-meta">
  230. <span>{{ selectedNetwork.networkCode }}</span>
  231. <span>{{
  232. getNetworkTypeText(selectedNetwork.networkType)
  233. }}</span>
  234. <span>{{
  235. selectedNetwork.managementUnit || "未维护管养单位"
  236. }}</span>
  237. </div>
  238. </div>
  239. <el-descriptions
  240. :column="1"
  241. border
  242. size="small"
  243. class="detail-descriptions"
  244. >
  245. <el-descriptions-item label="设施等级">{{
  246. selectedNetwork.networkLevel || "-"
  247. }}</el-descriptions-item>
  248. <el-descriptions-item label="位置描述">{{
  249. selectedNetwork.location || "-"
  250. }}</el-descriptions-item>
  251. <el-descriptions-item label="经纬度">
  252. {{ selectedNetwork.longitude ?? "-" }} /
  253. {{ selectedNetwork.latitude ?? "-" }}
  254. </el-descriptions-item>
  255. <el-descriptions-item label="长度/管径">
  256. {{ selectedNetwork.length ?? "-" }} m /
  257. {{ selectedNetwork.diameter ?? "-" }} mm
  258. </el-descriptions-item>
  259. <el-descriptions-item label="材质">{{
  260. selectedNetwork.material || "-"
  261. }}</el-descriptions-item>
  262. <el-descriptions-item label="建设日期">{{
  263. selectedNetwork.buildDate || "-"
  264. }}</el-descriptions-item>
  265. <el-descriptions-item label="备注">{{
  266. selectedNetwork.remark || "-"
  267. }}</el-descriptions-item>
  268. </el-descriptions>
  269. <div class="section-block">
  270. <div class="section-title">关联监测点位</div>
  271. <div class="section-tip">
  272. 共 {{ relatedPoints.length }} 个点位,异常/需关注
  273. {{ abnormalPointCount }} 个
  274. </div>
  275. <el-table
  276. v-loading="pointLoading"
  277. :data="relatedPoints"
  278. size="small"
  279. max-height="300"
  280. >
  281. <el-table-column
  282. label="点位名称"
  283. prop="pointName"
  284. min-width="140"
  285. show-overflow-tooltip
  286. />
  287. <el-table-column
  288. label="类型"
  289. prop="pointType"
  290. min-width="100"
  291. />
  292. <el-table-column label="状态" width="90">
  293. <template #default="scope">
  294. <el-tag
  295. :type="getStatusTagType(scope.row.status)"
  296. size="small"
  297. >
  298. {{ getStatusText(scope.row.status) }}
  299. </el-tag>
  300. </template>
  301. </el-table-column>
  302. <el-table-column
  303. label="安装日期"
  304. prop="installationDate"
  305. width="110"
  306. />
  307. </el-table>
  308. </div>
  309. </template>
  310. </el-card>
  311. </el-col>
  312. </el-row>
  313. <el-dialog :title="title" v-model="open" width="760px" append-to-body>
  314. <el-form
  315. ref="networkRef"
  316. :model="form"
  317. :rules="rules"
  318. label-width="110px"
  319. >
  320. <el-row :gutter="16">
  321. <el-col :span="12">
  322. <el-form-item label="设施编码" prop="networkCode">
  323. <el-input
  324. v-model="form.networkCode"
  325. placeholder="请输入设施编码"
  326. />
  327. </el-form-item>
  328. </el-col>
  329. <el-col :span="12">
  330. <el-form-item label="设施名称" prop="networkName">
  331. <el-input
  332. v-model="form.networkName"
  333. placeholder="请输入设施名称"
  334. />
  335. </el-form-item>
  336. </el-col>
  337. <el-col :span="12">
  338. <el-form-item label="设施类型" prop="networkType">
  339. <el-select
  340. v-model="form.networkType"
  341. placeholder="请选择设施类型"
  342. filterable
  343. default-first-option
  344. style="width: 100%"
  345. >
  346. <el-option
  347. v-for="item in networkTypeOptions"
  348. :key="item.value"
  349. :label="item.label"
  350. :value="item.value"
  351. />
  352. </el-select>
  353. </el-form-item>
  354. </el-col>
  355. <el-col :span="12">
  356. <el-form-item label="设施等级" prop="networkLevel">
  357. <el-input
  358. v-model="form.networkLevel"
  359. placeholder="请输入设施等级"
  360. />
  361. </el-form-item>
  362. </el-col>
  363. <el-col :span="12">
  364. <el-form-item label="管养单位" prop="managementUnit">
  365. <el-input
  366. v-model="form.managementUnit"
  367. placeholder="请输入管养单位"
  368. />
  369. </el-form-item>
  370. </el-col>
  371. <el-col :span="12">
  372. <el-form-item label="状态" prop="status">
  373. <el-select
  374. v-model="form.status"
  375. placeholder="请选择状态"
  376. filterable
  377. default-first-option
  378. style="width: 100%"
  379. >
  380. <el-option
  381. v-for="item in statusOptions"
  382. :key="item.value"
  383. :label="item.label"
  384. :value="item.value"
  385. />
  386. </el-select>
  387. </el-form-item>
  388. </el-col>
  389. <el-col :span="24">
  390. <el-form-item label="位置描述" prop="location">
  391. <el-input v-model="form.location" placeholder="请输入位置描述" />
  392. </el-form-item>
  393. </el-col>
  394. <el-col :span="12">
  395. <el-form-item label="经度" prop="longitude">
  396. <el-input-number
  397. v-model="form.longitude"
  398. :precision="6"
  399. :step="0.000001"
  400. style="width: 100%"
  401. />
  402. </el-form-item>
  403. </el-col>
  404. <el-col :span="12">
  405. <el-form-item label="纬度" prop="latitude">
  406. <el-input-number
  407. v-model="form.latitude"
  408. :precision="6"
  409. :step="0.000001"
  410. style="width: 100%"
  411. />
  412. </el-form-item>
  413. </el-col>
  414. <el-col :span="12">
  415. <el-form-item label="长度(米)" prop="length">
  416. <el-input-number
  417. v-model="form.length"
  418. :precision="2"
  419. :step="1"
  420. style="width: 100%"
  421. />
  422. </el-form-item>
  423. </el-col>
  424. <el-col :span="12">
  425. <el-form-item label="管径(mm)" prop="diameter">
  426. <el-input-number
  427. v-model="form.diameter"
  428. :precision="2"
  429. :step="1"
  430. style="width: 100%"
  431. />
  432. </el-form-item>
  433. </el-col>
  434. <el-col :span="12">
  435. <el-form-item label="材质" prop="material">
  436. <el-input v-model="form.material" placeholder="请输入材质" />
  437. </el-form-item>
  438. </el-col>
  439. <el-col :span="12">
  440. <el-form-item label="建设日期" prop="buildDate">
  441. <el-date-picker
  442. v-model="form.buildDate"
  443. type="date"
  444. value-format="YYYY-MM-DD"
  445. placeholder="请选择建设日期"
  446. style="width: 100%"
  447. />
  448. </el-form-item>
  449. </el-col>
  450. <el-col :span="24">
  451. <el-form-item label="备注" prop="remark">
  452. <el-input
  453. v-model="form.remark"
  454. type="textarea"
  455. :rows="3"
  456. placeholder="请输入备注"
  457. />
  458. </el-form-item>
  459. </el-col>
  460. </el-row>
  461. </el-form>
  462. <template #footer>
  463. <div class="dialog-footer">
  464. <el-button type="primary" @click="submitForm">确 定</el-button>
  465. <el-button @click="cancel">取 消</el-button>
  466. </div>
  467. </template>
  468. </el-dialog>
  469. </div>
  470. </template>
  471. <script setup>
  472. import {
  473. computed,
  474. getCurrentInstance,
  475. onMounted,
  476. reactive,
  477. ref,
  478. toRefs,
  479. } from "vue";
  480. import {
  481. addPipeNetwork,
  482. delPipeNetwork,
  483. getPipeNetwork,
  484. listMonitorPointAll,
  485. listPipeNetwork,
  486. listPipeNetworkAll,
  487. listPointsByNetwork,
  488. updatePipeNetwork,
  489. } from "@/api/pipeNetwork/pdeviceInform";
  490. const { proxy } = getCurrentInstance();
  491. const networkList = ref([]);
  492. const relatedPoints = ref([]);
  493. const loading = ref(false);
  494. const pointLoading = ref(false);
  495. const open = ref(false);
  496. const showSearch = ref(true);
  497. const ids = ref([]);
  498. const multiple = ref(true);
  499. const total = ref(0);
  500. const title = ref("");
  501. const selectedNetwork = ref({});
  502. const networkTypeOptions = [
  503. { label: "燃气设施", value: "0" },
  504. { label: "排水设施", value: "1" },
  505. { label: "供水设施", value: "2" },
  506. ];
  507. const statusOptions = [
  508. { label: "正常", value: "0" },
  509. { label: "维护中", value: "1" },
  510. { label: "异常", value: "2" },
  511. ];
  512. const networkTypeMap = { 0: "燃气设施", 1: "排水设施", 2: "供水设施" };
  513. const statusMap = { 0: "正常", 1: "维护中", 2: "异常" };
  514. function getNetworkTypeText(val) {
  515. return networkTypeMap[val] || val || "-";
  516. }
  517. function getStatusText(val) {
  518. return statusMap[val] || val || "-";
  519. }
  520. const summary = reactive({
  521. totalNetworks: 0,
  522. activeNetworks: 0,
  523. typeCount: 0,
  524. totalPoints: 0,
  525. });
  526. const abnormalPointCount = computed(() => {
  527. return relatedPoints.value.filter((item) => item.status !== "0").length;
  528. });
  529. const data = reactive({
  530. queryParams: {
  531. pageNum: 1,
  532. pageSize: 10,
  533. networkCode: undefined,
  534. networkName: undefined,
  535. networkType: undefined,
  536. status: undefined,
  537. },
  538. form: {},
  539. rules: {
  540. networkCode: [
  541. { required: true, message: "设施编码不能为空", trigger: "blur" },
  542. ],
  543. networkName: [
  544. { required: true, message: "设施名称不能为空", trigger: "blur" },
  545. ],
  546. networkType: [
  547. { required: true, message: "设施类型不能为空", trigger: "change" },
  548. ],
  549. },
  550. });
  551. const { queryParams, form, rules } = toRefs(data);
  552. function createEmptyForm() {
  553. return {
  554. networkId: undefined,
  555. networkCode: undefined,
  556. networkName: undefined,
  557. networkType: undefined,
  558. networkLevel: undefined,
  559. managementUnit: undefined,
  560. location: undefined,
  561. longitude: undefined,
  562. latitude: undefined,
  563. length: undefined,
  564. diameter: undefined,
  565. material: undefined,
  566. buildDate: undefined,
  567. status: "0",
  568. remark: undefined,
  569. };
  570. }
  571. function getStatusTagType(status) {
  572. if (status === "0") {
  573. return "success";
  574. }
  575. if (status === "1") {
  576. return "warning";
  577. }
  578. if (status === "2") {
  579. return "danger";
  580. }
  581. return "";
  582. }
  583. function loadRelatedPoints(networkId) {
  584. if (!networkId) {
  585. relatedPoints.value = [];
  586. return Promise.resolve();
  587. }
  588. pointLoading.value = true;
  589. return listPointsByNetwork(networkId)
  590. .then((response) => {
  591. relatedPoints.value = response.data || [];
  592. })
  593. .finally(() => {
  594. pointLoading.value = false;
  595. });
  596. }
  597. function loadSummary() {
  598. return Promise.all([listPipeNetworkAll(), listMonitorPointAll()]).then(
  599. ([networkRes, pointRes]) => {
  600. const allNetworks = networkRes.data || [];
  601. const allPoints = pointRes.data || [];
  602. summary.totalNetworks = allNetworks.length;
  603. summary.activeNetworks = allNetworks.filter(
  604. (item) => item.status === "0",
  605. ).length;
  606. summary.typeCount = new Set(
  607. allNetworks.map((item) => item.networkType).filter(Boolean),
  608. ).size;
  609. summary.totalPoints = allPoints.length;
  610. if (selectedNetwork.value.networkId) {
  611. const refreshed = allNetworks.find(
  612. (item) => item.networkId === selectedNetwork.value.networkId,
  613. );
  614. if (refreshed) {
  615. selectedNetwork.value = refreshed;
  616. }
  617. } else if (allNetworks.length) {
  618. selectedNetwork.value = allNetworks[0];
  619. }
  620. },
  621. );
  622. }
  623. function getList() {
  624. loading.value = true;
  625. return listPipeNetwork(queryParams.value)
  626. .then((response) => {
  627. networkList.value = response.records || [];
  628. total.value = response.total || 0;
  629. if (!selectedNetwork.value.networkId && networkList.value.length) {
  630. const firstNetwork = networkList.value[0];
  631. selectedNetwork.value = firstNetwork;
  632. return loadRelatedPoints(firstNetwork.networkId);
  633. }
  634. return undefined;
  635. })
  636. .finally(() => {
  637. loading.value = false;
  638. });
  639. }
  640. function reset() {
  641. form.value = createEmptyForm();
  642. proxy.resetForm("networkRef");
  643. }
  644. function handleQuery() {
  645. queryParams.value.pageNum = 1;
  646. getList();
  647. }
  648. function resetQuery() {
  649. proxy.resetForm("queryRef");
  650. handleQuery();
  651. }
  652. function handleSelectionChange(selection) {
  653. ids.value = selection.map((item) => item.networkId);
  654. multiple.value = !selection.length;
  655. }
  656. function handleCurrentChange(currentRow) {
  657. if (!currentRow?.networkId) {
  658. return;
  659. }
  660. // 点击一行会同时触发 row-click 与 current-change,选中项未变化时不再重复加载关联点位
  661. if (selectedNetwork.value.networkId === currentRow.networkId) {
  662. return;
  663. }
  664. selectedNetwork.value = currentRow;
  665. loadRelatedPoints(currentRow.networkId);
  666. }
  667. function handleRowClick(row) {
  668. handleCurrentChange(row);
  669. }
  670. function handleAdd() {
  671. reset();
  672. open.value = true;
  673. title.value = "新增管网基础信息";
  674. }
  675. function handleUpdate(row) {
  676. reset();
  677. const networkId = row?.networkId || ids.value[0];
  678. if (!networkId) {
  679. return;
  680. }
  681. getPipeNetwork(networkId).then((response) => {
  682. form.value = { ...createEmptyForm(), ...(response.data || {}) };
  683. open.value = true;
  684. title.value = "修改管网基础信息";
  685. });
  686. }
  687. function cancel() {
  688. open.value = false;
  689. reset();
  690. }
  691. function submitForm() {
  692. proxy.$refs.networkRef.validate((valid) => {
  693. if (!valid) {
  694. return;
  695. }
  696. const request = form.value.networkId
  697. ? updatePipeNetwork(form.value)
  698. : addPipeNetwork(form.value);
  699. request.then(() => {
  700. proxy.$modal.msgSuccess(form.value.networkId ? "修改成功" : "新增成功");
  701. open.value = false;
  702. Promise.all([getList(), loadSummary()]).then(() => {
  703. if (form.value.networkId) {
  704. getPipeNetwork(form.value.networkId).then((response) => {
  705. selectedNetwork.value = response.data || {};
  706. loadRelatedPoints(selectedNetwork.value.networkId);
  707. });
  708. }
  709. });
  710. });
  711. });
  712. }
  713. function handleDelete(row) {
  714. const deleteIds = row?.networkId ? [row.networkId] : ids.value;
  715. if (!deleteIds.length) {
  716. return;
  717. }
  718. proxy.$modal
  719. .confirm(`是否确认删除选中的 ${deleteIds.length} 条管网设施数据?`)
  720. .then(() => Promise.all(deleteIds.map((id) => delPipeNetwork(id))))
  721. .then(() => {
  722. proxy.$modal.msgSuccess("删除成功");
  723. if (deleteIds.includes(selectedNetwork.value.networkId)) {
  724. selectedNetwork.value = {};
  725. relatedPoints.value = [];
  726. }
  727. return Promise.all([getList(), loadSummary()]);
  728. });
  729. }
  730. onMounted(() => {
  731. reset();
  732. Promise.all([getList(), loadSummary()]).then(() => {
  733. if (selectedNetwork.value.networkId) {
  734. loadRelatedPoints(selectedNetwork.value.networkId);
  735. }
  736. });
  737. });
  738. </script>
  739. <style scoped>
  740. .pipe-network-page {
  741. background: #f5f8fc;
  742. }
  743. .summary-grid {
  744. display: grid;
  745. grid-template-columns: repeat(4, minmax(0, 1fr));
  746. gap: 16px;
  747. margin-bottom: 16px;
  748. }
  749. .summary-card {
  750. border: none;
  751. color: #fff;
  752. }
  753. .summary-card :deep(.el-card__body) {
  754. padding: 20px;
  755. }
  756. .summary-card.primary {
  757. background: linear-gradient(135deg, #2b73ff, #5da2ff);
  758. }
  759. .summary-card.success {
  760. background: linear-gradient(135deg, #16a34a, #41c878);
  761. }
  762. .summary-card.warning {
  763. background: linear-gradient(135deg, #f59e0b, #f8bf52);
  764. }
  765. .summary-card.info {
  766. background: linear-gradient(135deg, #0f766e, #2bb2a6);
  767. }
  768. .summary-label {
  769. font-size: 14px;
  770. opacity: 0.9;
  771. }
  772. .summary-value {
  773. margin: 8px 0 10px;
  774. font-size: 30px;
  775. font-weight: 700;
  776. line-height: 1;
  777. }
  778. .summary-desc {
  779. font-size: 12px;
  780. opacity: 0.9;
  781. }
  782. .query-card,
  783. .table-card,
  784. .detail-card {
  785. border-radius: 16px;
  786. }
  787. .query-card {
  788. margin-bottom: 16px;
  789. }
  790. .main-row {
  791. align-items: stretch;
  792. }
  793. .card-header-row {
  794. display: flex;
  795. justify-content: space-between;
  796. align-items: center;
  797. gap: 12px;
  798. }
  799. .card-title {
  800. font-size: 16px;
  801. font-weight: 600;
  802. color: #1f2937;
  803. }
  804. .card-subtitle {
  805. margin-top: 4px;
  806. font-size: 12px;
  807. color: #6b7280;
  808. }
  809. .card-actions {
  810. display: flex;
  811. align-items: center;
  812. gap: 8px;
  813. flex-wrap: wrap;
  814. }
  815. .detail-hero {
  816. margin-bottom: 16px;
  817. padding: 16px;
  818. border-radius: 14px;
  819. background: linear-gradient(135deg, #eff6ff, #f8fbff);
  820. border: 1px solid #dbeafe;
  821. }
  822. .hero-title {
  823. font-size: 18px;
  824. font-weight: 700;
  825. color: #1d4ed8;
  826. }
  827. .hero-meta {
  828. display: flex;
  829. gap: 10px;
  830. flex-wrap: wrap;
  831. margin-top: 8px;
  832. font-size: 12px;
  833. color: #4b5563;
  834. }
  835. .detail-descriptions {
  836. margin-bottom: 16px;
  837. }
  838. .section-block {
  839. margin-top: 12px;
  840. }
  841. .section-title {
  842. font-size: 15px;
  843. font-weight: 600;
  844. color: #1f2937;
  845. }
  846. .section-tip {
  847. margin: 6px 0 12px;
  848. font-size: 12px;
  849. color: #6b7280;
  850. }
  851. .side-actions {
  852. display: flex;
  853. gap: 10px;
  854. margin-top: 16px;
  855. }
  856. .fixed-width .cell {
  857. white-space: nowrap;
  858. }
  859. @media (max-width: 1200px) {
  860. .summary-grid {
  861. grid-template-columns: repeat(2, minmax(0, 1fr));
  862. }
  863. }
  864. @media (max-width: 768px) {
  865. .summary-grid {
  866. grid-template-columns: 1fr;
  867. }
  868. .card-header-row {
  869. align-items: flex-start;
  870. flex-direction: column;
  871. }
  872. .side-actions {
  873. flex-direction: column;
  874. }
  875. }
  876. </style>