| | |
| | | </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> |
| | |
| | | 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' |
| | |
| | | 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') |
| | |
| | | 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 |
| | |
| | | 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 }) |
| | |
| | | onMounted(async () => { |
| | | await loadProjectTypeMap() |
| | | await loadDetail() |
| | | await Promise.all([loadLawsuitList(), loadBiddingList(), loadContractList()]) |
| | | }) |
| | | </script> |
| | | |