From 08277c3be79dbcf018d04d453dc738d6354b4b1b Mon Sep 17 00:00:00 2001
From: quxin <592870522@qq.com>
Date: 星期三, 02 九月 2026 13:28:57 +0800
Subject: [PATCH] 诉讼信息自动归集至对应项目; 中标信息自动关联至对应项目; 所有销售合同数据均按项维度归集,作为项目管理模块中销售合同汇总的数据来源。

---
 src/views/projectManagement/Management/projectDetail.vue |  249 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 249 insertions(+), 0 deletions(-)

diff --git a/src/views/projectManagement/Management/projectDetail.vue b/src/views/projectManagement/Management/projectDetail.vue
index be670cb..dee5f99 100644
--- a/src/views/projectManagement/Management/projectDetail.vue
+++ b/src/views/projectManagement/Management/projectDetail.vue
@@ -160,6 +160,88 @@
             </el-table-column>
           </el-table>
         </el-tab-pane>
+
+        <el-tab-pane label="璇夎淇℃伅" name="lawsuit">
+          <el-table :data="lawsuitRows" border v-loading="lawsuitLoading" style="margin-top: 14px;">
+            <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+            <el-table-column label="妗堜欢缂栧彿" prop="caseNo" width="160" show-overflow-tooltip />
+            <el-table-column label="鎵�灞為」鐩�" prop="projectName" min-width="160" show-overflow-tooltip />
+            <el-table-column label="鍚堝悓缂栧彿" prop="contractNo" width="160" show-overflow-tooltip />
+            <el-table-column label="璇夎鐘舵��" width="110" align="center">
+              <template #default="{ row }">
+                <el-tag :type="lawsuitStatusTypeMap[row.lawsuitStatus] || 'info'">{{ row.lawsuitStatus || '-' }}</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="绔嬫鏃堕棿" prop="filingDate" width="110" :formatter="formatDateColumn" />
+            <el-table-column label="鎵垮姙寰嬪笀/娉曞姟璐熻矗浜�" prop="lawyer" width="160" show-overflow-tooltip />
+            <el-table-column label="寮�搴椂闂�" prop="hearingDate" width="110" :formatter="formatDateColumn" />
+            <el-table-column label="璇夎鏍囩殑閲戦(鍏�)" prop="claimAmount" width="150" align="right" :formatter="formatAmountColumn" />
+            <el-table-column label="鍒ゅ喅缁撴灉鎽樿" prop="judgmentSummary" min-width="180" show-overflow-tooltip />
+            <el-table-column label="璇夎杩涘睍澶囨敞" prop="progressRemark" min-width="160" show-overflow-tooltip />
+            <el-table-column label="闄勪欢" min-width="160">
+              <template #default="{ row }">
+                <div v-if="getLawsuitAttachments(row).length > 0" class="attachment-list">
+                  <el-button
+                    v-for="att in getLawsuitAttachments(row)"
+                    :key="att.id || att.url || att.name || att.fileName"
+                    link
+                    type="primary"
+                    size="small"
+                    @click="downloadAttachment(att)"
+                  >{{ att.name || att.fileName || '闄勪欢' }}</el-button>
+                </div>
+                <span v-else>-</span>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-tab-pane>
+
+        <el-tab-pane label="涓爣淇℃伅" name="bidding">
+          <el-table :data="biddingRows" border v-loading="biddingLoading" style="margin-top: 14px;">
+            <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+            <el-table-column label="鎵�灞為」鐩�" prop="projectName" min-width="160" show-overflow-tooltip />
+            <el-table-column label="鏍囦功鍚嶇О" prop="bidDocName" min-width="160" show-overflow-tooltip />
+            <el-table-column label="鎷涙爣鍗曚綅" prop="tenderUnit" width="160" show-overflow-tooltip />
+            <el-table-column label="鎶曟爣鍗曚綅" prop="bidUnit" width="160" show-overflow-tooltip />
+            <el-table-column label="鎷涙爣鏃ユ湡" prop="tenderDate" width="110" :formatter="formatDateColumn" />
+            <el-table-column label="鎶曟爣鏃ユ湡" prop="bidDate" width="110" :formatter="formatDateColumn" />
+            <el-table-column label="鏍囦功娆鹃噾棰�(鍏�)" prop="bidDocFee" width="130" align="right" :formatter="formatAmountColumn" />
+            <el-table-column label="鏍囦功娆剧姸鎬�" width="110" align="center">
+              <template #default="{ row }">
+                <el-tag :type="bidDocFeeStatusTypeMap[row.bidDocFeeStatus] || 'info'">{{ row.bidDocFeeStatus || '-' }}</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="鏍囦功娆剧姸鎬佸彉鏇存棩鏈�" prop="bidDocFeeStatusChangeDate" width="160" :formatter="formatDateColumn" />
+            <el-table-column label="鎶曟爣淇濊瘉閲�(鍏�)" prop="bidDeposit" width="130" align="right" :formatter="formatAmountColumn" />
+            <el-table-column label="鎷涙姇鏍囩粨鏋�" width="150" align="center">
+              <template #default="{ row }">
+                <el-tag :type="bidResultTypeMap[row.bidResult] || 'info'">{{ row.bidResult || '-' }}</el-tag>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-tab-pane>
+
+        <el-tab-pane label="閿�鍞悎鍚�" name="contract">
+          <el-table :data="contractRows" border v-loading="contractLoading" style="margin-top: 14px;">
+            <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+            <el-table-column label="閿�鍞悎鍚屽彿" prop="salesContractNo" width="180" show-overflow-tooltip />
+            <el-table-column label="瀹㈡埛鍚嶇О" prop="customerName" min-width="180" show-overflow-tooltip />
+            <el-table-column label="涓氬姟鍛�" prop="salesman" width="100" show-overflow-tooltip />
+            <el-table-column label="椤圭洰鍚嶇О" prop="projectName" width="180" show-overflow-tooltip />
+            <el-table-column label="浠樻鏂瑰紡" prop="paymentMethod" width="120">
+              <template #default="{ row }">
+                <dict-tag :options="checkout_payment" :value="row.paymentMethod" />
+              </template>
+            </el-table-column>
+            <el-table-column label="鍚堝悓閲戦(鍏�)" prop="contractAmount" width="140" align="right" :formatter="formatAmountColumn" />
+            <el-table-column label="瀹為檯鍚堝悓閲戦(鍏�)" prop="netContractAmount" width="150" align="right" :formatter="formatAmountColumn" />
+            <el-table-column label="褰曞叆浜�" prop="entryPersonName" width="100" show-overflow-tooltip />
+            <el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="110" :formatter="formatDateColumn" />
+            <el-table-column label="绛捐鏃ユ湡" prop="executionDate" width="110" :formatter="formatDateColumn" />
+            <el-table-column label="浜や粯鏃ユ湡" prop="deliveryDate" width="110" :formatter="formatDateColumn" />
+            <el-table-column label="澶囨敞" prop="remarks" min-width="160" show-overflow-tooltip />
+          </el-table>
+        </el-tab-pane>
       </el-tabs>
     </el-card>
   </div>
