1424 lines
56 KiB
Vue
1424 lines
56 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<el-form :model="queryParams" ref="queryForm" size="small" v-show="showSearch" label-width="100px" class="query-form">
|
||
<el-row :gutter="16">
|
||
<el-col :span="6">
|
||
<el-form-item label="项目名称" prop="projectName">
|
||
<el-input
|
||
v-model="queryParams.projectName"
|
||
placeholder="请输入项目名称"
|
||
clearable
|
||
style="width: 100%"
|
||
@keyup.enter.native="handleQuery"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="标的物名称" prop="subjectName">
|
||
<el-input
|
||
v-model="queryParams.subjectName"
|
||
placeholder="请输入标的物名称"
|
||
clearable
|
||
style="width: 100%"
|
||
@keyup.enter.native="handleQuery"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="申请人" prop="applicantName">
|
||
<el-input
|
||
v-model="queryParams.applicantName"
|
||
placeholder="请输入申请人"
|
||
clearable
|
||
style="width: 100%"
|
||
@keyup.enter.native="handleQuery"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="申请日期">
|
||
<el-date-picker
|
||
v-model="dateRange"
|
||
style="width: 100%"
|
||
value-format="yyyy-MM-dd"
|
||
type="daterange"
|
||
range-separator="-"
|
||
start-placeholder="开始日期"
|
||
end-placeholder="结束日期"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
|
||
<el-row :gutter="10" class="mb8">
|
||
<el-col :span="1.5">
|
||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button
|
||
type="primary"
|
||
plain
|
||
icon="el-icon-plus"
|
||
size="mini"
|
||
@click="handleAdd"
|
||
v-hasPermi="['ccdi:purchaseTransaction:add']"
|
||
>新增</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5">
|
||
<el-button
|
||
type="success"
|
||
plain
|
||
icon="el-icon-upload2"
|
||
size="mini"
|
||
@click="handleImport"
|
||
v-hasPermi="['ccdi:purchaseTransaction:import']"
|
||
>导入</el-button>
|
||
</el-col>
|
||
<el-col :span="1.5" v-if="showFailureButton">
|
||
<el-tooltip :content="getLastImportTooltip()" placement="top">
|
||
<el-button
|
||
type="warning"
|
||
plain
|
||
icon="el-icon-warning"
|
||
size="mini"
|
||
@click="viewImportFailures"
|
||
>查看导入失败记录</el-button>
|
||
</el-tooltip>
|
||
</el-col>
|
||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
|
||
</el-row>
|
||
|
||
<el-table v-loading="loading" :data="transactionList" @selection-change="handleSelectionChange">
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column label="采购事项ID" align="center" prop="purchaseId" width="150" :show-overflow-tooltip="true" />
|
||
<el-table-column label="采购类别" align="center" prop="purchaseCategory" width="110" />
|
||
<el-table-column label="项目名称" align="center" prop="projectName" :show-overflow-tooltip="true" />
|
||
<el-table-column label="标的物名称" align="center" prop="subjectName" :show-overflow-tooltip="true" />
|
||
<el-table-column label="采购方式" align="center" prop="purchaseMethod" width="120" />
|
||
<el-table-column label="中标供应商" align="center" prop="supplierName" :show-overflow-tooltip="true">
|
||
<template slot-scope="scope">
|
||
<span>{{ scope.row.supplierName || "-" }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="参与供应商数" align="center" prop="supplierCount" width="120">
|
||
<template slot-scope="scope">
|
||
<span>{{ scope.row.supplierCount || 0 }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="预算金额(元)" align="center" prop="budgetAmount" width="120">
|
||
<template slot-scope="scope">
|
||
{{ formatAmount(scope.row.budgetAmount) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="申请人" align="center" prop="applicantName" width="100" />
|
||
<el-table-column label="申请部门" align="center" prop="applyDepartment" width="120" />
|
||
<el-table-column label="申请日期" align="center" prop="applyDate" width="120">
|
||
<template slot-scope="scope">
|
||
<span>{{ parseTime(scope.row.applyDate, '{y}-{m}-{d}') }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
|
||
<template slot-scope="scope">
|
||
<el-button
|
||
size="mini"
|
||
type="text"
|
||
icon="el-icon-view"
|
||
@click="handleDetail(scope.row)"
|
||
v-hasPermi="['ccdi:purchaseTransaction:query']"
|
||
>详情</el-button>
|
||
<el-button
|
||
size="mini"
|
||
type="text"
|
||
icon="el-icon-edit"
|
||
@click="handleUpdate(scope.row)"
|
||
v-hasPermi="['ccdi:purchaseTransaction:edit']"
|
||
>编辑</el-button>
|
||
<el-button
|
||
size="mini"
|
||
type="text"
|
||
icon="el-icon-delete"
|
||
@click="handleDelete(scope.row)"
|
||
v-hasPermi="['ccdi:purchaseTransaction:remove']"
|
||
>删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<pagination
|
||
v-show="total > 0"
|
||
:total="total"
|
||
:page.sync="queryParams.pageNum"
|
||
:limit.sync="queryParams.pageSize"
|
||
@pagination="getList"
|
||
/>
|
||
|
||
<el-dialog :title="title" :visible.sync="open" width="80%" append-to-body>
|
||
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
|
||
<el-divider content-position="left">基本信息</el-divider>
|
||
<el-row :gutter="16">
|
||
<el-col :span="12">
|
||
<el-form-item label="采购事项ID" prop="purchaseId">
|
||
<el-input v-model="form.purchaseId" placeholder="请输入采购事项ID" maxlength="32" :disabled="!isAdd" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="采购类别" prop="purchaseCategory">
|
||
<el-input v-model="form.purchaseCategory" placeholder="请输入采购类别" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="16">
|
||
<el-col :span="12">
|
||
<el-form-item label="项目名称" prop="projectName">
|
||
<el-input v-model="form.projectName" placeholder="请输入项目名称" maxlength="200" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="标的物名称" prop="subjectName">
|
||
<el-input v-model="form.subjectName" placeholder="请输入标的物名称" maxlength="200" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-form-item label="标的物描述" prop="subjectDesc">
|
||
<el-input v-model="form.subjectDesc" type="textarea" :rows="2" placeholder="请输入标的物描述" />
|
||
</el-form-item>
|
||
|
||
<el-divider content-position="left">数量与金额</el-divider>
|
||
<el-row :gutter="16">
|
||
<el-col :span="8">
|
||
<el-form-item label="采购数量" prop="purchaseQty">
|
||
<el-input-number v-model="form.purchaseQty" :min="0" :precision="2" placeholder="请输入采购数量" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="预算金额(元)" prop="budgetAmount">
|
||
<el-input-number v-model="form.budgetAmount" :min="0" :precision="2" placeholder="请输入预算金额" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="中标金额(元)" prop="bidAmount">
|
||
<el-input-number v-model="form.bidAmount" :min="0" :precision="2" placeholder="请输入中标金额" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="16">
|
||
<el-col :span="8">
|
||
<el-form-item label="实际采购金额(元)" prop="actualAmount">
|
||
<el-input-number v-model="form.actualAmount" :min="0" :precision="2" placeholder="请输入实际采购金额" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="合同金额(元)" prop="contractAmount">
|
||
<el-input-number v-model="form.contractAmount" :min="0" :precision="2" placeholder="请输入合同金额" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="结算金额(元)" prop="settlementAmount">
|
||
<el-input-number v-model="form.settlementAmount" :min="0" :precision="2" placeholder="请输入结算金额" style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="16">
|
||
<el-col :span="12">
|
||
<el-form-item label="采购方式" prop="purchaseMethod">
|
||
<el-input v-model="form.purchaseMethod" placeholder="请输入采购方式" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-divider content-position="left">供应商明细</el-divider>
|
||
<div class="supplier-toolbar">
|
||
<span class="supplier-toolbar__tip">支持录入中标供应商之外的全部参标供应商,并标记唯一中标供应商。</span>
|
||
<el-button type="primary" plain size="mini" icon="el-icon-plus" @click="handleAddSupplier">添加供应商</el-button>
|
||
</div>
|
||
<el-empty
|
||
v-if="!form.supplierList.length"
|
||
:image-size="72"
|
||
description="当前未录入供应商信息"
|
||
class="supplier-empty"
|
||
/>
|
||
<el-table v-else :data="form.supplierList" border size="small" class="supplier-table">
|
||
<el-table-column label="中标" width="90" align="center">
|
||
<template slot-scope="scope">
|
||
<el-radio v-model="winnerSupplierIndex" :label="scope.$index">中标</el-radio>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="序号" width="70" align="center">
|
||
<template slot-scope="scope">
|
||
<span>{{ scope.$index + 1 }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="供应商名称" min-width="220">
|
||
<template slot-scope="scope">
|
||
<el-form-item
|
||
class="supplier-form-item"
|
||
label-width="0px"
|
||
:prop="'supplierList.' + scope.$index + '.supplierName'"
|
||
:rules="getSupplierFieldRules('supplierName')"
|
||
>
|
||
<el-input v-model="scope.row.supplierName" placeholder="请输入供应商名称" maxlength="200" />
|
||
</el-form-item>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="统一信用代码" min-width="200">
|
||
<template slot-scope="scope">
|
||
<el-form-item
|
||
class="supplier-form-item"
|
||
label-width="0px"
|
||
:prop="'supplierList.' + scope.$index + '.supplierUscc'"
|
||
:rules="getSupplierFieldRules('supplierUscc')"
|
||
>
|
||
<el-input v-model="scope.row.supplierUscc" placeholder="请输入统一信用代码" maxlength="18" />
|
||
</el-form-item>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="联系人" min-width="140">
|
||
<template slot-scope="scope">
|
||
<el-form-item
|
||
class="supplier-form-item"
|
||
label-width="0px"
|
||
:prop="'supplierList.' + scope.$index + '.contactPerson'"
|
||
:rules="getSupplierFieldRules('contactPerson')"
|
||
>
|
||
<el-input v-model="scope.row.contactPerson" placeholder="请输入联系人" maxlength="50" />
|
||
</el-form-item>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="联系电话" min-width="160">
|
||
<template slot-scope="scope">
|
||
<el-form-item
|
||
class="supplier-form-item"
|
||
label-width="0px"
|
||
:prop="'supplierList.' + scope.$index + '.contactPhone'"
|
||
:rules="getSupplierFieldRules('contactPhone')"
|
||
>
|
||
<el-input v-model="scope.row.contactPhone" placeholder="请输入联系电话" maxlength="20" />
|
||
</el-form-item>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="银行账户" min-width="180">
|
||
<template slot-scope="scope">
|
||
<el-form-item
|
||
class="supplier-form-item"
|
||
label-width="0px"
|
||
:prop="'supplierList.' + scope.$index + '.supplierBankAccount'"
|
||
:rules="getSupplierFieldRules('supplierBankAccount')"
|
||
>
|
||
<el-input v-model="scope.row.supplierBankAccount" placeholder="请输入银行账户" maxlength="50" />
|
||
</el-form-item>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="90" align="center" fixed="right">
|
||
<template slot-scope="scope">
|
||
<el-button type="text" size="mini" @click="handleRemoveSupplier(scope.$index)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<el-divider content-position="left">重要日期</el-divider>
|
||
<el-row :gutter="16">
|
||
<el-col :span="8">
|
||
<el-form-item label="采购申请日期" prop="applyDate">
|
||
<el-date-picker
|
||
v-model="form.applyDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="计划批准日期" prop="planApproveDate">
|
||
<el-date-picker
|
||
v-model="form.planApproveDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="公告发布日期" prop="announceDate">
|
||
<el-date-picker
|
||
v-model="form.announceDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="16">
|
||
<el-col :span="8">
|
||
<el-form-item label="开标日期" prop="bidOpenDate">
|
||
<el-date-picker
|
||
v-model="form.bidOpenDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="合同签订日期" prop="contractSignDate">
|
||
<el-date-picker
|
||
v-model="form.contractSignDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="预计交货日期" prop="expectedDeliveryDate">
|
||
<el-date-picker
|
||
v-model="form.expectedDeliveryDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="16">
|
||
<el-col :span="8">
|
||
<el-form-item label="实际交货日期" prop="actualDeliveryDate">
|
||
<el-date-picker
|
||
v-model="form.actualDeliveryDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="验收日期" prop="acceptanceDate">
|
||
<el-date-picker
|
||
v-model="form.acceptanceDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="结算日期" prop="settlementDate">
|
||
<el-date-picker
|
||
v-model="form.settlementDate"
|
||
type="date"
|
||
placeholder="选择日期"
|
||
value-format="yyyy-MM-dd"
|
||
style="width: 100%"
|
||
/>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-divider content-position="left">申请人信息</el-divider>
|
||
<el-row :gutter="16">
|
||
<el-col :span="12">
|
||
<el-form-item label="申请人姓名" prop="applicantName">
|
||
<el-input v-model="form.applicantName" placeholder="请输入申请人姓名" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="申请人工号" prop="applicantId">
|
||
<el-input v-model="form.applicantId" placeholder="请输入申请人工号" maxlength="20" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="16">
|
||
<el-col :span="12">
|
||
<el-form-item label="申请部门" prop="applyDepartment">
|
||
<el-input v-model="form.applyDepartment" placeholder="请输入申请部门" maxlength="100" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<el-divider content-position="left">采购负责人信息</el-divider>
|
||
<el-row :gutter="16">
|
||
<el-col :span="12">
|
||
<el-form-item label="采购负责人姓名" prop="purchaseLeaderName">
|
||
<el-input v-model="form.purchaseLeaderName" placeholder="请输入采购负责人姓名" maxlength="50" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="采购负责人工号" prop="purchaseLeaderId">
|
||
<el-input v-model="form.purchaseLeaderId" placeholder="请输入采购负责人工号" maxlength="20" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="16">
|
||
<el-col :span="12">
|
||
<el-form-item label="采购部门" prop="purchaseDepartment">
|
||
<el-input v-model="form.purchaseDepartment" placeholder="请输入采购部门" maxlength="100" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="cancel">取消</el-button>
|
||
<el-button type="primary" @click="submitForm">确定</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<el-dialog title="招投标信息详情" :visible.sync="detailOpen" width="1200px" append-to-body>
|
||
<div class="detail-container">
|
||
<el-divider content-position="left">基本信息</el-divider>
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="采购事项ID">{{ transactionDetail.purchaseId || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="采购类别">{{ transactionDetail.purchaseCategory || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="项目名称" :span="2">{{ transactionDetail.projectName || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="标的物名称">{{ transactionDetail.subjectName || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="采购方式">{{ transactionDetail.purchaseMethod || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="标的物描述" :span="2">{{ transactionDetail.subjectDesc || "-" }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<el-divider content-position="left">数量与金额</el-divider>
|
||
<el-descriptions :column="3" border>
|
||
<el-descriptions-item label="采购数量">{{ transactionDetail.purchaseQty || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="预算金额(元)">{{ formatAmount(transactionDetail.budgetAmount) }}</el-descriptions-item>
|
||
<el-descriptions-item label="中标金额(元)">{{ formatAmount(transactionDetail.bidAmount) }}</el-descriptions-item>
|
||
<el-descriptions-item label="实际采购金额(元)">{{ formatAmount(transactionDetail.actualAmount) }}</el-descriptions-item>
|
||
<el-descriptions-item label="合同金额(元)">{{ formatAmount(transactionDetail.contractAmount) }}</el-descriptions-item>
|
||
<el-descriptions-item label="结算金额(元)">{{ formatAmount(transactionDetail.settlementAmount) }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<el-divider content-position="left">供应商明细</el-divider>
|
||
<el-empty
|
||
v-if="!transactionDetail.supplierList || !transactionDetail.supplierList.length"
|
||
:image-size="72"
|
||
description="未录入供应商信息"
|
||
/>
|
||
<el-table v-else :data="transactionDetail.supplierList" border size="small">
|
||
<el-table-column label="排序" prop="sortOrder" width="90" align="center" />
|
||
<el-table-column label="中标结果" width="110" align="center">
|
||
<template slot-scope="scope">
|
||
<el-tag :type="scope.row.isBidWinner === 1 ? 'danger' : 'info'" size="mini">
|
||
{{ scope.row.isBidWinner === 1 ? "中标" : "参标" }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="供应商名称" prop="supplierName" min-width="220" :show-overflow-tooltip="true" />
|
||
<el-table-column label="统一信用代码" prop="supplierUscc" min-width="180" :show-overflow-tooltip="true" />
|
||
<el-table-column label="联系人" prop="contactPerson" min-width="120" :show-overflow-tooltip="true" />
|
||
<el-table-column label="联系电话" prop="contactPhone" min-width="150" :show-overflow-tooltip="true" />
|
||
<el-table-column label="银行账户" prop="supplierBankAccount" min-width="180" :show-overflow-tooltip="true" />
|
||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||
<template slot-scope="scope">
|
||
<el-button
|
||
type="text"
|
||
size="mini"
|
||
:loading="enterpriseDetailLoading"
|
||
@click="handleSupplierEnterpriseDetail(scope.row)"
|
||
>详情</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<el-divider content-position="left">重要日期</el-divider>
|
||
<el-descriptions :column="3" border>
|
||
<el-descriptions-item label="采购申请日期">{{ transactionDetail.applyDate || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="计划批准日期">{{ transactionDetail.planApproveDate || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="公告发布日期">{{ transactionDetail.announceDate || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="开标日期">{{ transactionDetail.bidOpenDate || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="合同签订日期">{{ transactionDetail.contractSignDate || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="预计交货日期">{{ transactionDetail.expectedDeliveryDate || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="实际交货日期">{{ transactionDetail.actualDeliveryDate || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="验收日期">{{ transactionDetail.acceptanceDate || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="结算日期">{{ transactionDetail.settlementDate || "-" }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<el-divider content-position="left">申请人信息</el-divider>
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="申请人姓名">{{ transactionDetail.applicantName || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="申请人工号">{{ transactionDetail.applicantId || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="申请部门">{{ transactionDetail.applyDepartment || "-" }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<el-divider content-position="left">采购负责人信息</el-divider>
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="采购负责人姓名">{{ transactionDetail.purchaseLeaderName || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="采购负责人工号">{{ transactionDetail.purchaseLeaderId || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="采购部门">{{ transactionDetail.purchaseDepartment || "-" }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<el-divider content-position="left">审计信息</el-divider>
|
||
<el-descriptions :column="2" border>
|
||
<el-descriptions-item label="创建时间">
|
||
{{ transactionDetail.createTime ? parseTime(transactionDetail.createTime) : "-" }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="创建人">{{ transactionDetail.createdBy || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="更新时间">
|
||
{{ transactionDetail.updateTime ? parseTime(transactionDetail.updateTime) : "-" }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="更新人">{{ transactionDetail.updatedBy || "-" }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="detailOpen = false" icon="el-icon-close">关闭</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<el-dialog
|
||
title="企业信息详情"
|
||
:visible.sync="enterpriseDetailOpen"
|
||
width="1000px"
|
||
append-to-body
|
||
@close="resetEnterpriseDetail"
|
||
>
|
||
<el-descriptions :column="2" border v-loading="enterpriseDetailLoading">
|
||
<el-descriptions-item label="统一社会信用代码">{{ enterpriseDetailData.socialCreditCode || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="企业名称">{{ enterpriseDetailData.enterpriseName || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="企业类型">{{ enterpriseDetailData.enterpriseType || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="企业性质">{{ enterpriseDetailData.enterpriseNature || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="行业分类">{{ enterpriseDetailData.industryClass || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="所属行业">{{ enterpriseDetailData.industryName || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="成立日期">{{ parseTime(enterpriseDetailData.establishDate, "{y}-{m}-{d}") || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="注册地址">{{ enterpriseDetailData.registerAddress || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="法定代表人">{{ enterpriseDetailData.legalRepresentative || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="法定代表人证件类型">{{ enterpriseDetailData.legalCertType || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="法定代表人证件号码">{{ enterpriseDetailData.legalCertNo || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="经营状态">{{ formatEnterpriseStatus(enterpriseDetailData.status) }}</el-descriptions-item>
|
||
<el-descriptions-item label="风险等级">{{ formatEnterpriseRiskLevel(enterpriseDetailData.riskLevel) }}</el-descriptions-item>
|
||
<el-descriptions-item label="企业来源">{{ formatEnterpriseSource(enterpriseDetailData.entSource) }}</el-descriptions-item>
|
||
<el-descriptions-item label="数据来源">{{ formatEnterpriseDataSource(enterpriseDetailData.dataSource) }}</el-descriptions-item>
|
||
<el-descriptions-item label="创建时间">{{ parseTime(enterpriseDetailData.createTime) || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="股东1">{{ enterpriseDetailData.shareholder1 || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="股东2">{{ enterpriseDetailData.shareholder2 || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="股东3">{{ enterpriseDetailData.shareholder3 || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="股东4">{{ enterpriseDetailData.shareholder4 || "-" }}</el-descriptions-item>
|
||
<el-descriptions-item label="股东5">{{ enterpriseDetailData.shareholder5 || "-" }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="enterpriseDetailOpen = false">关闭</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<el-dialog
|
||
:title="upload.title"
|
||
:visible.sync="upload.open"
|
||
width="420px"
|
||
append-to-body
|
||
@close="handleImportDialogClose"
|
||
>
|
||
<el-upload
|
||
ref="upload"
|
||
:limit="1"
|
||
accept=".xlsx, .xls"
|
||
:headers="upload.headers"
|
||
:action="upload.url"
|
||
:disabled="upload.isUploading"
|
||
:on-progress="handleFileUploadProgress"
|
||
:on-success="handleFileSuccess"
|
||
:auto-upload="false"
|
||
drag
|
||
>
|
||
<i class="el-icon-upload" />
|
||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||
<div class="el-upload__tip" slot="tip">
|
||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline;" @click="importTemplate">
|
||
下载模板
|
||
</el-link>
|
||
<span class="upload-tip-text">模板包含“招投标主信息”和“供应商明细”两个 Sheet。</span>
|
||
</div>
|
||
</el-upload>
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button type="primary" @click="submitFileForm" :loading="upload.isUploading">确定</el-button>
|
||
<el-button @click="upload.open = false" :disabled="upload.isUploading">取消</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<import-result-dialog
|
||
:visible.sync="importResultVisible"
|
||
:content="importResultContent"
|
||
title="导入结果"
|
||
@close="handleImportResultClose"
|
||
/>
|
||
|
||
<el-dialog
|
||
title="招投标导入失败记录"
|
||
:visible.sync="failureDialogVisible"
|
||
width="1200px"
|
||
append-to-body
|
||
>
|
||
<el-alert
|
||
v-if="lastImportInfo"
|
||
:title="lastImportInfo"
|
||
type="info"
|
||
:closable="false"
|
||
style="margin-bottom: 15px"
|
||
/>
|
||
|
||
<el-table :data="failureList" v-loading="failureLoading">
|
||
<el-table-column label="失败Sheet" prop="sheetName" align="center" width="140" />
|
||
<el-table-column label="失败行号" prop="sheetRowNum" align="center" width="120">
|
||
<template slot-scope="scope">
|
||
<span>{{ scope.row.sheetRowNum ? `第${scope.row.sheetRowNum}行` : "-" }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="采购事项ID" prop="purchaseId" align="center" />
|
||
<el-table-column label="项目名称" prop="projectName" align="center" :show-overflow-tooltip="true" />
|
||
<el-table-column label="标的物名称" prop="subjectName" align="center" :show-overflow-tooltip="true" />
|
||
<el-table-column label="失败原因" prop="errorMessage" align="center" min-width="200" :show-overflow-tooltip="true" />
|
||
</el-table>
|
||
|
||
<pagination
|
||
v-show="failureTotal > 0"
|
||
:total="failureTotal"
|
||
:page.sync="failureQueryParams.pageNum"
|
||
:limit.sync="failureQueryParams.pageSize"
|
||
@pagination="getFailureList"
|
||
/>
|
||
|
||
<div slot="footer" class="dialog-footer">
|
||
<el-button @click="failureDialogVisible = false">关闭</el-button>
|
||
<el-button type="danger" plain @click="clearImportHistory">清除历史记录</el-button>
|
||
</div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
addTransaction,
|
||
delTransaction,
|
||
getImportFailures,
|
||
getImportStatus,
|
||
getTransaction,
|
||
listTransaction,
|
||
updateTransaction
|
||
} from "@/api/ccdiPurchaseTransaction";
|
||
import { getEnterpriseBaseInfo } from "@/api/ccdiEnterpriseBaseInfo";
|
||
import {
|
||
getDataSourceOptions,
|
||
getEnterpriseRiskLevelOptions,
|
||
getEnterpriseSourceOptions
|
||
} from "@/api/ccdiEnum";
|
||
import { getToken } from "@/utils/auth";
|
||
import ImportResultDialog from "@/components/ImportResultDialog.vue";
|
||
|
||
const createSupplierRow = () => ({
|
||
supplierName: "",
|
||
supplierUscc: "",
|
||
contactPerson: "",
|
||
contactPhone: "",
|
||
supplierBankAccount: "",
|
||
sortOrder: null,
|
||
isBidWinner: 0
|
||
});
|
||
|
||
const createDefaultForm = () => ({
|
||
purchaseId: null,
|
||
purchaseCategory: null,
|
||
projectName: null,
|
||
subjectName: null,
|
||
subjectDesc: null,
|
||
purchaseQty: null,
|
||
budgetAmount: null,
|
||
bidAmount: null,
|
||
actualAmount: null,
|
||
contractAmount: null,
|
||
settlementAmount: null,
|
||
purchaseMethod: null,
|
||
applyDate: null,
|
||
planApproveDate: null,
|
||
announceDate: null,
|
||
bidOpenDate: null,
|
||
contractSignDate: null,
|
||
expectedDeliveryDate: null,
|
||
actualDeliveryDate: null,
|
||
acceptanceDate: null,
|
||
settlementDate: null,
|
||
applicantId: null,
|
||
applicantName: null,
|
||
applyDepartment: null,
|
||
purchaseLeaderId: null,
|
||
purchaseLeaderName: null,
|
||
purchaseDepartment: null,
|
||
supplierList: []
|
||
});
|
||
|
||
export default {
|
||
name: "PurchaseTransaction",
|
||
components: { ImportResultDialog },
|
||
data() {
|
||
return {
|
||
loading: true,
|
||
ids: [],
|
||
single: true,
|
||
multiple: true,
|
||
showSearch: true,
|
||
total: 0,
|
||
transactionList: [],
|
||
title: "",
|
||
open: false,
|
||
detailOpen: false,
|
||
enterpriseDetailOpen: false,
|
||
enterpriseDetailLoading: false,
|
||
enterpriseDetailData: {},
|
||
transactionDetail: { supplierList: [] },
|
||
isAdd: false,
|
||
dateRange: [],
|
||
winnerSupplierIndex: null,
|
||
enterpriseRiskLevelOptions: [],
|
||
enterpriseSourceOptions: [],
|
||
enterpriseDataSourceOptions: [],
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 10,
|
||
projectName: null,
|
||
subjectName: null,
|
||
applicantName: null
|
||
},
|
||
form: createDefaultForm(),
|
||
rules: {
|
||
purchaseId: [
|
||
{ required: true, message: "采购事项ID不能为空", trigger: "blur" },
|
||
{ max: 32, message: "采购事项ID长度不能超过32个字符", trigger: "blur" }
|
||
],
|
||
purchaseCategory: [
|
||
{ required: true, message: "采购类别不能为空", trigger: "blur" },
|
||
{ max: 50, message: "采购类别长度不能超过50个字符", trigger: "blur" }
|
||
],
|
||
projectName: [
|
||
{ max: 200, message: "项目名称长度不能超过200个字符", trigger: "blur" }
|
||
],
|
||
subjectName: [
|
||
{ required: true, message: "标的物名称不能为空", trigger: "blur" },
|
||
{ max: 200, message: "标的物名称长度不能超过200个字符", trigger: "blur" }
|
||
],
|
||
subjectDesc: [
|
||
{ max: 500, message: "标的物描述长度不能超过500个字符", trigger: "blur" }
|
||
],
|
||
purchaseQty: [
|
||
{ required: true, message: "采购数量不能为空", trigger: "blur" },
|
||
{ type: "number", min: 0.0001, message: "采购数量必须大于0", trigger: "blur" }
|
||
],
|
||
budgetAmount: [
|
||
{ required: true, message: "预算金额不能为空", trigger: "blur" },
|
||
{ type: "number", min: 0.01, message: "预算金额必须大于0", trigger: "blur" }
|
||
],
|
||
bidAmount: [
|
||
{ type: "number", min: 0.01, message: "中标金额必须大于0", trigger: "blur" }
|
||
],
|
||
actualAmount: [
|
||
{ type: "number", min: 0.01, message: "实际采购金额必须大于0", trigger: "blur" }
|
||
],
|
||
contractAmount: [
|
||
{ type: "number", min: 0.01, message: "合同金额必须大于0", trigger: "blur" }
|
||
],
|
||
settlementAmount: [
|
||
{ type: "number", min: 0.01, message: "结算金额必须大于0", trigger: "blur" }
|
||
],
|
||
purchaseMethod: [
|
||
{ required: true, message: "采购方式不能为空", trigger: "blur" },
|
||
{ max: 50, message: "采购方式长度不能超过50个字符", trigger: "blur" }
|
||
],
|
||
applyDate: [
|
||
{ required: true, message: "采购申请日期不能为空", trigger: "change" }
|
||
],
|
||
applicantName: [
|
||
{ required: true, message: "申请人姓名不能为空", trigger: "blur" },
|
||
{ max: 50, message: "申请人姓名长度不能超过50个字符", trigger: "blur" }
|
||
],
|
||
applicantId: [
|
||
{ required: true, message: "申请人工号不能为空", trigger: "blur" },
|
||
{ pattern: /^\d{7}$/, message: "申请人工号必须为7位数字", trigger: "blur" }
|
||
],
|
||
applyDepartment: [
|
||
{ required: true, message: "申请部门不能为空", trigger: "blur" },
|
||
{ max: 100, message: "申请部门长度不能超过100个字符", trigger: "blur" }
|
||
],
|
||
purchaseLeaderName: [
|
||
{ max: 50, message: "采购负责人姓名长度不能超过50个字符", trigger: "blur" }
|
||
],
|
||
purchaseLeaderId: [
|
||
{ pattern: /^\d{7}$/, message: "采购负责人工号必须为7位数字", trigger: "blur" }
|
||
],
|
||
purchaseDepartment: [
|
||
{ max: 100, message: "采购部门长度不能超过100个字符", trigger: "blur" }
|
||
]
|
||
},
|
||
upload: {
|
||
open: false,
|
||
title: "",
|
||
isUploading: false,
|
||
headers: { Authorization: "Bearer " + getToken() },
|
||
url: process.env.VUE_APP_BASE_API + "/ccdi/purchaseTransaction/importData"
|
||
},
|
||
importResultVisible: false,
|
||
importResultContent: "",
|
||
importPollingTimer: null,
|
||
showFailureButton: false,
|
||
currentTaskId: null,
|
||
failureDialogVisible: false,
|
||
failureList: [],
|
||
failureLoading: false,
|
||
failureTotal: 0,
|
||
failureQueryParams: {
|
||
pageNum: 1,
|
||
pageSize: 10
|
||
}
|
||
};
|
||
},
|
||
computed: {
|
||
lastImportInfo() {
|
||
const savedTask = this.getImportTaskFromStorage();
|
||
if (savedTask && savedTask.totalCount) {
|
||
return `导入时间: ${this.parseTime(savedTask.saveTime)} | 总数: ${savedTask.totalCount}条 | 成功: ${savedTask.successCount}条 | 失败: ${savedTask.failureCount}条`;
|
||
}
|
||
return "";
|
||
}
|
||
},
|
||
created() {
|
||
this.getList();
|
||
this.restoreImportState();
|
||
this.loadEnterpriseDetailOptions();
|
||
},
|
||
beforeDestroy() {
|
||
if (this.importPollingTimer) {
|
||
clearInterval(this.importPollingTimer);
|
||
this.importPollingTimer = null;
|
||
}
|
||
},
|
||
methods: {
|
||
loadEnterpriseDetailOptions() {
|
||
return Promise.all([
|
||
getEnterpriseRiskLevelOptions(),
|
||
getEnterpriseSourceOptions(),
|
||
getDataSourceOptions()
|
||
]).then(([riskRes, sourceRes, dataSourceRes]) => {
|
||
this.enterpriseRiskLevelOptions = riskRes.data || [];
|
||
this.enterpriseSourceOptions = sourceRes.data || [];
|
||
this.enterpriseDataSourceOptions = dataSourceRes.data || [];
|
||
}).catch(() => {
|
||
this.enterpriseRiskLevelOptions = [];
|
||
this.enterpriseSourceOptions = [];
|
||
this.enterpriseDataSourceOptions = [];
|
||
});
|
||
},
|
||
getEnterpriseOptionLabel(options, value) {
|
||
if (!value) return "-";
|
||
const matched = (options || []).find(item => item.value === value);
|
||
return matched ? matched.label : value;
|
||
},
|
||
getList() {
|
||
this.loading = true;
|
||
const params = this.addDateRangeFlat(this.queryParams, this.dateRange, "applyDateStart", "applyDateEnd");
|
||
listTransaction(params).then(response => {
|
||
this.transactionList = response.rows || [];
|
||
this.total = response.total || 0;
|
||
this.loading = false;
|
||
}).catch(() => {
|
||
this.loading = false;
|
||
});
|
||
},
|
||
restoreImportState() {
|
||
const savedTask = this.getImportTaskFromStorage();
|
||
if (!savedTask) {
|
||
this.showFailureButton = false;
|
||
this.currentTaskId = null;
|
||
return;
|
||
}
|
||
if (savedTask.hasFailures && savedTask.taskId) {
|
||
this.currentTaskId = savedTask.taskId;
|
||
this.showFailureButton = true;
|
||
}
|
||
},
|
||
getLastImportTooltip() {
|
||
const savedTask = this.getImportTaskFromStorage();
|
||
if (savedTask && savedTask.saveTime) {
|
||
const date = new Date(savedTask.saveTime);
|
||
return `上次导入: ${this.parseTime(date, "{y}-{m}-{d} {h}:{i}")}`;
|
||
}
|
||
return "";
|
||
},
|
||
formatAmount(amount) {
|
||
if (amount === null || amount === undefined || amount === "") return "-";
|
||
return parseFloat(amount).toLocaleString("zh-CN", {
|
||
minimumFractionDigits: 2,
|
||
maximumFractionDigits: 2
|
||
});
|
||
},
|
||
formatEnterpriseRiskLevel(value) {
|
||
return this.getEnterpriseOptionLabel(this.enterpriseRiskLevelOptions, value);
|
||
},
|
||
formatEnterpriseSource(value) {
|
||
return this.getEnterpriseOptionLabel(this.enterpriseSourceOptions, value);
|
||
},
|
||
formatEnterpriseDataSource(value) {
|
||
return this.getEnterpriseOptionLabel(this.enterpriseDataSourceOptions, value);
|
||
},
|
||
formatEnterpriseStatus(value) {
|
||
return value || "-";
|
||
},
|
||
getSupplierFieldRules(field) {
|
||
const ruleMap = {
|
||
supplierName: [
|
||
{ required: true, message: "供应商名称不能为空", trigger: "blur" }
|
||
],
|
||
supplierUscc: [
|
||
{ required: true, message: "统一信用代码不能为空", trigger: "blur" }
|
||
],
|
||
};
|
||
return ruleMap[field] || [];
|
||
},
|
||
cancel() {
|
||
this.open = false;
|
||
this.reset();
|
||
},
|
||
reset() {
|
||
this.form = createDefaultForm();
|
||
this.winnerSupplierIndex = null;
|
||
this.resetForm("form");
|
||
},
|
||
resetEnterpriseDetail() {
|
||
this.enterpriseDetailOpen = false;
|
||
this.enterpriseDetailLoading = false;
|
||
this.enterpriseDetailData = {};
|
||
},
|
||
handleQuery() {
|
||
this.queryParams.pageNum = 1;
|
||
this.getList();
|
||
},
|
||
resetQuery() {
|
||
this.dateRange = [];
|
||
this.resetForm("queryForm");
|
||
this.handleQuery();
|
||
},
|
||
handleSelectionChange(selection) {
|
||
this.ids = selection.map(item => item.purchaseId);
|
||
this.single = selection.length !== 1;
|
||
this.multiple = !selection.length;
|
||
},
|
||
handleAdd() {
|
||
this.reset();
|
||
this.open = true;
|
||
this.title = "添加招投标信息";
|
||
this.isAdd = true;
|
||
},
|
||
handleUpdate(row) {
|
||
this.reset();
|
||
const purchaseId = row.purchaseId || this.ids[0];
|
||
getTransaction(purchaseId).then(response => {
|
||
this.applyFormData(response.data);
|
||
this.open = true;
|
||
this.title = "修改招投标信息";
|
||
this.isAdd = false;
|
||
this.$nextTick(() => {
|
||
this.$refs.form && this.$refs.form.clearValidate();
|
||
});
|
||
});
|
||
},
|
||
handleDetail(row) {
|
||
getTransaction(row.purchaseId).then(response => {
|
||
this.transactionDetail = this.normalizeTransactionData(response.data);
|
||
this.detailOpen = true;
|
||
});
|
||
},
|
||
async handleSupplierEnterpriseDetail(row) {
|
||
const socialCreditCode = row && row.supplierUscc ? row.supplierUscc.trim() : "";
|
||
if (!socialCreditCode) {
|
||
this.$message.warning("暂无企业信息");
|
||
return;
|
||
}
|
||
this.enterpriseDetailLoading = true;
|
||
this.enterpriseDetailData = {};
|
||
try {
|
||
const response = await getEnterpriseBaseInfo(socialCreditCode);
|
||
if (!response || !response.data) {
|
||
this.$message.warning("暂无企业信息");
|
||
return;
|
||
}
|
||
this.enterpriseDetailData = response.data;
|
||
this.enterpriseDetailOpen = true;
|
||
} catch (error) {
|
||
this.$message.warning("暂无企业信息");
|
||
} finally {
|
||
this.enterpriseDetailLoading = false;
|
||
}
|
||
},
|
||
handleAddSupplier() {
|
||
this.form.supplierList.push(createSupplierRow());
|
||
},
|
||
handleRemoveSupplier(index) {
|
||
this.form.supplierList.splice(index, 1);
|
||
if (this.winnerSupplierIndex === index) {
|
||
this.winnerSupplierIndex = null;
|
||
} else if (this.winnerSupplierIndex !== null && this.winnerSupplierIndex > index) {
|
||
this.winnerSupplierIndex -= 1;
|
||
}
|
||
},
|
||
submitForm() {
|
||
this.$refs.form.validate(valid => {
|
||
if (!valid) {
|
||
return;
|
||
}
|
||
const payload = this.buildSubmitPayload();
|
||
if (!this.validateSupplierDuplicates(payload.supplierList)) {
|
||
return;
|
||
}
|
||
const request = this.isAdd ? addTransaction(payload) : updateTransaction(payload);
|
||
request.then(() => {
|
||
this.$modal.msgSuccess(this.isAdd ? "新增成功" : "修改成功");
|
||
this.open = false;
|
||
this.getList();
|
||
});
|
||
});
|
||
},
|
||
buildSubmitPayload() {
|
||
const payload = JSON.parse(JSON.stringify(this.form));
|
||
payload.supplierList = this.form.supplierList
|
||
.filter(item => this.hasAnySupplierValue(item))
|
||
.map((item, index) => ({
|
||
...item,
|
||
supplierName: item.supplierName ? item.supplierName.trim() : "",
|
||
supplierUscc: item.supplierUscc ? item.supplierUscc.trim() : "",
|
||
contactPerson: item.contactPerson ? item.contactPerson.trim() : "",
|
||
contactPhone: item.contactPhone ? item.contactPhone.trim() : "",
|
||
supplierBankAccount: item.supplierBankAccount ? item.supplierBankAccount.trim() : "",
|
||
sortOrder: item.sortOrder || index + 1,
|
||
isBidWinner: this.winnerSupplierIndex === index ? 1 : 0
|
||
}));
|
||
return payload;
|
||
},
|
||
validateSupplierDuplicates(supplierList) {
|
||
const seen = new Set();
|
||
for (const item of supplierList) {
|
||
const key = `${item.supplierName || ""}|${item.supplierUscc || ""}`;
|
||
if (seen.has(key)) {
|
||
this.$message.error("同一招投标事项存在重复供应商,请检查供应商名称和统一信用代码");
|
||
return false;
|
||
}
|
||
seen.add(key);
|
||
}
|
||
return true;
|
||
},
|
||
hasAnySupplierValue(item) {
|
||
return !!(
|
||
item.supplierName ||
|
||
item.supplierUscc ||
|
||
item.contactPerson ||
|
||
item.contactPhone ||
|
||
item.supplierBankAccount
|
||
);
|
||
},
|
||
handleDelete(row) {
|
||
const purchaseIds = row.purchaseId || this.ids;
|
||
this.$modal.confirm(`是否确认删除采购事项ID为"${purchaseIds}"的数据项?`).then(function() {
|
||
return delTransaction(purchaseIds);
|
||
}).then(() => {
|
||
this.getList();
|
||
this.$modal.msgSuccess("删除成功");
|
||
}).catch(() => {});
|
||
},
|
||
handleImport() {
|
||
this.upload.title = "招投标信息导入";
|
||
this.upload.open = true;
|
||
},
|
||
importTemplate() {
|
||
this.download("ccdi/purchaseTransaction/importTemplate", {}, `招投标信息维护导入模板_${new Date().getTime()}.xlsx`);
|
||
},
|
||
handleFileUploadProgress() {
|
||
this.upload.isUploading = true;
|
||
},
|
||
handleFileSuccess(response) {
|
||
this.upload.isUploading = false;
|
||
this.upload.open = false;
|
||
|
||
if (response.code !== 200) {
|
||
this.$modal.msgError(response.msg);
|
||
return;
|
||
}
|
||
if (!response.data || !response.data.taskId) {
|
||
this.$modal.msgError("导入任务创建失败: 缺少任务ID");
|
||
this.upload.open = true;
|
||
return;
|
||
}
|
||
|
||
const taskId = response.data.taskId;
|
||
if (this.importPollingTimer) {
|
||
clearInterval(this.importPollingTimer);
|
||
this.importPollingTimer = null;
|
||
}
|
||
|
||
this.clearImportTaskFromStorage();
|
||
this.saveImportTaskToStorage({
|
||
taskId,
|
||
status: "PROCESSING",
|
||
timestamp: Date.now(),
|
||
hasFailures: false
|
||
});
|
||
this.showFailureButton = false;
|
||
this.currentTaskId = taskId;
|
||
|
||
this.$notify({
|
||
title: "导入任务已提交",
|
||
message: "正在后台处理中,处理完成后将通知您",
|
||
type: "info",
|
||
duration: 3000
|
||
});
|
||
|
||
this.startImportStatusPolling(taskId);
|
||
},
|
||
startImportStatusPolling(taskId) {
|
||
let pollCount = 0;
|
||
const maxPolls = 150;
|
||
this.importPollingTimer = setInterval(async () => {
|
||
try {
|
||
pollCount += 1;
|
||
if (pollCount > maxPolls) {
|
||
clearInterval(this.importPollingTimer);
|
||
this.$modal.msgWarning("导入任务处理超时,请联系管理员");
|
||
return;
|
||
}
|
||
const response = await getImportStatus(taskId);
|
||
if (response.data && response.data.status !== "PROCESSING") {
|
||
clearInterval(this.importPollingTimer);
|
||
this.handleImportComplete(response.data);
|
||
}
|
||
} catch (error) {
|
||
clearInterval(this.importPollingTimer);
|
||
this.$modal.msgError("查询导入状态失败: " + error.message);
|
||
}
|
||
}, 2000);
|
||
},
|
||
getFailureList() {
|
||
this.failureLoading = true;
|
||
getImportFailures(
|
||
this.currentTaskId,
|
||
this.failureQueryParams.pageNum,
|
||
this.failureQueryParams.pageSize
|
||
).then(response => {
|
||
this.failureList = response.rows;
|
||
this.failureTotal = response.total;
|
||
this.failureLoading = false;
|
||
}).catch(error => {
|
||
this.failureLoading = false;
|
||
if (error.response) {
|
||
const status = error.response.status;
|
||
if (status === 404) {
|
||
this.$modal.msgWarning("导入记录已过期,无法查看失败记录");
|
||
this.clearImportTaskFromStorage();
|
||
this.showFailureButton = false;
|
||
this.currentTaskId = null;
|
||
this.failureDialogVisible = false;
|
||
} else if (status === 500) {
|
||
this.$modal.msgError("服务器错误,请稍后重试");
|
||
} else {
|
||
this.$modal.msgError(`查询失败: ${error.response.data.msg || "未知错误"}`);
|
||
}
|
||
} else if (error.request) {
|
||
this.$modal.msgError("网络连接失败,请检查网络");
|
||
} else {
|
||
this.$modal.msgError("查询失败记录失败: " + error.message);
|
||
}
|
||
});
|
||
},
|
||
viewImportFailures() {
|
||
this.failureDialogVisible = true;
|
||
this.getFailureList();
|
||
},
|
||
handleImportComplete(statusResult) {
|
||
this.saveImportTaskToStorage({
|
||
taskId: statusResult.taskId,
|
||
status: statusResult.status,
|
||
hasFailures: statusResult.failureCount > 0,
|
||
totalCount: statusResult.totalCount,
|
||
successCount: statusResult.successCount,
|
||
failureCount: statusResult.failureCount
|
||
});
|
||
|
||
if (statusResult.status === "SUCCESS") {
|
||
this.$notify({
|
||
title: "导入完成",
|
||
message: `全部成功! 共导入${statusResult.totalCount}条数据`,
|
||
type: "success",
|
||
duration: 5000
|
||
});
|
||
this.showFailureButton = false;
|
||
this.getList();
|
||
return;
|
||
}
|
||
|
||
if (statusResult.failureCount > 0) {
|
||
this.$notify({
|
||
title: "导入完成",
|
||
message: `成功${statusResult.successCount}条,失败${statusResult.failureCount}条`,
|
||
type: "warning",
|
||
duration: 5000
|
||
});
|
||
this.showFailureButton = true;
|
||
this.currentTaskId = statusResult.taskId;
|
||
this.getList();
|
||
}
|
||
},
|
||
handleImportResultClose() {
|
||
this.importResultVisible = false;
|
||
this.importResultContent = "";
|
||
},
|
||
submitFileForm() {
|
||
this.$refs.upload.submit();
|
||
},
|
||
handleImportDialogClose() {
|
||
this.upload.isUploading = false;
|
||
this.$refs.upload && this.$refs.upload.clearFiles();
|
||
},
|
||
saveImportTaskToStorage(taskData) {
|
||
try {
|
||
localStorage.setItem(
|
||
"purchase_transaction_import_last_task",
|
||
JSON.stringify({ ...taskData, saveTime: Date.now() })
|
||
);
|
||
} catch (error) {
|
||
console.error("保存导入任务状态失败:", error);
|
||
}
|
||
},
|
||
getImportTaskFromStorage() {
|
||
try {
|
||
const data = localStorage.getItem("purchase_transaction_import_last_task");
|
||
if (!data) return null;
|
||
const task = JSON.parse(data);
|
||
if (!task || !task.taskId) {
|
||
this.clearImportTaskFromStorage();
|
||
return null;
|
||
}
|
||
if (task.saveTime && typeof task.saveTime !== "number") {
|
||
this.clearImportTaskFromStorage();
|
||
return null;
|
||
}
|
||
const sevenDays = 7 * 24 * 60 * 60 * 1000;
|
||
if (Date.now() - task.saveTime > sevenDays) {
|
||
this.clearImportTaskFromStorage();
|
||
return null;
|
||
}
|
||
return task;
|
||
} catch (error) {
|
||
console.error("读取导入任务状态失败:", error);
|
||
this.clearImportTaskFromStorage();
|
||
return null;
|
||
}
|
||
},
|
||
clearImportHistory() {
|
||
this.$confirm("确认清除上次导入记录?", "提示", {
|
||
confirmButtonText: "确定",
|
||
cancelButtonText: "取消",
|
||
type: "warning"
|
||
}).then(() => {
|
||
this.clearImportTaskFromStorage();
|
||
this.showFailureButton = false;
|
||
this.currentTaskId = null;
|
||
this.failureDialogVisible = false;
|
||
this.$message.success("已清除");
|
||
}).catch(() => {});
|
||
},
|
||
clearImportTaskFromStorage() {
|
||
try {
|
||
localStorage.removeItem("purchase_transaction_import_last_task");
|
||
} catch (error) {
|
||
console.error("清除导入任务状态失败:", error);
|
||
}
|
||
},
|
||
applyFormData(data) {
|
||
const normalized = this.normalizeTransactionData(data);
|
||
this.form = normalized;
|
||
this.winnerSupplierIndex = normalized.supplierList.findIndex(item => item.isBidWinner === 1);
|
||
if (this.winnerSupplierIndex < 0) {
|
||
this.winnerSupplierIndex = null;
|
||
}
|
||
},
|
||
normalizeTransactionData(data) {
|
||
const normalized = {
|
||
...createDefaultForm(),
|
||
...(data || {})
|
||
};
|
||
let supplierList = Array.isArray(data && data.supplierList) ? data.supplierList : [];
|
||
if (!supplierList.length && data && data.supplierName) {
|
||
supplierList = [{
|
||
supplierName: data.supplierName || "",
|
||
supplierUscc: data.supplierUscc || "",
|
||
contactPerson: data.contactPerson || "",
|
||
contactPhone: data.contactPhone || "",
|
||
supplierBankAccount: data.supplierBankAccount || "",
|
||
sortOrder: 1,
|
||
isBidWinner: 1
|
||
}];
|
||
}
|
||
normalized.supplierList = supplierList.map((item, index) => ({
|
||
...createSupplierRow(),
|
||
...item,
|
||
sortOrder: item.sortOrder || index + 1,
|
||
isBidWinner: item.isBidWinner === 1 ? 1 : 0
|
||
}));
|
||
return normalized;
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.query-form ::v-deep .el-row {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.query-form ::v-deep .el-col {
|
||
float: none;
|
||
}
|
||
|
||
.query-form ::v-deep .el-form-item {
|
||
margin-right: 0;
|
||
}
|
||
|
||
.detail-container {
|
||
padding: 0 20px;
|
||
}
|
||
|
||
.el-divider {
|
||
margin: 16px 0;
|
||
}
|
||
|
||
.supplier-toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.supplier-toolbar__tip {
|
||
color: #606266;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.supplier-empty {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.supplier-table {
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.supplier-form-item {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.upload-tip-text {
|
||
margin-left: 8px;
|
||
color: #909399;
|
||
font-size: 12px;
|
||
}
|
||
</style>
|