From ffe06be91f95ffd2459ebdb2f9130d1e38d53ea7 Mon Sep 17 00:00:00 2001
From: zzw <228544128@qq.com>
Date: 星期二, 06 十月 2026 18:32:34 +0800
Subject: [PATCH] fix: preserve manually edited menu names

---
 emergency-repair-web/src/views/completion/CompletionQuery.vue |  209 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 209 insertions(+), 0 deletions(-)

diff --git a/emergency-repair-web/src/views/completion/CompletionQuery.vue b/emergency-repair-web/src/views/completion/CompletionQuery.vue
new file mode 100644
index 0000000..38044cf
--- /dev/null
+++ b/emergency-repair-web/src/views/completion/CompletionQuery.vue
@@ -0,0 +1,209 @@
+<template>
+  <div class="page">
+    <div class="page-title">
+      <div>
+        <h2>瀹屽伐淇℃伅鏌ラ槄</h2>
+        <p>鎸夋晠闅滄眹鎬伙紝鍚屼竴鏁呴殰鐨勫寮犳淳宸ュ崟鍏辩敤鏁呴殰淇℃伅锛屽畬宸ュ鐞嗘儏鍐靛拰鏇存崲鍣ㄦ潗鎸夊鏉℃槑缁嗗睍绀�</p>
+      </div>
+      <el-tag type="info">鍏� {{ rows.length }} 涓晠闅�</el-tag>
+    </div>
+
+    <div class="toolbar">
+      <el-input v-model="filters.keyword" placeholder="鏁呴殰缂栧彿 / rk_X鍙� / 娲惧伐鍗曠紪鍙� / 宸ョ▼鍚嶇О / 鎵夸慨鍗曚綅" clearable style="width:380px" @keyup.enter.native="load"></el-input>
+      <el-input v-model="filters.contractorName" placeholder="鎵夸慨鍗曚綅" clearable style="width:190px" @keyup.enter.native="load"></el-input>
+      <el-select v-model="filters.completionStatus" placeholder="瀹屽伐鐘舵��" clearable style="width:140px">
+        <el-option v-for="(meta, key) in completionStatuses" :key="key" :label="meta.text" :value="key"></el-option>
+      </el-select>
+      <el-date-picker v-model="filters.completionDate" type="daterange" value-format="yyyy-MM-dd" range-separator="鑷�" start-placeholder="瀹屽伐寮�濮嬫棩鏈�" end-placeholder="瀹屽伐缁撴潫鏃ユ湡" style="width:270px"></el-date-picker>
+      <el-button type="primary" icon="el-icon-search" v-permission="'completion:query:query'" @click="load">鏌ヨ</el-button>
+      <el-button icon="el-icon-refresh" @click="reset">閲嶇疆</el-button>
+    </div>
+
+    <div class="table-panel">
+      <el-table v-loading="loading" :data="displayRows" border size="small" :span-method="spanMethod" row-key="rowKey">
+        <el-table-column label="搴忓彿" width="62" align="center">
+          <template slot-scope="{ row }">{{ row.faultIndex }}</template>
+        </el-table-column>
+        <el-table-column prop="faultCode" label="鏁呴殰缂栧彿" width="150"></el-table-column>
+        <el-table-column prop="xNo" label="rk_X鍙�" width="72"></el-table-column>
+        <el-table-column prop="equipmentName" label="璁惧鍚嶇О" width="130" show-overflow-tooltip></el-table-column>
+        <el-table-column prop="engineeringNameDesc" label="宸ョ▼鍚嶇О鍙婃崯鍧忔儏鍐�" min-width="190" show-overflow-tooltip></el-table-column>
+        <el-table-column prop="reportUnit" label="涓婃姤鍗曚綅" width="130" show-overflow-tooltip></el-table-column>
+        <el-table-column prop="reportDate" label="涓婃姤鏃ユ湡" width="105"></el-table-column>
+
+        <el-table-column prop="workOrderNo" label="娲惧伐鍗曠紪鍙�" width="155"></el-table-column>
+        <el-table-column prop="contractorName" label="鎵夸慨鍗曚綅" width="150" show-overflow-tooltip></el-table-column>
+        <el-table-column label="瀹屽伐鐘舵��" width="100">
+          <template slot-scope="{ row }">
+            <el-tag size="mini" :type="completionMeta(row.completionStatus).type">{{ completionMeta(row.completionStatus).text }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="engineeringNo" label="宸ョ▼缂栧彿" width="145"></el-table-column>
+        <el-table-column label="寮�宸� / 瀹屽伐" width="190">
+          <template slot-scope="{ row }">{{ formatPeriod(row.startDate, row.finishDate) }}</template>
+        </el-table-column>
+        <el-table-column prop="contractorPerson" label="鎵夸慨浜�" width="100"></el-table-column>
+        <el-table-column label="瀹屽伐澶勭悊鎯呭喌" min-width="260">
+          <template slot-scope="{ row }">
+            <div v-if="row.solutions && row.solutions.length" class="detail-list">
+              <div v-for="(item, index) in row.solutions" :key="index" class="detail-line">
+                <span class="detail-index">{{ index + 1 }}</span>
+                <span>{{ item.content || '-' }}</span>
+              </div>
+            </div>
+            <span v-else class="muted">-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="鏇存崲鍣ㄦ潗" min-width="300">
+          <template slot-scope="{ row }">
+            <div v-if="row.materials && row.materials.length" class="detail-list">
+              <div v-for="(item, index) in row.materials" :key="index" class="material-line">
+                <span class="detail-index">{{ index + 1 }}</span>
+                <div>
+                  <div><strong>{{ item.name || '-' }}</strong><span v-if="item.specification"> / {{ item.specification }}</span><span v-if="item.quantity != null"> 脳 {{ item.quantity }}{{ item.unit || '' }}</span></div>
+                  <div class="material-meta">{{ materialMeta(item) }}</div>
+                </div>
+              </div>
+            </div>
+            <span v-else class="muted">-</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="qualityInfo" label="淇悗璐ㄩ噺鎯呭喌" min-width="180" show-overflow-tooltip></el-table-column>
+        <el-table-column prop="reasonAnalysis" label="鍘熷洜鍒嗘瀽" min-width="180" show-overflow-tooltip></el-table-column>
+        <el-table-column prop="submitTime" label="鎻愪氦鏃堕棿" width="160"></el-table-column>
+      </el-table>
+    </div>
+  </div>
+</template>
+
+<script>
+import api from '@/api'
+import { completionStatuses, statusMeta } from '@/utils/dict'
+
+export default {
+  name: 'CompletionQuery',
+  data() {
+    return {
+      loading: false,
+      completionStatuses,
+      rows: [],
+      filters: {
+        keyword: '',
+        contractorName: '',
+        completionStatus: '',
+        completionDate: []
+      }
+    }
+  },
+  computed: {
+    displayRows() {
+      const rows = []
+      this.rows.forEach((fault, faultIndex) => {
+        const workOrders = fault.workOrders || []
+        const total = workOrders.length || 1
+        if (!workOrders.length) {
+          rows.push({ ...fault, faultIndex: faultIndex + 1, faultRowspan: 1, rowKey: fault.faultId + '-empty' })
+          return
+        }
+        workOrders.forEach((workOrder, index) => {
+          rows.push({
+            ...fault,
+            ...workOrder,
+            faultIndex: faultIndex + 1,
+            faultRowspan: index === 0 ? total : 0,
+            rowKey: fault.faultId + '-' + workOrder.workOrderId
+          })
+        })
+      })
+      return rows
+    }
+  },
+  created() {
+    this.load()
+  },
+  methods: {
+    load() {
+      this.loading = true
+      const range = this.filters.completionDate || []
+      api.get('/work-orders/completion/query', {
+        params: {
+          keyword: this.filters.keyword || undefined,
+          contractorName: this.filters.contractorName || undefined,
+          completionStatus: this.filters.completionStatus || undefined,
+          startDate: range[0] || undefined,
+          endDate: range[1] || undefined
+        }
+      }).then(res => {
+        this.rows = res.data || []
+      }).finally(() => {
+        this.loading = false
+      })
+    },
+    reset() {
+      this.filters = { keyword: '', contractorName: '', completionStatus: '', completionDate: [] }
+      this.load()
+    },
+    completionMeta(value) {
+      return statusMeta(completionStatuses, value)
+    },
+    spanMethod({ row, columnIndex }) {
+      if (columnIndex <= 6) {
+        return row.faultRowspan === 0 ? { rowspan: 0, colspan: 0 } : { rowspan: row.faultRowspan, colspan: 1 }
+      }
+      return { rowspan: 1, colspan: 1 }
+    },
+    formatPeriod(startDate, finishDate) {
+      return [startDate || '-', finishDate || '-'].join(' 鑷� ')
+    },
+    materialMeta(item) {
+      const parts = []
+      if (item.technicalParameter) parts.push(item.technicalParameter)
+      if (item.source) parts.push('鏉ユ簮锛�' + item.source)
+      if (item.outboundNo) parts.push('鍑哄簱鍗曪細' + item.outboundNo)
+      if (item.certificate) parts.push('鍚堟牸璇佹槑锛�' + item.certificate)
+      if (item.remark) parts.push('澶囨敞锛�' + item.remark)
+      return parts.join('锛�') || '-'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.table-panel {
+  overflow-x: auto;
+}
+.detail-list {
+  display: flex;
+  flex-direction: column;
+  gap: 5px;
+}
+.detail-line,
+.material-line {
+  display: flex;
+  align-items: flex-start;
+  gap: 6px;
+  line-height: 20px;
+}
+.material-line > div {
+  min-width: 0;
+}
+.detail-index {
+  flex: 0 0 auto;
+  min-width: 16px;
+  height: 16px;
+  margin-top: 2px;
+  border-radius: 50%;
+  color: #fff;
+  background: #7b8fa6;
+  font-size: 11px;
+  line-height: 16px;
+  text-align: center;
+}
+.material-meta {
+  color: #7b8a9a;
+  font-size: 12px;
+}
+.muted {
+  color: #a3afbc;
+}
+</style>

--
Gitblit v1.9.1