@@ -188,6 +270,9 @@
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { getProject, saveStage, listStage, deleteStage } from '@/api/projectManagement/project'
 import { listPlan } from '@/api/projectManagement/projectType'
+import { lawsuitListPage } from '@/api/salesManagement/lawsuitManagement'
+import { biddingListPage } from '@/api/salesManagement/biddingManagement'
+import { ledgerListPage } from '@/api/salesManagement/salesLedger'
 import ProgressReportDialog from '@/components/ProjectManagement/ProgressReportDialog.vue'
 import DiscussProgressDialog from '@/components/ProjectManagement/DiscussProgressDialog.vue'
 import useUserStore from '@/store/modules/user'
@@ -196,6 +281,7 @@
 const route = useRoute()
 const router = useRouter()
 const { bill_status, project_management, plan_status } = proxy.useDict('bill_status', 'project_management', 'plan_status')
+const { checkout_payment } = proxy.useDict('checkout_payment')
 
 const loading = ref(false)
 const activeTab = ref('base')
@@ -214,6 +300,13 @@
 const planNodeRows = ref([])
 const planAttachments = ref([])
 const stageNodeRows = ref([])
+
+const lawsuitRows = ref([])
+const biddingRows = ref([])
+const contractRows = ref([])
+const lawsuitLoading = ref(false)
+const biddingLoading = ref(false)
+const contractLoading = ref(false)
 
 const estimatedDays = computed(() => {
   const raw = info.value?.estimatedDays
@@ -374,6 +467,161 @@
   ElMessage.warning('闄勪欢鏆傛棤涓嬭浇鍦板潃')
 }
 
+const lawsuitStatusTypeMap = {
+  绔嬫涓�: 'warning',
+  瀹$悊涓�: 'primary',
+  宸插垽鍐�: 'success',
+  宸叉挙璇�: 'info',
+  宸插拰瑙�: 'success'
+}
+
+const bidDocFeeStatusTypeMap = {
+  鏈氦: 'warning',
+  宸蹭氦: 'success',
+  宸查��: 'info'
+}
+
+const bidResultTypeMap = {
+  搴熸爣: 'info',
+  娴佹爣: 'info',
+  鏈腑鏍�: 'warning',
+  涓爣寰呭嚭閫氱煡涔�: 'primary',
+  宸插嚭涓爣閫氱煡涔�: 'success',
+  宸茬鍚堝悓: 'success'
+}
+
+function formatDate(val) {
+  if (val === undefined || val === null || val === '') return '-'
+  return String(val).substring(0, 10)
+}
+
+function formatAmount(val) {
+  if (val === undefined || val === null || val === '') return '0.00'
+  const num = Number(val)
+  return Number.isNaN(num) ? '0.00' : num.toFixed(2)
+}
+
+function formatDateColumn(row, column, cellValue) {
+  return formatDate(cellValue)
+}
+
+function formatAmountColumn(row, column, cellValue) {
+  return formatAmount(cellValue)
+}
+
+function getLawsuitAttachments(row) {
+  if (Array.isArray(row?.storageBlobVOs)) return row.storageBlobVOs
+  if (Array.isArray(row?.attachmentList)) return row.attachmentList
+  return []
+}
+
+function matchProject(record, projectId, projectTitle) {
+  if (!record) return false
+  if (record.projectId !== undefined && record.projectId !== null && record.projectId !== '') {
+    if (String(record.projectId) === String(projectId)) return true
+  }
+  const name = String(record.projectName || '').trim()
+  const title = String(projectTitle || '').trim()
+  if (name && title) {
+    return name === title || name.includes(title) || title.includes(name)
+  }
+  return false
+}
+
+function filterByProject(records, projectId, projectTitle) {
+  const list = Array.isArray(records) ? records : []
+  if (list.length === 0) return []
+  const matched = list.filter(r => matchProject(r, projectId, projectTitle))
+  return matched.length > 0 ? matched : list
+}
+
+function extractRecords(res) {
+  return (
+    res?.data?.records ||
+    res?.records ||
+    res?.rows ||
+    res?.data?.rows ||
+    res?.data?.list ||
+    (Array.isArray(res?.data) ? res.data : []) ||
+    []
+  )
+}
+
+async function loadLawsuitList() {
+  const projectId = info.value?.id
+  const projectTitle = info.value?.title
+  if (!projectId) {
+    lawsuitRows.value = []
+    return
+  }
+  lawsuitLoading.value = true
+  try {
+    let res = await lawsuitListPage({ projectName: projectTitle, current: 1, size: 999 })
+    let records = extractRecords(res)
+    let matched = records.filter(r => matchProject(r, projectId, projectTitle))
+    if (matched.length === 0) {
+      res = await lawsuitListPage({ current: 1, size: 999 })
+      records = extractRecords(res)
+      matched = records.filter(r => matchProject(r, projectId, projectTitle))
+    }
+    lawsuitRows.value = matched
+  } catch {
+    lawsuitRows.value = []
+  } finally {
+    lawsuitLoading.value = false
+  }
+}
+
+async function loadBiddingList() {
+  const projectId = info.value?.id
+  const projectTitle = info.value?.title
+  if (!projectId) {
+    biddingRows.value = []
+    return
+  }
+  biddingLoading.value = true
+  try {
+    let res = await biddingListPage({ projectName: projectTitle, current: 1, size: 999 })
+    let records = extractRecords(res)
+    let matched = records.filter(r => matchProject(r, projectId, projectTitle))
+    if (matched.length === 0) {
+      res = await biddingListPage({ current: 1, size: 999 })
+      records = extractRecords(res)
+      matched = records.filter(r => matchProject(r, projectId, projectTitle))
+    }
+    biddingRows.value = matched
+  } catch {
+    biddingRows.value = []
+  } finally {
+    biddingLoading.value = false
+  }
+}
+
+async function loadContractList() {
+  const projectId = info.value?.id
+  if (!projectId) {
+    contractRows.value = []
+    return
+  }
+  contractLoading.value = true
+  try {
+    const res = await ledgerListPage({ projectId, current: 1, size: 999 })
+    const records = extractRecords(res)
+    contractRows.value = filterByProject(records, projectId, info.value?.title).sort((a, b) => {
+      const da = String(a.executionDate || '').substring(0, 10)
+      const db = String(b.executionDate || '').substring(0, 10)
+      if (!da && !db) return 0
+      if (!da) return 1
+      if (!db) return -1
+      return db.localeCompare(da)
+    })
+  } catch {
+    contractRows.value = []
+  } finally {
+    contractLoading.value = false
+  }
+}
+
 async function loadProjectTypeMap() {
   try {
     const res = await listPlan({ current: 1, size: 999 })
@@ -471,6 +719,7 @@
 onMounted(async () => {
   await loadProjectTypeMap()
   await loadDetail()
+  await Promise.all([loadLawsuitList(), loadBiddingList(), loadContractList()])
 })
 </script>
 

--
Gitblit v1.9.3