2026-02-06 16:44:05 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="app-container">
|
|
|
|
|
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
|
|
|
|
|
<el-form-item label="项目名称" prop="projectName">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="queryParams.projectName"
|
|
|
|
|
|
placeholder="请输入项目名称"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
style="width: 240px"
|
|
|
|
|
|
@keyup.enter.native="handleQuery"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="标的物名称" prop="subjectName">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="queryParams.subjectName"
|
|
|
|
|
|
placeholder="请输入标的物名称"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
style="width: 240px"
|
|
|
|
|
|
@keyup.enter.native="handleQuery"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="申请人" prop="applicantName">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model="queryParams.applicantName"
|
|
|
|
|
|
placeholder="请输入申请人"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
style="width: 240px"
|
|
|
|
|
|
@keyup.enter.native="handleQuery"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="申请日期">
|
|
|
|
|
|
<el-date-picker
|
|
|
|
|
|
v-model="dateRange"
|
|
|
|
|
|
style="width: 240px"
|
|
|
|
|
|
value-format="yyyy-MM-dd"
|
|
|
|
|
|
type="daterange"
|
|
|
|
|
|
range-separator="-"
|
|
|
|
|
|
start-placeholder="开始日期"
|
|
|
|
|
|
end-placeholder="结束日期"
|
2026-04-22 16:20:37 +08:00
|
|
|
|
/>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<el-row :gutter="10" class="mb8">
|
2026-04-23 16:07:17 +08:00
|
|
|
|
<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>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
<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>
|
2026-02-08 14:02:03 +08:00
|
|
|
|
<el-col :span="1.5" v-if="showFailureButton">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-tooltip :content="getLastImportTooltip()" placement="top">
|
2026-02-08 14:02:03 +08:00
|
|
|
|
<el-button
|
|
|
|
|
|
type="warning"
|
|
|
|
|
|
plain
|
|
|
|
|
|
icon="el-icon-warning"
|
|
|
|
|
|
size="mini"
|
|
|
|
|
|
@click="viewImportFailures"
|
|
|
|
|
|
>查看导入失败记录</el-button>
|
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
</el-col>
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-row>
|
|
|
|
|
|
|
|
|
|
|
|
<el-table v-loading="loading" :data="transactionList" @selection-change="handleSelectionChange">
|
|
|
|
|
|
<el-table-column type="selection" width="55" align="center" />
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<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>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
<el-table-column label="预算金额(元)" align="center" prop="budgetAmount" width="120">
|
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
{{ formatAmount(scope.row.budgetAmount) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-table-column label="申请人" align="center" prop="applicantName" width="100" />
|
|
|
|
|
|
<el-table-column label="申请部门" align="center" prop="applyDepartment" width="120" />
|
2026-02-06 16:44:05 +08:00
|
|
|
|
<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
|
2026-04-22 16:20:37 +08:00
|
|
|
|
v-show="total > 0"
|
2026-02-06 16:44:05 +08:00
|
|
|
|
:total="total"
|
|
|
|
|
|
:page.sync="queryParams.pageNum"
|
|
|
|
|
|
:limit.sync="queryParams.pageSize"
|
|
|
|
|
|
@pagination="getList"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-dialog :title="title" :visible.sync="open" width="80%" append-to-body>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
<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">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-input-number v-model="form.purchaseQty" :min="0" :precision="2" placeholder="请输入采购数量" style="width: 100%" />
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-form-item label="预算金额(元)" prop="budgetAmount">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-input-number v-model="form.budgetAmount" :min="0" :precision="2" placeholder="请输入预算金额" style="width: 100%" />
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-form-item label="中标金额(元)" prop="bidAmount">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-input-number v-model="form.bidAmount" :min="0" :precision="2" placeholder="请输入中标金额" style="width: 100%" />
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
<el-row :gutter="16">
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-form-item label="实际采购金额(元)" prop="actualAmount">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-input-number v-model="form.actualAmount" :min="0" :precision="2" placeholder="请输入实际采购金额" style="width: 100%" />
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-form-item label="合同金额(元)" prop="contractAmount">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-input-number v-model="form.contractAmount" :min="0" :precision="2" placeholder="请输入合同金额" style="width: 100%" />
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-form-item label="结算金额(元)" prop="settlementAmount">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-input-number v-model="form.settlementAmount" :min="0" :precision="2" placeholder="请输入结算金额" style="width: 100%" />
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</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>
|
|
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<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>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
|
|
|
|
|
|
<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>
|
|
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-dialog title="招投标信息详情" :visible.sync="detailOpen" width="1200px" append-to-body>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
<div class="detail-container">
|
|
|
|
|
|
<el-divider content-position="left">基本信息</el-divider>
|
|
|
|
|
|
<el-descriptions :column="2" border>
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<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>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
|
|
|
|
|
|
<el-divider content-position="left">数量与金额</el-divider>
|
|
|
|
|
|
<el-descriptions :column="3" border>
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-descriptions-item label="采购数量">{{ transactionDetail.purchaseQty || "-" }}</el-descriptions-item>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
<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>
|
|
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<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>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
|
|
|
|
|
|
<el-divider content-position="left">重要日期</el-divider>
|
|
|
|
|
|
<el-descriptions :column="3" border>
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<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>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
|
|
|
|
|
|
<el-divider content-position="left">申请人信息</el-divider>
|
|
|
|
|
|
<el-descriptions :column="2" border>
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<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>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
|
|
|
|
|
|
<el-divider content-position="left">采购负责人信息</el-divider>
|
|
|
|
|
|
<el-descriptions :column="2" border>
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<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>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
|
|
|
|
|
|
<el-divider content-position="left">审计信息</el-divider>
|
|
|
|
|
|
<el-descriptions :column="2" border>
|
|
|
|
|
|
<el-descriptions-item label="创建时间">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
{{ transactionDetail.createTime ? parseTime(transactionDetail.createTime) : "-" }}
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-descriptions-item>
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-descriptions-item label="创建人">{{ transactionDetail.createdBy || "-" }}</el-descriptions-item>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
<el-descriptions-item label="更新时间">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
{{ transactionDetail.updateTime ? parseTime(transactionDetail.updateTime) : "-" }}
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-descriptions-item>
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-descriptions-item label="更新人">{{ transactionDetail.updatedBy || "-" }}</el-descriptions-item>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div slot="footer" class="dialog-footer">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-button @click="detailOpen = false" icon="el-icon-close">关闭</el-button>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<el-dialog
|
|
|
|
|
|
:title="upload.title"
|
|
|
|
|
|
:visible.sync="upload.open"
|
2026-04-22 16:20:37 +08:00
|
|
|
|
width="420px"
|
2026-02-06 16:44:05 +08:00
|
|
|
|
append-to-body
|
|
|
|
|
|
@close="handleImportDialogClose"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-upload
|
|
|
|
|
|
ref="upload"
|
|
|
|
|
|
:limit="1"
|
|
|
|
|
|
accept=".xlsx, .xls"
|
|
|
|
|
|
:headers="upload.headers"
|
2026-02-09 01:12:22 +08:00
|
|
|
|
:action="upload.url"
|
2026-02-06 16:44:05 +08:00
|
|
|
|
:disabled="upload.isUploading"
|
|
|
|
|
|
:on-progress="handleFileUploadProgress"
|
|
|
|
|
|
:on-success="handleFileSuccess"
|
|
|
|
|
|
:auto-upload="false"
|
|
|
|
|
|
drag
|
|
|
|
|
|
>
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<i class="el-icon-upload" />
|
2026-02-06 16:44:05 +08:00
|
|
|
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
|
|
|
|
<div class="el-upload__tip" slot="tip">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline;" @click="importTemplate">
|
|
|
|
|
|
下载模板
|
|
|
|
|
|
</el-link>
|
|
|
|
|
|
<span class="upload-tip-text">模板包含“招投标主信息”和“供应商明细”两个 Sheet。</span>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</el-upload>
|
|
|
|
|
|
<div slot="footer" class="dialog-footer">
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-button type="primary" @click="submitFileForm" :loading="upload.isUploading">确定</el-button>
|
|
|
|
|
|
<el-button @click="upload.open = false" :disabled="upload.isUploading">取消</el-button>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<import-result-dialog
|
|
|
|
|
|
:visible.sync="importResultVisible"
|
|
|
|
|
|
:content="importResultContent"
|
|
|
|
|
|
title="导入结果"
|
|
|
|
|
|
@close="handleImportResultClose"
|
|
|
|
|
|
/>
|
2026-02-08 14:02:10 +08:00
|
|
|
|
|
|
|
|
|
|
<el-dialog
|
2026-04-22 16:20:37 +08:00
|
|
|
|
title="招投标导入失败记录"
|
2026-02-08 14:02:10 +08:00
|
|
|
|
: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="采购事项ID" prop="purchaseId" align="center" />
|
2026-04-22 16:20:37 +08:00
|
|
|
|
<el-table-column label="项目名称" prop="projectName" align="center" :show-overflow-tooltip="true" />
|
|
|
|
|
|
<el-table-column label="标的物名称" prop="subjectName" align="center" :show-overflow-tooltip="true" />
|
2026-02-08 14:02:10 +08:00
|
|
|
|
<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>
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
2026-02-06 17:22:59 +08:00
|
|
|
|
import {
|
|
|
|
|
|
addTransaction,
|
|
|
|
|
|
delTransaction,
|
|
|
|
|
|
getImportFailures,
|
|
|
|
|
|
getImportStatus,
|
|
|
|
|
|
getTransaction,
|
|
|
|
|
|
listTransaction,
|
|
|
|
|
|
updateTransaction
|
|
|
|
|
|
} from "@/api/ccdiPurchaseTransaction";
|
2026-04-22 16:20:37 +08:00
|
|
|
|
import { getToken } from "@/utils/auth";
|
2026-02-06 16:44:05 +08:00
|
|
|
|
import ImportResultDialog from "@/components/ImportResultDialog.vue";
|
|
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
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: []
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-02-06 16:44:05 +08:00
|
|
|
|
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,
|
2026-04-22 16:20:37 +08:00
|
|
|
|
transactionDetail: { supplierList: [] },
|
2026-02-06 16:44:05 +08:00
|
|
|
|
isAdd: false,
|
|
|
|
|
|
dateRange: [],
|
2026-04-22 16:20:37 +08:00
|
|
|
|
winnerSupplierIndex: null,
|
2026-02-06 16:44:05 +08:00
|
|
|
|
queryParams: {
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
pageSize: 10,
|
|
|
|
|
|
projectName: null,
|
|
|
|
|
|
subjectName: null,
|
|
|
|
|
|
applicantName: null
|
|
|
|
|
|
},
|
2026-04-22 16:20:37 +08:00
|
|
|
|
form: createDefaultForm(),
|
2026-02-06 16:44:05 +08:00
|
|
|
|
rules: {
|
|
|
|
|
|
purchaseId: [
|
|
|
|
|
|
{ required: true, message: "采购事项ID不能为空", trigger: "blur" },
|
|
|
|
|
|
{ max: 32, message: "采购事项ID长度不能超过32个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
purchaseCategory: [
|
2026-02-06 17:22:59 +08:00
|
|
|
|
{ required: true, message: "采购类别不能为空", trigger: "blur" },
|
2026-02-06 16:44:05 +08:00
|
|
|
|
{ max: 50, message: "采购类别长度不能超过50个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
projectName: [
|
|
|
|
|
|
{ max: 200, message: "项目名称长度不能超过200个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
subjectName: [
|
2026-02-06 17:22:59 +08:00
|
|
|
|
{ required: true, message: "标的物名称不能为空", trigger: "blur" },
|
2026-02-06 16:44:05 +08:00
|
|
|
|
{ max: 200, message: "标的物名称长度不能超过200个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
subjectDesc: [
|
|
|
|
|
|
{ max: 500, message: "标的物描述长度不能超过500个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
2026-02-06 17:22:59 +08:00
|
|
|
|
purchaseQty: [
|
|
|
|
|
|
{ required: true, message: "采购数量不能为空", trigger: "blur" },
|
2026-04-22 16:20:37 +08:00
|
|
|
|
{ type: "number", min: 0.0001, message: "采购数量必须大于0", trigger: "blur" }
|
2026-02-06 17:22:59 +08:00
|
|
|
|
],
|
|
|
|
|
|
budgetAmount: [
|
|
|
|
|
|
{ required: true, message: "预算金额不能为空", trigger: "blur" },
|
2026-04-22 16:20:37 +08:00
|
|
|
|
{ type: "number", min: 0.01, message: "预算金额必须大于0", trigger: "blur" }
|
2026-02-06 17:22:59 +08:00
|
|
|
|
],
|
|
|
|
|
|
bidAmount: [
|
2026-04-22 16:20:37 +08:00
|
|
|
|
{ type: "number", min: 0.01, message: "中标金额必须大于0", trigger: "blur" }
|
2026-02-06 17:22:59 +08:00
|
|
|
|
],
|
|
|
|
|
|
actualAmount: [
|
2026-04-22 16:20:37 +08:00
|
|
|
|
{ type: "number", min: 0.01, message: "实际采购金额必须大于0", trigger: "blur" }
|
2026-02-06 17:22:59 +08:00
|
|
|
|
],
|
|
|
|
|
|
contractAmount: [
|
2026-04-22 16:20:37 +08:00
|
|
|
|
{ type: "number", min: 0.01, message: "合同金额必须大于0", trigger: "blur" }
|
2026-02-06 17:22:59 +08:00
|
|
|
|
],
|
|
|
|
|
|
settlementAmount: [
|
2026-04-22 16:20:37 +08:00
|
|
|
|
{ type: "number", min: 0.01, message: "结算金额必须大于0", trigger: "blur" }
|
2026-02-06 17:22:59 +08:00
|
|
|
|
],
|
2026-02-06 16:44:05 +08:00
|
|
|
|
purchaseMethod: [
|
2026-02-06 17:22:59 +08:00
|
|
|
|
{ required: true, message: "采购方式不能为空", trigger: "blur" },
|
2026-02-06 16:44:05 +08:00
|
|
|
|
{ max: 50, message: "采购方式长度不能超过50个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
2026-02-06 17:22:59 +08:00
|
|
|
|
applyDate: [
|
|
|
|
|
|
{ required: true, message: "采购申请日期不能为空", trigger: "change" }
|
|
|
|
|
|
],
|
2026-02-06 16:44:05 +08:00
|
|
|
|
applicantName: [
|
2026-02-06 17:22:59 +08:00
|
|
|
|
{ required: true, message: "申请人姓名不能为空", trigger: "blur" },
|
2026-02-06 16:44:05 +08:00
|
|
|
|
{ max: 50, message: "申请人姓名长度不能超过50个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
applicantId: [
|
2026-02-06 17:22:59 +08:00
|
|
|
|
{ required: true, message: "申请人工号不能为空", trigger: "blur" },
|
|
|
|
|
|
{ pattern: /^\d{7}$/, message: "申请人工号必须为7位数字", trigger: "blur" }
|
2026-02-06 16:44:05 +08:00
|
|
|
|
],
|
|
|
|
|
|
applyDepartment: [
|
2026-02-06 17:22:59 +08:00
|
|
|
|
{ required: true, message: "申请部门不能为空", trigger: "blur" },
|
2026-02-06 16:44:05 +08:00
|
|
|
|
{ max: 100, message: "申请部门长度不能超过100个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
purchaseLeaderName: [
|
|
|
|
|
|
{ max: 50, message: "采购负责人姓名长度不能超过50个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
purchaseLeaderId: [
|
2026-02-06 17:22:59 +08:00
|
|
|
|
{ pattern: /^\d{7}$/, message: "采购负责人工号必须为7位数字", trigger: "blur" }
|
2026-02-06 16:44:05 +08:00
|
|
|
|
],
|
|
|
|
|
|
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: "",
|
2026-02-08 13:49:12 +08:00
|
|
|
|
importPollingTimer: null,
|
|
|
|
|
|
showFailureButton: false,
|
|
|
|
|
|
currentTaskId: null,
|
|
|
|
|
|
failureDialogVisible: false,
|
|
|
|
|
|
failureList: [],
|
|
|
|
|
|
failureLoading: false,
|
|
|
|
|
|
failureTotal: 0,
|
|
|
|
|
|
failureQueryParams: {
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
pageSize: 10
|
|
|
|
|
|
}
|
2026-02-06 16:44:05 +08:00
|
|
|
|
};
|
|
|
|
|
|
},
|
2026-02-08 13:51:06 +08:00
|
|
|
|
computed: {
|
|
|
|
|
|
lastImportInfo() {
|
|
|
|
|
|
const savedTask = this.getImportTaskFromStorage();
|
|
|
|
|
|
if (savedTask && savedTask.totalCount) {
|
|
|
|
|
|
return `导入时间: ${this.parseTime(savedTask.saveTime)} | 总数: ${savedTask.totalCount}条 | 成功: ${savedTask.successCount}条 | 失败: ${savedTask.failureCount}条`;
|
|
|
|
|
|
}
|
2026-04-22 16:20:37 +08:00
|
|
|
|
return "";
|
2026-02-08 13:51:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-02-06 16:44:05 +08:00
|
|
|
|
created() {
|
|
|
|
|
|
this.getList();
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.restoreImportState();
|
2026-02-06 16:44:05 +08:00
|
|
|
|
},
|
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
|
if (this.importPollingTimer) {
|
|
|
|
|
|
clearInterval(this.importPollingTimer);
|
|
|
|
|
|
this.importPollingTimer = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
getList() {
|
|
|
|
|
|
this.loading = true;
|
2026-04-22 16:20:37 +08:00
|
|
|
|
const params = this.addDateRangeFlat(this.queryParams, this.dateRange, "applyDateStart", "applyDateEnd");
|
2026-02-06 16:44:05 +08:00
|
|
|
|
listTransaction(params).then(response => {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.transactionList = response.rows || [];
|
|
|
|
|
|
this.total = response.total || 0;
|
|
|
|
|
|
this.loading = false;
|
|
|
|
|
|
}).catch(() => {
|
2026-02-06 16:44:05 +08:00
|
|
|
|
this.loading = false;
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
2026-02-08 13:59:09 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-02-08 13:59:17 +08:00
|
|
|
|
getLastImportTooltip() {
|
|
|
|
|
|
const savedTask = this.getImportTaskFromStorage();
|
|
|
|
|
|
if (savedTask && savedTask.saveTime) {
|
|
|
|
|
|
const date = new Date(savedTask.saveTime);
|
2026-04-22 16:20:37 +08:00
|
|
|
|
return `上次导入: ${this.parseTime(date, "{y}-{m}-{d} {h}:{i}")}`;
|
2026-02-08 13:59:17 +08:00
|
|
|
|
}
|
2026-04-22 16:20:37 +08:00
|
|
|
|
return "";
|
2026-02-08 13:59:17 +08:00
|
|
|
|
},
|
2026-02-06 16:44:05 +08:00
|
|
|
|
formatAmount(amount) {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
if (amount === null || amount === undefined || amount === "") return "-";
|
|
|
|
|
|
return parseFloat(amount).toLocaleString("zh-CN", {
|
2026-02-06 16:44:05 +08:00
|
|
|
|
minimumFractionDigits: 2,
|
|
|
|
|
|
maximumFractionDigits: 2
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
2026-04-22 16:20:37 +08:00
|
|
|
|
getSupplierFieldRules(field) {
|
|
|
|
|
|
const ruleMap = {
|
|
|
|
|
|
supplierName: [
|
|
|
|
|
|
{ required: true, message: "供应商名称不能为空", trigger: "blur" },
|
|
|
|
|
|
{ max: 200, message: "供应商名称长度不能超过200个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
supplierUscc: [
|
|
|
|
|
|
{ pattern: /^$|^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/, message: "请输入正确的18位统一信用代码", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
contactPerson: [
|
|
|
|
|
|
{ max: 50, message: "联系人长度不能超过50个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
contactPhone: [
|
|
|
|
|
|
{ pattern: /^$|^1[3-9]\d{9}$|^0\d{2,3}-?\d{7,8}$/, message: "请输入正确的联系电话(手机号或座机号)", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
supplierBankAccount: [
|
|
|
|
|
|
{ max: 50, message: "银行账户长度不能超过50个字符", trigger: "blur" }
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
return ruleMap[field] || [];
|
|
|
|
|
|
},
|
2026-02-06 16:44:05 +08:00
|
|
|
|
cancel() {
|
|
|
|
|
|
this.open = false;
|
|
|
|
|
|
this.reset();
|
|
|
|
|
|
},
|
|
|
|
|
|
reset() {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.form = createDefaultForm();
|
|
|
|
|
|
this.winnerSupplierIndex = null;
|
2026-02-06 16:44:05 +08:00
|
|
|
|
this.resetForm("form");
|
|
|
|
|
|
},
|
|
|
|
|
|
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;
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.title = "添加招投标信息";
|
2026-02-06 16:44:05 +08:00
|
|
|
|
this.isAdd = true;
|
|
|
|
|
|
},
|
|
|
|
|
|
handleUpdate(row) {
|
|
|
|
|
|
this.reset();
|
|
|
|
|
|
const purchaseId = row.purchaseId || this.ids[0];
|
|
|
|
|
|
getTransaction(purchaseId).then(response => {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.applyFormData(response.data);
|
2026-02-06 16:44:05 +08:00
|
|
|
|
this.open = true;
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.title = "修改招投标信息";
|
2026-02-06 16:44:05 +08:00
|
|
|
|
this.isAdd = false;
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
|
this.$refs.form && this.$refs.form.clearValidate();
|
|
|
|
|
|
});
|
2026-02-06 16:44:05 +08:00
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
handleDetail(row) {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
getTransaction(row.purchaseId).then(response => {
|
|
|
|
|
|
this.transactionDetail = this.normalizeTransactionData(response.data);
|
2026-02-06 16:44:05 +08:00
|
|
|
|
this.detailOpen = true;
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
2026-04-22 16:20:37 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-02-06 16:44:05 +08:00
|
|
|
|
submitForm() {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$refs.form.validate(valid => {
|
|
|
|
|
|
if (!valid) {
|
|
|
|
|
|
return;
|
2026-02-06 16:44:05 +08:00
|
|
|
|
}
|
2026-04-22 16:20:37 +08:00
|
|
|
|
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();
|
|
|
|
|
|
});
|
2026-02-06 16:44:05 +08:00
|
|
|
|
});
|
|
|
|
|
|
},
|
2026-04-22 16:20:37 +08:00
|
|
|
|
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
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
2026-02-06 16:44:05 +08:00
|
|
|
|
handleDelete(row) {
|
|
|
|
|
|
const purchaseIds = row.purchaseId || this.ids;
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$modal.confirm(`是否确认删除采购事项ID为"${purchaseIds}"的数据项?`).then(function() {
|
2026-02-06 16:44:05 +08:00
|
|
|
|
return delTransaction(purchaseIds);
|
|
|
|
|
|
}).then(() => {
|
|
|
|
|
|
this.getList();
|
|
|
|
|
|
this.$modal.msgSuccess("删除成功");
|
|
|
|
|
|
}).catch(() => {});
|
|
|
|
|
|
},
|
|
|
|
|
|
handleImport() {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.upload.title = "招投标信息导入";
|
2026-02-06 16:44:05 +08:00
|
|
|
|
this.upload.open = true;
|
|
|
|
|
|
},
|
|
|
|
|
|
importTemplate() {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.download("ccdi/purchaseTransaction/importTemplate", {}, `招投标信息维护导入模板_${new Date().getTime()}.xlsx`);
|
2026-02-06 16:44:05 +08:00
|
|
|
|
},
|
2026-04-22 16:20:37 +08:00
|
|
|
|
handleFileUploadProgress() {
|
2026-02-06 16:44:05 +08:00
|
|
|
|
this.upload.isUploading = true;
|
|
|
|
|
|
},
|
2026-04-22 16:20:37 +08:00
|
|
|
|
handleFileSuccess(response) {
|
2026-02-08 14:00:10 +08:00
|
|
|
|
this.upload.isUploading = false;
|
|
|
|
|
|
this.upload.open = false;
|
|
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2026-02-08 14:00:10 +08:00
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
const taskId = response.data.taskId;
|
|
|
|
|
|
if (this.importPollingTimer) {
|
|
|
|
|
|
clearInterval(this.importPollingTimer);
|
|
|
|
|
|
this.importPollingTimer = null;
|
|
|
|
|
|
}
|
2026-02-08 14:00:10 +08:00
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.clearImportTaskFromStorage();
|
|
|
|
|
|
this.saveImportTaskToStorage({
|
|
|
|
|
|
taskId,
|
|
|
|
|
|
status: "PROCESSING",
|
|
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
hasFailures: false
|
|
|
|
|
|
});
|
|
|
|
|
|
this.showFailureButton = false;
|
|
|
|
|
|
this.currentTaskId = taskId;
|
2026-02-08 14:00:10 +08:00
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$notify({
|
|
|
|
|
|
title: "导入任务已提交",
|
|
|
|
|
|
message: "正在后台处理中,处理完成后将通知您",
|
|
|
|
|
|
type: "info",
|
|
|
|
|
|
duration: 3000
|
|
|
|
|
|
});
|
2026-02-08 14:00:10 +08:00
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.startImportStatusPolling(taskId);
|
2026-02-06 16:44:05 +08:00
|
|
|
|
},
|
2026-02-08 14:00:18 +08:00
|
|
|
|
startImportStatusPolling(taskId) {
|
|
|
|
|
|
let pollCount = 0;
|
2026-04-22 16:20:37 +08:00
|
|
|
|
const maxPolls = 150;
|
2026-02-08 14:00:18 +08:00
|
|
|
|
this.importPollingTimer = setInterval(async () => {
|
|
|
|
|
|
try {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
pollCount += 1;
|
2026-02-08 14:00:18 +08:00
|
|
|
|
if (pollCount > maxPolls) {
|
|
|
|
|
|
clearInterval(this.importPollingTimer);
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$modal.msgWarning("导入任务处理超时,请联系管理员");
|
2026-02-08 14:00:18 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const response = await getImportStatus(taskId);
|
2026-04-22 16:20:37 +08:00
|
|
|
|
if (response.data && response.data.status !== "PROCESSING") {
|
2026-02-08 14:00:18 +08:00
|
|
|
|
clearInterval(this.importPollingTimer);
|
|
|
|
|
|
this.handleImportComplete(response.data);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
clearInterval(this.importPollingTimer);
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$modal.msgError("查询导入状态失败: " + error.message);
|
2026-02-08 14:00:18 +08:00
|
|
|
|
}
|
2026-04-22 16:20:37 +08:00
|
|
|
|
}, 2000);
|
2026-02-08 14:00:18 +08:00
|
|
|
|
},
|
2026-02-08 14:00:51 +08:00
|
|
|
|
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) {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$modal.msgWarning("导入记录已过期,无法查看失败记录");
|
2026-02-08 14:00:51 +08:00
|
|
|
|
this.clearImportTaskFromStorage();
|
|
|
|
|
|
this.showFailureButton = false;
|
|
|
|
|
|
this.currentTaskId = null;
|
|
|
|
|
|
this.failureDialogVisible = false;
|
|
|
|
|
|
} else if (status === 500) {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$modal.msgError("服务器错误,请稍后重试");
|
2026-02-08 14:00:51 +08:00
|
|
|
|
} else {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$modal.msgError(`查询失败: ${error.response.data.msg || "未知错误"}`);
|
2026-02-08 14:00:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else if (error.request) {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$modal.msgError("网络连接失败,请检查网络");
|
2026-02-08 14:00:51 +08:00
|
|
|
|
} else {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$modal.msgError("查询失败记录失败: " + error.message);
|
2026-02-08 14:00:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
2026-02-08 14:00:43 +08:00
|
|
|
|
viewImportFailures() {
|
|
|
|
|
|
this.failureDialogVisible = true;
|
|
|
|
|
|
this.getFailureList();
|
|
|
|
|
|
},
|
2026-02-08 14:00:35 +08:00
|
|
|
|
handleImportComplete(statusResult) {
|
|
|
|
|
|
this.saveImportTaskToStorage({
|
|
|
|
|
|
taskId: statusResult.taskId,
|
|
|
|
|
|
status: statusResult.status,
|
|
|
|
|
|
hasFailures: statusResult.failureCount > 0,
|
|
|
|
|
|
totalCount: statusResult.totalCount,
|
|
|
|
|
|
successCount: statusResult.successCount,
|
|
|
|
|
|
failureCount: statusResult.failureCount
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-04-22 16:20:37 +08:00
|
|
|
|
if (statusResult.status === "SUCCESS") {
|
2026-02-08 14:00:35 +08:00
|
|
|
|
this.$notify({
|
2026-04-22 16:20:37 +08:00
|
|
|
|
title: "导入完成",
|
|
|
|
|
|
message: `全部成功! 共导入${statusResult.totalCount}条数据`,
|
|
|
|
|
|
type: "success",
|
2026-02-08 14:00:35 +08:00
|
|
|
|
duration: 5000
|
|
|
|
|
|
});
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.showFailureButton = false;
|
2026-02-08 14:00:35 +08:00
|
|
|
|
this.getList();
|
2026-04-22 16:20:37 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (statusResult.failureCount > 0) {
|
2026-02-08 14:00:35 +08:00
|
|
|
|
this.$notify({
|
2026-04-22 16:20:37 +08:00
|
|
|
|
title: "导入完成",
|
|
|
|
|
|
message: `成功${statusResult.successCount}条,失败${statusResult.failureCount}条`,
|
|
|
|
|
|
type: "warning",
|
2026-02-08 14:00:35 +08:00
|
|
|
|
duration: 5000
|
|
|
|
|
|
});
|
|
|
|
|
|
this.showFailureButton = true;
|
|
|
|
|
|
this.currentTaskId = statusResult.taskId;
|
|
|
|
|
|
this.getList();
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-02-06 16:44:05 +08:00
|
|
|
|
handleImportResultClose() {
|
|
|
|
|
|
this.importResultVisible = false;
|
|
|
|
|
|
this.importResultContent = "";
|
|
|
|
|
|
},
|
|
|
|
|
|
submitFileForm() {
|
|
|
|
|
|
this.$refs.upload.submit();
|
|
|
|
|
|
},
|
|
|
|
|
|
handleImportDialogClose() {
|
|
|
|
|
|
this.upload.isUploading = false;
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$refs.upload && this.$refs.upload.clearFiles();
|
2026-02-08 13:55:25 +08:00
|
|
|
|
},
|
|
|
|
|
|
saveImportTaskToStorage(taskData) {
|
|
|
|
|
|
try {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
localStorage.setItem(
|
|
|
|
|
|
"purchase_transaction_import_last_task",
|
|
|
|
|
|
JSON.stringify({ ...taskData, saveTime: Date.now() })
|
|
|
|
|
|
);
|
2026-02-08 13:55:25 +08:00
|
|
|
|
} catch (error) {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
console.error("保存导入任务状态失败:", error);
|
2026-02-08 13:55:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
getImportTaskFromStorage() {
|
|
|
|
|
|
try {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
const data = localStorage.getItem("purchase_transaction_import_last_task");
|
2026-02-08 13:55:25 +08:00
|
|
|
|
if (!data) return null;
|
|
|
|
|
|
const task = JSON.parse(data);
|
|
|
|
|
|
if (!task || !task.taskId) {
|
|
|
|
|
|
this.clearImportTaskFromStorage();
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
2026-04-22 16:20:37 +08:00
|
|
|
|
if (task.saveTime && typeof task.saveTime !== "number") {
|
2026-02-08 13:55:25 +08:00
|
|
|
|
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) {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
console.error("读取导入任务状态失败:", error);
|
2026-02-08 13:55:25 +08:00
|
|
|
|
this.clearImportTaskFromStorage();
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-02-08 14:01:19 +08:00
|
|
|
|
clearImportHistory() {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$confirm("确认清除上次导入记录?", "提示", {
|
|
|
|
|
|
confirmButtonText: "确定",
|
|
|
|
|
|
cancelButtonText: "取消",
|
|
|
|
|
|
type: "warning"
|
2026-02-08 14:01:19 +08:00
|
|
|
|
}).then(() => {
|
|
|
|
|
|
this.clearImportTaskFromStorage();
|
|
|
|
|
|
this.showFailureButton = false;
|
|
|
|
|
|
this.currentTaskId = null;
|
|
|
|
|
|
this.failureDialogVisible = false;
|
2026-04-22 16:20:37 +08:00
|
|
|
|
this.$message.success("已清除");
|
2026-02-08 14:01:19 +08:00
|
|
|
|
}).catch(() => {});
|
|
|
|
|
|
},
|
2026-02-08 13:55:25 +08:00
|
|
|
|
clearImportTaskFromStorage() {
|
|
|
|
|
|
try {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
localStorage.removeItem("purchase_transaction_import_last_task");
|
2026-02-08 13:55:25 +08:00
|
|
|
|
} catch (error) {
|
2026-04-22 16:20:37 +08:00
|
|
|
|
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;
|
2026-02-08 13:55:25 +08:00
|
|
|
|
}
|
2026-04-22 16:20:37 +08:00
|
|
|
|
},
|
|
|
|
|
|
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;
|
2026-02-06 16:44:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.detail-container {
|
|
|
|
|
|
padding: 0 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-divider {
|
|
|
|
|
|
margin: 16px 0;
|
|
|
|
|
|
}
|
2026-04-22 16:20:37 +08:00
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
2026-02-06 16:44:05 +08:00
|
|
|
|
</style>
|