1731 lines
68 KiB
Vue
1731 lines
68 KiB
Vue
<template>
|
||
<div class="modern-page-container">
|
||
<div class="page-wrapper">
|
||
<el-card class="content-card" shadow="never">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span class="card-title">
|
||
<el-icon class="title-icon"><Document /></el-icon>
|
||
新增采购申请
|
||
</span>
|
||
</div>
|
||
</template>
|
||
<div v-loading="loading" style="padding-bottom: 20px;">
|
||
<!-- 步骤条 -->
|
||
<!-- <el-steps :active="currentStep" finish-status="success" style="margin-bottom: 30px;">
|
||
<el-step title="基本信息" />
|
||
<el-step :title="stepTwoTitle" />
|
||
</el-steps> -->
|
||
|
||
<el-form
|
||
ref="formRef"
|
||
:model="dataForm"
|
||
:rules="dataRules"
|
||
label-width="140px">
|
||
|
||
<!-- 第一步:基本信息 -->
|
||
<div v-show="currentStep === 0">
|
||
<el-row :gutter="24">
|
||
<el-col :span="12" class="mb20">
|
||
<el-form-item label="采购项目名称" prop="projectName">
|
||
<el-input
|
||
v-model="dataForm.projectName"
|
||
placeholder="请输入采购项目名称"
|
||
clearable />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12" class="mb20">
|
||
<el-form-item label="填报日期" prop="applyDate">
|
||
<el-date-picker
|
||
v-model="dataForm.applyDate"
|
||
type="date"
|
||
placeholder="请选择填报日期"
|
||
format="YYYY-MM-DD"
|
||
value-format="YYYY-MM-DD"
|
||
style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="24">
|
||
<el-col :span="12" class="mb20">
|
||
<el-form-item label="资金来源" prop="fundSource">
|
||
<el-select
|
||
v-model="dataForm.fundSource"
|
||
placeholder="请选择资金来源"
|
||
clearable
|
||
style="width: 100%">
|
||
<el-option
|
||
v-for="item in fundSourceList"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12" class="mb20">
|
||
<el-form-item label="预算金额(元)" prop="budget">
|
||
<el-input-number
|
||
v-model="dataForm.budget"
|
||
:min="0.01"
|
||
:precision="2"
|
||
placeholder="请输入预算金额"
|
||
style="width: 100%" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="24">
|
||
<el-col :span="12" class="mb20">
|
||
<el-form-item label="是否集采" prop="isCentralized">
|
||
<el-select
|
||
v-model="dataForm.isCentralized"
|
||
placeholder="请选择是否集采"
|
||
clearable
|
||
style="width: 100%">
|
||
<el-option
|
||
v-for="item in isCentralizedList"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12" class="mb20">
|
||
<el-form-item label="是否特殊情况" prop="isSpecial">
|
||
<el-select
|
||
v-model="dataForm.isSpecial"
|
||
placeholder="请选择是否特殊情况"
|
||
clearable
|
||
style="width: 100%">
|
||
<el-option
|
||
v-for="item in isSpecialList"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="24">
|
||
<el-col :span="24" class="mb20">
|
||
<el-form-item label="品目编码" prop="categoryCode">
|
||
<el-cascader
|
||
v-model="categoryCodePath"
|
||
:options="categoryTreeData"
|
||
:props="{ value: 'code', label: 'name', children: 'children', checkStrictly: true }"
|
||
placeholder="请选择品目编码"
|
||
clearable
|
||
filterable
|
||
:show-all-levels="true"
|
||
style="width: 100%"
|
||
@change="handleCategoryChange" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<!-- 第二步:采购详情 -->
|
||
<div v-show="currentStep === 1">
|
||
<!-- 分支一:部门自行采购 -->
|
||
<div class="mb20" v-if="isDeptPurchase">
|
||
<div class="step-title mb20">部门自行采购</div>
|
||
<el-form-item label="采购内容" prop="projectContent" class="mb20">
|
||
<el-input
|
||
v-model="dataForm.projectContent"
|
||
type="textarea"
|
||
:rows="4"
|
||
:maxlength="1000"
|
||
show-word-limit
|
||
placeholder="请输入采购内容(限制1000字)"
|
||
clearable />
|
||
</el-form-item>
|
||
<el-form-item label="采购方式" prop="purchaseType" class="mb20">
|
||
<el-select
|
||
v-model="dataForm.purchaseType"
|
||
placeholder="请选择采购方式"
|
||
clearable
|
||
:disabled="isAutoSelectPurchaseType"
|
||
style="width: 100%">
|
||
<el-option
|
||
v-for="item in purchaseTypeDeptList"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<!-- 商务洽谈 -->
|
||
<el-form-item
|
||
v-if="isPurchaseType(PURCHASE_TYPE_IDS.BUSINESS_NEGOTIATION)"
|
||
label="商务洽谈表"
|
||
prop="businessNegotiationTable"
|
||
class="mb20">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('business_negotiation')"
|
||
class="mb10">
|
||
下载商务洽谈表模版
|
||
</el-button>
|
||
<upload-file
|
||
v-model="dataForm.businessNegotiationTable"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.businessNegotiationTable }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
|
||
<!-- 市场采购 -->
|
||
<el-form-item
|
||
v-if="isPurchaseType(PURCHASE_TYPE_IDS.MARKET_PURCHASE)"
|
||
label="市场采购纪要"
|
||
prop="marketPurchaseMinutes"
|
||
class="mb20">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('market_purchase_minutes')"
|
||
class="mb10">
|
||
下载市场采购纪要模版
|
||
</el-button>
|
||
<upload-file
|
||
v-model="dataForm.marketPurchaseMinutes"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.marketPurchaseMinutes }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
|
||
<!-- 网上商城 -->
|
||
<el-form-item
|
||
v-if="isPurchaseType(PURCHASE_TYPE_IDS.ONLINE_MALL)"
|
||
label="网上商城采购相关材料"
|
||
prop="onlineMallMaterials"
|
||
class="mb20">
|
||
<upload-file
|
||
v-model="dataForm.onlineMallMaterials"
|
||
:limit="1"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.onlineMallMaterials }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
|
||
<!-- 委托采购中心 -->
|
||
<template v-if="isPurchaseType(PURCHASE_TYPE_IDS.ENTRUST_CENTER)">
|
||
<el-form-item label="委托采购中心方式" prop="entrustCenterType" class="mb20">
|
||
<el-radio-group v-model="dataForm.entrustCenterType">
|
||
<el-radio label="service_online">服务类网上商城</el-radio>
|
||
<el-radio label="other">其他方式</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
|
||
<!-- 服务类网上商城:判断品目编码的第一个值是否为C -->
|
||
<template v-if="dataForm.entrustCenterType === 'service_online' && categoryCodePath && categoryCodePath.length > 0 && categoryCodePath[0] === 'C'">
|
||
<el-form-item label="是否有供应商" prop="hasSupplier" class="mb20">
|
||
<el-radio-group v-model="dataForm.hasSupplier">
|
||
<el-radio label="yes">有</el-radio>
|
||
<el-radio label="no">无</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<!-- 有供应商:显示供应商名称输入框 -->
|
||
<template v-if="dataForm.hasSupplier === 'yes'">
|
||
<el-form-item
|
||
label="供应商名称"
|
||
prop="suppliers"
|
||
class="mb20">
|
||
<el-input
|
||
v-model="dataForm.suppliers"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="请输入供应商名称,多个供应商请用逗号或分号分隔"
|
||
maxlength="500"
|
||
show-word-limit />
|
||
<div class="template-note mt5">
|
||
<el-text type="info" size="small">多个供应商请用逗号(,)或分号(;)分隔</el-text>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item
|
||
label="服务商城项目需求模板(直选)"
|
||
prop="serviceDirectSelect"
|
||
class="mb20">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('direct_select')"
|
||
class="mb10">
|
||
下载《服务商城项目需求模板(直选)》模版
|
||
</el-button>
|
||
<upload-file
|
||
v-model="dataForm.serviceDirectSelect"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.serviceDirectSelect }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
</template>
|
||
<el-form-item
|
||
v-if="dataForm.hasSupplier === 'no'"
|
||
label="服务商城项目需求模板(邀请比选)"
|
||
prop="serviceInviteSelect"
|
||
class="mb20">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('invite_select')"
|
||
class="mb10">
|
||
下载《服务商城项目需求模板(邀请比选)》模版
|
||
</el-button>
|
||
<upload-file
|
||
v-model="dataForm.serviceInviteSelect"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.serviceInviteSelect }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
</template>
|
||
|
||
<!-- 其他方式(货物类) -->
|
||
<el-form-item
|
||
v-if="dataForm.entrustCenterType === 'other' && categoryCodePath && categoryCodePath[0] === 'A'"
|
||
label="采购需求填报模板"
|
||
prop="purchaseRequirementTemplate"
|
||
class="mb20">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('purchase_requirement')"
|
||
class="mb10">
|
||
下载《表1:采购需求填报模板》模版
|
||
</el-button>
|
||
<upload-file
|
||
v-model="dataForm.purchaseRequirementTemplate"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.purchaseRequirementTemplate }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
</template>
|
||
|
||
<!-- 特殊规则:5万<=金额<40万,服务类目,自动使用邀请比选模版 -->
|
||
<template v-if="showAutoInviteSelect">
|
||
<el-form-item label="是否有推荐供应商" prop="hasRecommendedSupplier" class="mb20">
|
||
<el-radio-group v-model="dataForm.hasRecommendedSupplier">
|
||
<el-radio label="yes">有</el-radio>
|
||
<el-radio label="no">无</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<!-- 有推荐供应商:显示推荐供应商输入框和邀请比选模板 -->
|
||
<template v-if="dataForm.hasRecommendedSupplier === 'yes'">
|
||
<el-form-item
|
||
label="推荐供应商"
|
||
prop="recommendedSuppliers"
|
||
class="mb20">
|
||
<el-input
|
||
v-model="dataForm.recommendedSuppliers"
|
||
placeholder="请输入三家供应商名称,用逗号分隔"
|
||
clearable />
|
||
<div class="template-note mt5">
|
||
<el-text type="info" size="small">请输入三家供应商名称,用逗号分隔</el-text>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item
|
||
label="服务商城项目需求模板(邀请比选)"
|
||
prop="serviceInviteSelect"
|
||
class="mb20">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('invite_select')"
|
||
class="mb10">
|
||
下载《服务商城项目需求模板(邀请比选)》模版
|
||
</el-button>
|
||
<upload-file
|
||
v-model="dataForm.serviceInviteSelect"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.serviceInviteSelect }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
</template>
|
||
<!-- 无推荐供应商:显示公开比选模板 -->
|
||
<el-form-item
|
||
v-if="dataForm.hasRecommendedSupplier === 'no'"
|
||
label="服务商城项目需求模板(公开比选)"
|
||
prop="servicePublicSelectAuto"
|
||
class="mb20">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('public_select')"
|
||
class="mb10">
|
||
下载《服务商城项目需求模板(公开比选)》模版
|
||
</el-button>
|
||
<upload-file
|
||
v-model="dataForm.servicePublicSelectAuto"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.servicePublicSelectAuto }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
</template>
|
||
|
||
<!-- 其他材料(zip压缩包) -->
|
||
<el-form-item label="其他材料" prop="otherMaterials" class="mb20">
|
||
<upload-file
|
||
v-model="dataForm.otherMaterials"
|
||
:limit="5"
|
||
:file-type="['zip']"
|
||
:data="{ fileType: FILE_TYPE_MAP.otherMaterials }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
<div class="template-note">支持上传zip格式的压缩包文件</div>
|
||
</el-form-item>
|
||
</div>
|
||
|
||
<!-- 分支二:学校统一采购 -->
|
||
<div class="mb20" v-else >
|
||
<div class="step-title mb20">学校统一采购</div>
|
||
<el-form-item label="采购形式" prop="purchaseMode" class="mb20">
|
||
<el-select
|
||
v-model="dataForm.purchaseMode"
|
||
placeholder="请选择采购形式"
|
||
clearable
|
||
style="width: 100%">
|
||
<el-option
|
||
v-for="item in purchaseModeSchoolList"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="采购方式" prop="purchaseTypeUnion" class="mb20">
|
||
<el-select
|
||
v-model="dataForm.purchaseTypeUnion"
|
||
placeholder="请选择采购方式"
|
||
clearable
|
||
:disabled="isAutoSelectPurchaseTypeUnion"
|
||
style="width: 100%">
|
||
<el-option
|
||
v-for="item in purchaseTypeUnionList"
|
||
:key="item.value"
|
||
:label="item.label"
|
||
:value="item.value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<!-- 业务分管处室 -->
|
||
<el-form-item label="业务分管处室" prop="deptClassifyId" class="mb20">
|
||
<el-select
|
||
v-model="dataForm.deptClassifyId"
|
||
placeholder="请选择业务分管处室"
|
||
clearable
|
||
filterable
|
||
@change="handleBusinessDeptChange"
|
||
style="width: 100%">
|
||
<el-option
|
||
v-for="item in businessDeptList"
|
||
:key="item.id"
|
||
:label="item.deptName"
|
||
:value="item.id" />
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<!-- 分管校领导 -->
|
||
<el-form-item label="分管校领导" prop="schoolLeaderUserId" class="mb20">
|
||
<el-select
|
||
v-model="dataForm.schoolLeaderUserId"
|
||
placeholder="请选择分管校领导"
|
||
clearable
|
||
filterable
|
||
@change="handleSchoolLeaderChange"
|
||
style="width: 100%">
|
||
<el-option
|
||
v-for="item in schoolLeaderList"
|
||
:key="item.id"
|
||
:label="item.name"
|
||
:value="item.userId" />
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<!-- 金额>=30万,显示可行性论证报告和会议纪要 -->
|
||
<template v-if="dataForm.budget && dataForm.budget >= 300000">
|
||
<el-form-item
|
||
label="项目可行性论证报告"
|
||
prop="feasibilityReport"
|
||
class="mb20">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('feasibility_report')"
|
||
class="mb10">
|
||
下载《项目可行性论证报告模板.doc》
|
||
</el-button>
|
||
<upload-file
|
||
v-model="dataForm.feasibilityReport"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.feasibilityReport }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
<el-form-item label="会议纪要" prop="meetingMinutes" class="mb20">
|
||
<upload-file
|
||
v-model="dataForm.meetingMinutes"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.meetingMinutes }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
</template>
|
||
|
||
<!-- 特殊情况:紧急 -->
|
||
<template v-if="isUrgentSpecial">
|
||
<el-form-item label="会议纪要" prop="meetingMinutesUrgent" class="mb20">
|
||
<upload-file
|
||
v-model="dataForm.meetingMinutesUrgent"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.meetingMinutesUrgent }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
</template>
|
||
|
||
<!-- 特殊情况:单一来源 -->
|
||
<template v-if="isSpecialType('2')">
|
||
<el-form-item
|
||
label="单一来源论专家证附件"
|
||
prop="singleSourceProof"
|
||
class="mb20">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('single_source')"
|
||
class="mb10">
|
||
下载《单一来源论专家证附件.docx》
|
||
</el-button>
|
||
<upload-file
|
||
v-model="dataForm.singleSourceProof"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.singleSourceProof }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
<el-form-item label="会议纪要" prop="meetingMinutesSingle" class="mb20">
|
||
<upload-file
|
||
v-model="dataForm.meetingMinutesSingle"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.meetingMinutesSingle }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
</template>
|
||
|
||
<!-- 特殊情况:进口 -->
|
||
<template v-if="isSpecialType('3')">
|
||
<el-form-item
|
||
label="进口产品申请及专家论证意见表"
|
||
prop="importApplication"
|
||
class="mb20">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('import_application')"
|
||
class="mb10">
|
||
下载《进口产品申请及专家论证意见表.doc》
|
||
</el-button>
|
||
<upload-file
|
||
v-model="dataForm.importApplication"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.importApplication }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
<el-form-item label="会议纪要" prop="meetingMinutesImport" class="mb20">
|
||
<upload-file
|
||
v-model="dataForm.meetingMinutesImport"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.meetingMinutesImport }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
</template>
|
||
|
||
<!-- 特殊规则:5万<=金额<40万,服务类目(isMallService=1、isProjectService=1),自动使用邀请比选模版 -->
|
||
<template v-if="showAutoInviteSelectSchool">
|
||
<el-form-item label="是否有推荐供应商" prop="hasRecommendedSupplierSchool" class="mb20">
|
||
<el-radio-group v-model="dataForm.hasRecommendedSupplierSchool">
|
||
<el-radio label="yes">有</el-radio>
|
||
<el-radio label="no">无</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<!-- 有推荐供应商:显示推荐供应商输入框 -->
|
||
<el-form-item
|
||
v-if="dataForm.hasRecommendedSupplierSchool === 'yes'"
|
||
label="推荐供应商"
|
||
prop="recommendedSuppliersSchool"
|
||
class="mb20">
|
||
<el-input
|
||
v-model="dataForm.recommendedSuppliersSchool"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="请输入三家供应商名称,用逗号或分号分隔"
|
||
clearable />
|
||
<div class="template-note mt5">
|
||
<el-text type="info" size="small">请输入三家供应商名称,用逗号或分号分隔</el-text>
|
||
</div>
|
||
</el-form-item>
|
||
</template>
|
||
|
||
<!-- 需求文件 -->
|
||
<el-form-item label="需求文件" :prop="getRequirementFileProp()" class="mb20">
|
||
<!-- 特殊规则:5万<=金额<40万,服务类目(isMallService=1、isProjectService=1),自动使用邀请比选模版 -->
|
||
<template v-if="showAutoInviteSelectSchool">
|
||
<!-- 有推荐供应商:显示邀请比选模板 -->
|
||
<template v-if="dataForm.hasRecommendedSupplierSchool === 'yes'">
|
||
<div class="mb10">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('invite_select')"
|
||
class="mb10">
|
||
下载《服务商城项目需求模板(邀请比选)》模版
|
||
</el-button>
|
||
</div>
|
||
<upload-file
|
||
v-model="dataForm.serviceInviteSelectSchool"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.serviceInviteSelectSchool }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</template>
|
||
<!-- 无推荐供应商:显示公开比选模板 -->
|
||
<template v-else-if="dataForm.hasRecommendedSupplierSchool === 'no'">
|
||
<div class="mb10">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('public_select')"
|
||
class="mb10">
|
||
下载《服务商城项目需求模板(公开比选)》模版
|
||
</el-button>
|
||
</div>
|
||
<upload-file
|
||
v-model="dataForm.servicePublicSelectSchoolAuto"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.servicePublicSelectSchoolAuto }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</template>
|
||
</template>
|
||
|
||
<!-- 特殊规则:40万<=金额<100万,服务类目,自动使用公开比选需求模版 -->
|
||
<template v-else-if="showAutoPublicSelect">
|
||
<div class="mb10">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('public_select')"
|
||
class="mb10">
|
||
下载《服务商城项目需求模板(公开比选)》模版
|
||
</el-button>
|
||
</div>
|
||
<upload-file
|
||
v-model="dataForm.servicePublicSelectSchool"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.servicePublicSelectSchool }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</template>
|
||
|
||
<!-- 默认:采购需求填报模板 -->
|
||
<template v-else>
|
||
<div class="mb10">
|
||
<el-button
|
||
type="primary"
|
||
link
|
||
icon="Download"
|
||
@click="downloadTemplate('purchase_requirement')"
|
||
class="mb10">
|
||
下载《采购需求填报模板》模版
|
||
</el-button>
|
||
</div>
|
||
<upload-file
|
||
v-model="dataForm.purchaseRequirement"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.purchaseRequirement }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</template>
|
||
</el-form-item>
|
||
|
||
<!-- 金额>=100万,政府采购意向表 -->
|
||
<el-form-item
|
||
v-if="dataForm.budget && dataForm.budget >= 1000000"
|
||
label="政府采购意向申请表"
|
||
prop="governmentPurchaseIntent"
|
||
class="mb20">
|
||
<upload-file
|
||
v-model="dataForm.governmentPurchaseIntent"
|
||
:limit="5"
|
||
:file-type="['doc', 'docx', 'pdf']"
|
||
:data="{ fileType: FILE_TYPE_MAP.governmentPurchaseIntent }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
</el-form-item>
|
||
|
||
<!-- 其他材料(zip压缩包) -->
|
||
<el-form-item label="其他材料" prop="otherMaterials" class="mb20">
|
||
<upload-file
|
||
v-model="dataForm.otherMaterials"
|
||
:limit="5"
|
||
:file-type="['zip']"
|
||
:data="{ fileType: FILE_TYPE_MAP.otherMaterials }"
|
||
upload-file-url="/purchase/purchasingfiles/upload" />
|
||
<div class="template-note">支持上传zip格式的压缩包文件</div>
|
||
</el-form-item>
|
||
</div>
|
||
</div>
|
||
|
||
<el-form-item label="备注" prop="remark" v-if="currentStep === 1">
|
||
<el-input
|
||
v-model="dataForm.remark"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="请输入备注"
|
||
clearable />
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="form-footer">
|
||
<el-button @click="handleCancel">取消</el-button>
|
||
<el-button v-if="currentStep > 0" @click="prevStep">上一步</el-button>
|
||
<el-button
|
||
v-if="currentStep < 1"
|
||
type="primary"
|
||
@click="nextStep"
|
||
:disabled="loading">
|
||
下一步
|
||
</el-button>
|
||
<el-button
|
||
v-if="currentStep === 1"
|
||
type="warning"
|
||
@click="handleTempStore"
|
||
:disabled="loading">
|
||
暂存
|
||
</el-button>
|
||
<el-button
|
||
v-if="currentStep === 1"
|
||
type="primary"
|
||
@click="handleSubmit"
|
||
:disabled="loading">
|
||
提交
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts" name="PurchasingRequisitionAdd">
|
||
import { reactive, ref, onMounted, computed, watch, nextTick } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { addObj, tempStore } from '/@/api/finance/purchasingrequisition';
|
||
import { getTree } from '/@/api/finance/purchasingcategory';
|
||
import { getDicts } from '/@/api/admin/dict';
|
||
import { useMessage } from '/@/hooks/message';
|
||
import UploadFile from '/@/components/Upload/index.vue';
|
||
import other from '/@/utils/other';
|
||
import { Document, Download } from '@element-plus/icons-vue';
|
||
import { fetchList as getBusinessDeptList } from '/@/api/purchase/purchasingBusinessDept';
|
||
import { getPage as getSchoolLeaderPage } from '/@/api/finance/purchasingschoolleader';
|
||
|
||
// 路由
|
||
const router = useRouter();
|
||
|
||
// 定义变量内容
|
||
const formRef = ref();
|
||
const currentStep = ref(0);
|
||
const dataForm = reactive({
|
||
id: '',
|
||
projectName: '',
|
||
projectType: '',
|
||
projectContent: '',
|
||
applyDate: '',
|
||
fundSource: '',
|
||
budget: null as number | null,
|
||
isCentralized: '',
|
||
isSpecial: '',
|
||
purchaseMode: '',
|
||
purchaseType: '',
|
||
purchaseTypeUnion: '',
|
||
categoryCode: '',
|
||
remark: '',
|
||
status: '',
|
||
// 部门自行采购字段
|
||
businessNegotiationTable: '',
|
||
marketPurchaseMinutes: '',
|
||
onlineMallMaterials: '',
|
||
entrustCenterType: '',
|
||
hasSupplier: '',
|
||
suppliers: '', // 供应商名称(逗号或分号分隔)
|
||
serviceDirectSelect: '',
|
||
servicePublicSelect: '',
|
||
purchaseRequirementTemplate: '',
|
||
hasRecommendedSupplier: '',
|
||
recommendedSuppliers: '',
|
||
serviceInviteSelect: '',
|
||
servicePublicSelectAuto: '',
|
||
// 学校统一采购字段
|
||
purchaseRequirement: '',
|
||
meetingMinutes: '',
|
||
feasibilityReport: '',
|
||
meetingMinutesUrgent: '',
|
||
meetingMinutesSingle: '',
|
||
meetingMinutesImport: '',
|
||
singleSourceProof: '',
|
||
importApplication: '',
|
||
governmentPurchaseIntent: '',
|
||
servicePublicSelectSchool: '',
|
||
// 学校统一采购特殊规则字段(5万<=金额<40万)
|
||
hasRecommendedSupplierSchool: '',
|
||
recommendedSuppliersSchool: '',
|
||
serviceInviteSelectSchool: '',
|
||
servicePublicSelectSchoolAuto: '',
|
||
// 业务分管处室和分管校领导
|
||
deptClassifyId: '',
|
||
deptClassifyName: '',
|
||
schoolLeaderUserId: '',
|
||
schoolLeaderName: '',
|
||
// 其他材料(zip压缩包)
|
||
otherMaterials: '',
|
||
});
|
||
const categoryTreeData = ref<any[]>([]);
|
||
const categoryCodePath = ref<string[]>([]); // 级联选择器的路径数组
|
||
const fundSourceList = ref<any[]>([]);
|
||
const isCentralizedList = ref<any[]>([]);
|
||
const isSpecialList = ref<any[]>([]);
|
||
const purchaseTypeDeptList = ref<any[]>([]);
|
||
const purchaseModeSchoolList = ref<any[]>([]);
|
||
const purchaseTypeUnionList = ref<any[]>([]);
|
||
const businessDeptList = ref<any[]>([]);
|
||
const schoolLeaderList = ref<any[]>([]);
|
||
const loading = ref(false);
|
||
|
||
// 采购方式ID常量
|
||
const PURCHASE_TYPE_IDS = {
|
||
BUSINESS_NEGOTIATION: '77b429c146fc9e12ba4c5573da19ad70', // 商务洽谈
|
||
MARKET_PURCHASE: 'd522054027140e4d76e074cd96ecfc12', // 市场采购
|
||
ONLINE_MALL: 'e8723b4e3c3d51deb54f9349482ea894', // 网上商城
|
||
ENTRUST_CENTER: '981bf052a0b30b028a4a89ae490c9b1d' // 委托采购中心
|
||
};
|
||
|
||
// 文件类型映射(对应数据库 file_type 字段)
|
||
// 10:商务洽谈纪要 20:市场采购纪要 30:网上商城采购相关材料 40:可行性论证报告 50:会议记录 60:其他材料 70:单一来源专家论证表 80:进口产品申请表 90:进口产品专家论证表 100:政府采购意向表 110:履约验收单
|
||
const FILE_TYPE_MAP: Record<string, string> = {
|
||
businessNegotiationTable: '10', // 商务洽谈纪要
|
||
marketPurchaseMinutes: '20', // 市场采购纪要
|
||
onlineMallMaterials: '30', // 网上商城采购相关材料
|
||
feasibilityReport: '40', // 可行性论证报告
|
||
meetingMinutes: '50', // 会议记录
|
||
meetingMinutesUrgent: '50', // 会议记录
|
||
meetingMinutesSingle: '50', // 会议记录
|
||
meetingMinutesImport: '50', // 会议记录
|
||
otherMaterials: '60', // 其他材料
|
||
singleSourceProof: '70', // 单一来源专家论证表
|
||
importApplication: '80', // 进口产品申请表
|
||
governmentPurchaseIntent: '100', // 政府采购意向表
|
||
// 需求文件相关(暂时使用默认值,可根据实际需求调整)
|
||
serviceDirectSelect: '30', // 服务商城项目需求模板(直选)- 归类到网上商城采购相关材料
|
||
serviceInviteSelect: '30', // 服务商城项目需求模板(邀请比选)
|
||
servicePublicSelect: '30', // 服务商城项目需求模板(公开比选)
|
||
servicePublicSelectAuto: '30', // 服务商城项目需求模板(公开比选-自动)
|
||
purchaseRequirementTemplate: '30', // 采购需求填报模板
|
||
purchaseRequirement: '30', // 采购需求填报模板
|
||
serviceInviteSelectSchool: '30', // 服务商城项目需求模板(邀请比选-学校)
|
||
servicePublicSelectSchoolAuto: '30', // 服务商城项目需求模板(公开比选-学校-自动)
|
||
servicePublicSelectSchool: '30', // 服务商城项目需求模板(公开比选-学校)
|
||
};
|
||
|
||
// 辅助函数:判断当前采购方式是否为指定类型(通过 id 或 value 匹配)
|
||
const isPurchaseType = (purchaseTypeId: string) => {
|
||
if (!dataForm.purchaseType) return false;
|
||
// 在字典中查找匹配的项
|
||
const item = purchaseTypeDeptList.value.find(item =>
|
||
item.id === purchaseTypeId || item.value === purchaseTypeId
|
||
);
|
||
if (item) {
|
||
return dataForm.purchaseType === item.value;
|
||
}
|
||
// 如果字典中找不到,直接比较 value(兼容性处理)
|
||
return dataForm.purchaseType === purchaseTypeId;
|
||
};
|
||
|
||
// 辅助函数:判断特殊情况是否为指定类型(通过 id 或 value 匹配)
|
||
const isSpecialType = (specialIdOrValue: string) => {
|
||
if (!dataForm.isSpecial) return false;
|
||
// 在字典中查找匹配的项
|
||
const item = isSpecialList.value.find(item =>
|
||
item.id === specialIdOrValue || item.value === specialIdOrValue
|
||
);
|
||
if (item) {
|
||
return dataForm.isSpecial === item.value;
|
||
}
|
||
// 如果字典中找不到,直接比较 value(兼容性处理)
|
||
return dataForm.isSpecial === specialIdOrValue;
|
||
};
|
||
|
||
// 判断是否为部门自行采购
|
||
// 条件:特殊情况=否 且 集采=否 且 预算金额<5万 → 部门自行采购
|
||
// 其他情况 → 学校统一采购
|
||
const isDeptPurchase = computed(() => {
|
||
// 检查是否特殊情况是否为"否"(通过 id 或 value 查找,value 为 '0')
|
||
const isSpecialNo = isSpecialList.value.find(item =>
|
||
item.id === '1799c07f3a3b8a484f60c495ab9227b6' || item.value === '0'
|
||
);
|
||
const isSpecialNoValue = isSpecialNo ? isSpecialNo.value : null;
|
||
|
||
// 检查是否集采是否为"否"(通过 id 或 value 查找,value 为 '0')
|
||
const isCentralizedNo = isCentralizedList.value.find(item =>
|
||
item.id === '8e60f8860c1ea2459a41a8ae64fe5518' || item.value === '0'
|
||
);
|
||
const isCentralizedNoValue = isCentralizedNo ? isCentralizedNo.value : null;
|
||
|
||
// 三个条件必须同时满足:特殊情况=否 且 集采=否 且 预算金额<5万
|
||
if (isSpecialNoValue && isCentralizedNoValue &&
|
||
dataForm.isSpecial === isSpecialNoValue &&
|
||
dataForm.isCentralized === isCentralizedNoValue &&
|
||
dataForm.budget && dataForm.budget < 50000) {
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
});
|
||
|
||
// 判断是否为紧急情况(通过 id 或 value 查找,value 为 '1')
|
||
const isUrgentSpecial = computed(() => {
|
||
const urgentItem = isSpecialList.value.find(item =>
|
||
item.id === '6509b59e24c1c6568f4277e544f3e55e' ||
|
||
(item.value === '1' && (item.label?.includes('紧急') || item.dictLabel?.includes('紧急')))
|
||
);
|
||
if (!urgentItem) return false;
|
||
return dataForm.isSpecial === urgentItem.value;
|
||
});
|
||
|
||
// 第二步标题
|
||
const stepTwoTitle = computed(() => {
|
||
return isDeptPurchase.value ? '部门自行采购' : '学校统一采购';
|
||
});
|
||
|
||
// 根据 code 查找完整路径(用于回显)
|
||
const findCategoryPath = (data: any[], targetCode: string, path: string[] = []): string[] | null => {
|
||
for (const item of data) {
|
||
const currentPath = [...path, item.code];
|
||
if (item.code === targetCode) {
|
||
return currentPath;
|
||
}
|
||
if (item.children && item.children.length > 0) {
|
||
const found = findCategoryPath(item.children, targetCode, currentPath);
|
||
if (found) return found;
|
||
}
|
||
}
|
||
return null;
|
||
};
|
||
|
||
// 级联选择器变化处理
|
||
const handleCategoryChange = (value: string[]) => {
|
||
if (value && value.length > 0) {
|
||
// 取最后一个值作为选中的 code
|
||
dataForm.categoryCode = value[value.length - 1];
|
||
} else {
|
||
dataForm.categoryCode = '';
|
||
}
|
||
};
|
||
|
||
// 从品目编码中获取项目类型和属性
|
||
const getCategoryInfo = () => {
|
||
if (!dataForm.categoryCode || categoryTreeData.value.length === 0) {
|
||
return null;
|
||
}
|
||
const findCategory = (data: any[], code: string): any => {
|
||
for (const item of data) {
|
||
if (item.code === code) {
|
||
return item;
|
||
}
|
||
if (item.children && item.children.length > 0) {
|
||
const found = findCategory(item.children, code);
|
||
if (found) return found;
|
||
}
|
||
}
|
||
return null;
|
||
};
|
||
return findCategory(categoryTreeData.value, dataForm.categoryCode);
|
||
};
|
||
|
||
// 判断是否为服务类
|
||
const isServiceCategory = computed(() => {
|
||
// 通过 categoryCodePath 判断:第一个字符为 'C' 表示服务类
|
||
if (categoryCodePath.value && categoryCodePath.value.length > 0) {
|
||
return categoryCodePath.value[0] === 'C';
|
||
}
|
||
// 备用判断:通过 category 对象判断
|
||
const category = getCategoryInfo();
|
||
if (!category) return false;
|
||
return category.type === 'C' || category.projectType === 'C';
|
||
});
|
||
|
||
// 判断是否为货物类
|
||
const isGoodsCategory = computed(() => {
|
||
const category = getCategoryInfo();
|
||
if (!category) return false;
|
||
return category.type === 'A' || category.projectType === 'A';
|
||
});
|
||
|
||
// 判断是否为特殊服务类目(isMallService=1、isProjectService=1)
|
||
const isSpecialServiceCategory = computed(() => {
|
||
const category = getCategoryInfo();
|
||
if (!category) return false;
|
||
return Number(category.isMallService) === 1 || Number(category.isProjectService) === 1;
|
||
});
|
||
|
||
// 判断是否自动选择网上商城采购方式(5万<=金额<40万,服务类目,特殊服务类目)
|
||
const isAutoSelectPurchaseType = computed(() => {
|
||
if (!dataForm.budget) return false;
|
||
const budget = dataForm.budget;
|
||
return budget >= 50000 && budget < 400000 && isServiceCategory.value && isSpecialServiceCategory.value;
|
||
});
|
||
|
||
// 判断是否显示自动邀请比选模版(5万<=金额<40万,服务类目,特殊服务类目)
|
||
const showAutoInviteSelect = computed(() => {
|
||
if (!isDeptPurchase.value) return false;
|
||
if (!dataForm.budget) return false;
|
||
const budget = dataForm.budget;
|
||
return budget >= 50000 && budget < 400000 && isServiceCategory.value && isSpecialServiceCategory.value;
|
||
});
|
||
|
||
// 判断是否显示学校统一采购的自动邀请比选模版(5万<=金额<40万,服务类目,特殊服务类目)
|
||
const showAutoInviteSelectSchool = computed(() => {
|
||
if (isDeptPurchase.value) return false;
|
||
if (!dataForm.budget) return false;
|
||
const budget = dataForm.budget;
|
||
return budget >= 50000 && budget < 400000 && isSpecialServiceCategory.value;
|
||
});
|
||
|
||
// 判断是否显示自动公开比选模版(40万<=金额<100万,特殊服务类目:isMallService=1、isProjectService=1)
|
||
const showAutoPublicSelect = computed(() => {
|
||
if (isDeptPurchase.value) return false;
|
||
if (!dataForm.budget) return false;
|
||
const budget = dataForm.budget;
|
||
return budget >= 400000 && budget < 1000000 && isSpecialServiceCategory.value;
|
||
});
|
||
|
||
// 获取需求文件的 prop 名称(用于表单验证)
|
||
const getRequirementFileProp = () => {
|
||
if (showAutoInviteSelectSchool.value) {
|
||
if (dataForm.hasRecommendedSupplierSchool === 'yes') {
|
||
return 'serviceInviteSelectSchool';
|
||
} else if (dataForm.hasRecommendedSupplierSchool === 'no') {
|
||
return 'servicePublicSelectSchoolAuto';
|
||
}
|
||
} else if (showAutoPublicSelect.value) {
|
||
return 'servicePublicSelectSchool';
|
||
}
|
||
return 'purchaseRequirement';
|
||
};
|
||
|
||
// 判断学校统一采购是否需要自动设置采购方式(5万<=金额<40万,服务类目,特殊服务类目)
|
||
const isAutoSelectPurchaseTypeUnion = computed(() => {
|
||
if (isDeptPurchase.value) return false;
|
||
if (!dataForm.budget) return false;
|
||
const budget = dataForm.budget;
|
||
return budget >= 50000 && budget < 400000 && isSpecialServiceCategory.value;
|
||
});
|
||
|
||
// 监听品目编码和预算金额变化,自动设置采购方式
|
||
watch([() => dataForm.categoryCode, () => dataForm.budget], () => {
|
||
// 部门自行采购:自动设置网上商城
|
||
if (isAutoSelectPurchaseType.value && isDeptPurchase.value) {
|
||
// 查找网上商城选项(通过 id、value 或 label 匹配)
|
||
const onlineMallOption = purchaseTypeDeptList.value.find(item => {
|
||
const label = item.label || item.dictLabel || item.name || '';
|
||
return item.id === PURCHASE_TYPE_IDS.ONLINE_MALL ||
|
||
item.value === PURCHASE_TYPE_IDS.ONLINE_MALL ||
|
||
label.includes('网上商城') || label.includes('商城');
|
||
});
|
||
if (onlineMallOption && dataForm.purchaseType !== onlineMallOption.value) {
|
||
dataForm.purchaseType = onlineMallOption.value;
|
||
}
|
||
}
|
||
|
||
// 学校统一采购:自动设置网上商城采购方式
|
||
if (isAutoSelectPurchaseTypeUnion.value && !isDeptPurchase.value) {
|
||
// 查找学校统一采购方式字典中包含"网上商城"的选项
|
||
const onlineMallOption = purchaseTypeUnionList.value.find(item => {
|
||
const label = item.label || item.dictLabel || item.name || '';
|
||
return label.includes('网上商城') || label.includes('商城');
|
||
});
|
||
if (onlineMallOption && dataForm.purchaseTypeUnion !== onlineMallOption.value) {
|
||
dataForm.purchaseTypeUnion = onlineMallOption.value;
|
||
}
|
||
}
|
||
}, { immediate: true });
|
||
|
||
// 下载模版
|
||
const downloadTemplate = async (type: string) => {
|
||
const templateMap: Record<string, { fileName: string, displayName: string }> = {
|
||
'business_negotiation': { fileName: '商务洽谈表.xlsx', displayName: '商务洽谈表.xlsx' },
|
||
'market_purchase_minutes': { fileName: '市场采购纪要.xlsx', displayName: '市场采购纪要.xlsx' },
|
||
'direct_select': { fileName: '服务商城项目需求模板(直选).doc', displayName: '服务商城项目需求模板(直选).doc' },
|
||
'public_select': { fileName: '服务商城项目需求模板(公开比选).doc', displayName: '服务商城项目需求模板(公开比选).doc' },
|
||
'invite_select': { fileName: '服务商城项目需求模板(邀请比选).doc', displayName: '服务商城项目需求模板(邀请比选).doc' },
|
||
'purchase_requirement': { fileName: '表1:采购需求填报模板.doc', displayName: '采购需求填报模板.doc' },
|
||
'import_application': { fileName: '附件1:进口产品申请及专家论证意见表.doc', displayName: '进口产品申请及专家论证意见表.doc' },
|
||
'single_source': { fileName: '表7:单一来源论专家证附件.docx', displayName: '单一来源论专家证附件.docx' },
|
||
'feasibility_report': { fileName: '表6:项目可行性论证报告模板.doc', displayName: '项目可行性论证报告模板.doc' },
|
||
};
|
||
|
||
const template = templateMap[type];
|
||
if (!template) {
|
||
useMessage().error('模版不存在');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const fileUrl = new URL(`../../assets/file/${template.fileName}`, import.meta.url).href;
|
||
const response = await fetch(fileUrl);
|
||
if (!response.ok) {
|
||
throw new Error('文件下载失败');
|
||
}
|
||
const blob = await response.blob();
|
||
const url = window.URL.createObjectURL(blob);
|
||
const link = document.createElement('a');
|
||
link.href = url;
|
||
link.download = template.displayName;
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
window.URL.revokeObjectURL(url);
|
||
document.body.removeChild(link);
|
||
useMessage().success('模版下载成功');
|
||
} catch (error) {
|
||
try {
|
||
await other.downBlobFile(
|
||
`/purchase/purchasingfiles/downloadTemplate?type=${type}`,
|
||
{},
|
||
template.displayName
|
||
);
|
||
useMessage().success('模版下载成功');
|
||
} catch (err) {
|
||
useMessage().error('模版下载失败,请先维护模版文件');
|
||
}
|
||
}
|
||
};
|
||
|
||
const dataRules = reactive({
|
||
projectName: [
|
||
{ required: true, message: '采购项目名称不能为空', trigger: 'blur' }
|
||
],
|
||
applyDate: [
|
||
{ required: true, message: '填报日期不能为空', trigger: 'change' }
|
||
],
|
||
fundSource: [
|
||
{ required: true, message: '资金来源不能为空', trigger: 'change' }
|
||
],
|
||
budget: [
|
||
{ required: true, message: '预算金额不能为空', trigger: 'blur' },
|
||
{ type: 'number', min: 0.01, message: '预算金额必须大于0.01', trigger: 'blur' }
|
||
],
|
||
isCentralized: [
|
||
{ required: true, message: '请选择是否集采', trigger: 'change' }
|
||
],
|
||
isSpecial: [
|
||
{ required: true, message: '请选择是否特殊情况', trigger: 'change' }
|
||
],
|
||
categoryCode: [
|
||
{ required: true, message: '品目编码不能为空', trigger: 'change' }
|
||
],
|
||
projectContent: [
|
||
{ required: true, message: '采购内容不能为空', trigger: 'blur' },
|
||
{ max: 1000, message: '采购内容不能超过1000字', trigger: 'blur' }
|
||
],
|
||
purchaseType: [
|
||
{ required: true, message: '请选择采购方式', trigger: 'change' }
|
||
],
|
||
});
|
||
|
||
// 下一步
|
||
const nextStep = async () => {
|
||
try {
|
||
const fieldsToValidate = ['projectName', 'applyDate', 'fundSource', 'budget', 'isCentralized', 'isSpecial', 'categoryCode'];
|
||
await formRef.value?.validateField(fieldsToValidate);
|
||
|
||
// 打印品目编码的值
|
||
console.log('品目编码值 (categoryCode):', dataForm.categoryCode);
|
||
console.log('品目编码路径 (categoryCodePath):', categoryCodePath.value);
|
||
console.log('品目编码完整信息:', {
|
||
code: dataForm.categoryCode,
|
||
path: categoryCodePath.value,
|
||
categoryInfo: getCategoryInfo()
|
||
});
|
||
|
||
currentStep.value = 1;
|
||
} catch (error) {
|
||
useMessage().warning('请完善第一步信息');
|
||
}
|
||
};
|
||
|
||
// 上一步
|
||
const prevStep = () => {
|
||
currentStep.value = 0;
|
||
};
|
||
|
||
// 取消
|
||
const handleCancel = () => {
|
||
// 如果是在 iframe 中,向父窗口发送关闭消息
|
||
if (window.parent !== window) {
|
||
window.parent.postMessage({
|
||
type: 'purchasingrequisition:close'
|
||
}, '*');
|
||
} else {
|
||
router.back();
|
||
}
|
||
};
|
||
|
||
// 获取品目树形数据
|
||
const getCategoryTreeData = async () => {
|
||
try {
|
||
const res = await getTree();
|
||
categoryTreeData.value = [];
|
||
if (res.data && Array.isArray(res.data)) {
|
||
categoryTreeData.value = res.data;
|
||
}
|
||
} catch (err: any) {
|
||
console.error('获取品目树形数据失败', err);
|
||
categoryTreeData.value = [];
|
||
}
|
||
};
|
||
|
||
// 获取资金来源字典
|
||
const getFundSourceDict = async () => {
|
||
try {
|
||
const res = await getDicts('PURCHASE_FUND_SOURCE');
|
||
fundSourceList.value = [];
|
||
if (res.data && Array.isArray(res.data)) {
|
||
fundSourceList.value = res.data.map((item: any) => ({
|
||
id: item.id,
|
||
label: item.label || item.dictLabel || item.name,
|
||
value: item.value || item.dictValue || item.code
|
||
}));
|
||
} else {
|
||
fundSourceList.value = [
|
||
{ label: '切块经费', value: '0' },
|
||
{ label: '设备购置费', value: '1' },
|
||
{ label: '专项经费', value: '2' },
|
||
{ label: '代办费', value: '3' },
|
||
{ label: '培训经费', value: '4' },
|
||
{ label: '日常公用经费', value: '5' },
|
||
{ label: '技能大赛经费', value: '6' },
|
||
{ label: '基本建设资金', value: '7' },
|
||
{ label: '暂存款', value: '8' },
|
||
{ label: '会议费', value: '9' },
|
||
];
|
||
}
|
||
} catch (err) {
|
||
fundSourceList.value = [
|
||
{ label: '切块经费', value: '0' },
|
||
{ label: '设备购置费', value: '1' },
|
||
{ label: '专项经费', value: '2' },
|
||
{ label: '代办费', value: '3' },
|
||
{ label: '培训经费', value: '4' },
|
||
{ label: '日常公用经费', value: '5' },
|
||
{ label: '技能大赛经费', value: '6' },
|
||
{ label: '基本建设资金', value: '7' },
|
||
{ label: '暂存款', value: '8' },
|
||
{ label: '会议费', value: '9' },
|
||
];
|
||
}
|
||
};
|
||
|
||
// 获取是否集采字典
|
||
const getIsCentralizedDict = async () => {
|
||
try {
|
||
const res = await getDicts('PURCHASE_IS_CEN');
|
||
isCentralizedList.value = [];
|
||
if (res.data && Array.isArray(res.data)) {
|
||
isCentralizedList.value = res.data.map((item: any) => ({
|
||
id: item.id,
|
||
label: item.label || item.dictLabel || item.name,
|
||
value: item.value || item.dictValue || item.code
|
||
}));
|
||
} else {
|
||
isCentralizedList.value = [
|
||
{ id: '8e60f8860c1ea2459a41a8ae64fe5518', label: '否', value: '0' },
|
||
{ id: '', label: '政府集中采购', value: '1' },
|
||
{ id: '', label: '学校集中采购', value: '2' }
|
||
];
|
||
}
|
||
} catch (err) {
|
||
isCentralizedList.value = [
|
||
{ id: '8e60f8860c1ea2459a41a8ae64fe5518', label: '否', value: '0' },
|
||
{ id: '', label: '政府集中采购', value: '1' },
|
||
{ id: '', label: '学校集中采购', value: '2' }
|
||
];
|
||
}
|
||
};
|
||
|
||
// 获取是否特殊情况字典
|
||
const getIsSpecialDict = async () => {
|
||
try {
|
||
const res = await getDicts('PURCHASE_IS_SPEC');
|
||
isSpecialList.value = [];
|
||
if (res.data && Array.isArray(res.data)) {
|
||
isSpecialList.value = res.data.map((item: any) => ({
|
||
id: item.id,
|
||
label: item.label || item.dictLabel || item.name,
|
||
value: item.value || item.dictValue || item.code
|
||
}));
|
||
} else {
|
||
isSpecialList.value = [
|
||
{ id: '1799c07f3a3b8a484f60c495ab9227b6', label: '否', value: '0' },
|
||
{ id: '', label: '紧急', value: '1' },
|
||
{ id: '', label: '单一', value: '2' },
|
||
{ id: '', label: '进口', value: '3' }
|
||
];
|
||
}
|
||
} catch (err) {
|
||
isSpecialList.value = [
|
||
{ id: '1799c07f3a3b8a484f60c495ab9227b6', label: '否', value: '0' },
|
||
{ id: '', label: '紧急', value: '1' },
|
||
{ id: '', label: '单一', value: '2' },
|
||
{ id: '', label: '进口', value: '3' }
|
||
];
|
||
}
|
||
};
|
||
|
||
// 获取部门采购方式字典
|
||
const getPurchaseTypeDeptDict = async () => {
|
||
try {
|
||
const res = await getDicts('PURCHASE_TYPE_DEPT');
|
||
purchaseTypeDeptList.value = [];
|
||
if (res.data && Array.isArray(res.data)) {
|
||
purchaseTypeDeptList.value = res.data.map((item: any) => ({
|
||
id: item.id,
|
||
label: item.label || item.dictLabel || item.name,
|
||
value: item.value || item.dictValue || item.code
|
||
}));
|
||
}
|
||
} catch (err) {
|
||
console.error('获取部门采购方式字典失败', err);
|
||
}
|
||
};
|
||
|
||
// 获取学校采购形式字典
|
||
const getPurchaseModeSchoolDict = async () => {
|
||
try {
|
||
const res = await getDicts('PURCHASE_MODE_SCHOOL');
|
||
purchaseModeSchoolList.value = [];
|
||
if (res.data && Array.isArray(res.data)) {
|
||
purchaseModeSchoolList.value = res.data.map((item: any) => ({
|
||
id: item.id,
|
||
label: item.label || item.dictLabel || item.name,
|
||
value: item.value || item.dictValue || item.code
|
||
}));
|
||
} else {
|
||
purchaseModeSchoolList.value = [
|
||
{ label: '政府采购', value: '1' },
|
||
{ label: '学校自主采购', value: '2' }
|
||
];
|
||
}
|
||
} catch (err) {
|
||
purchaseModeSchoolList.value = [
|
||
{ label: '政府采购', value: '1' },
|
||
{ label: '学校自主采购', value: '2' }
|
||
];
|
||
}
|
||
};
|
||
|
||
// 获取学校统一采购方式字典
|
||
const getPurchaseTypeUnionDict = async () => {
|
||
try {
|
||
const res = await getDicts('PURCHASE_TYPE_UNION');
|
||
purchaseTypeUnionList.value = [];
|
||
if (res.data && Array.isArray(res.data)) {
|
||
purchaseTypeUnionList.value = res.data.map((item: any) => ({
|
||
id: item.id,
|
||
label: item.label || item.dictLabel || item.name,
|
||
value: item.value || item.dictValue || item.code
|
||
}));
|
||
}
|
||
} catch (err) {
|
||
console.error('获取学校统一采购方式字典失败', err);
|
||
}
|
||
};
|
||
|
||
// 获取业务分管处室列表
|
||
const getBusinessDeptListData = async () => {
|
||
try {
|
||
const res = await getBusinessDeptList({ records: 1000 }); // 获取所有数据
|
||
if (res.data && res.data.records) {
|
||
businessDeptList.value = res.data.records.map((item: any) => ({
|
||
id: item.id,
|
||
deptId: item.deptId,
|
||
deptName: item.deptName,
|
||
userId: item.userId,
|
||
name: item.name,
|
||
username: item.username
|
||
}));
|
||
}
|
||
} catch (err) {
|
||
console.error('获取业务分管处室列表失败:', err);
|
||
businessDeptList.value = [];
|
||
}
|
||
};
|
||
|
||
// 获取分管校领导列表
|
||
const getSchoolLeaderListData = async () => {
|
||
try {
|
||
const res = await getSchoolLeaderPage({ records: 1000 }); // 获取所有数据
|
||
if (res.data && res.data.records) {
|
||
schoolLeaderList.value = res.data.records.map((item: any) => ({
|
||
id: item.id,
|
||
userId: item.userId,
|
||
name: item.name,
|
||
username: item.username
|
||
}));
|
||
}
|
||
} catch (err) {
|
||
console.error('获取分管校领导列表失败:', err);
|
||
schoolLeaderList.value = [];
|
||
}
|
||
};
|
||
|
||
// 处理业务分管处室选择变化
|
||
const handleBusinessDeptChange = (value: string) => {
|
||
if (value) {
|
||
const selected = businessDeptList.value.find(item => item.id === value);
|
||
if (selected) {
|
||
dataForm.deptClassifyId = selected.id;
|
||
dataForm.deptClassifyName = selected.deptName || '';
|
||
}
|
||
} else {
|
||
dataForm.deptClassifyId = '';
|
||
dataForm.deptClassifyName = '';
|
||
}
|
||
};
|
||
|
||
// 处理分管校领导选择变化
|
||
const handleSchoolLeaderChange = (value: string) => {
|
||
if (value) {
|
||
const selected = schoolLeaderList.value.find(item => item.userId === value);
|
||
if (selected) {
|
||
dataForm.schoolLeaderUserId = selected.userId;
|
||
dataForm.schoolLeaderName = selected.name || '';
|
||
}
|
||
} else {
|
||
dataForm.schoolLeaderUserId = '';
|
||
dataForm.schoolLeaderName = '';
|
||
}
|
||
};
|
||
|
||
// 处理文件ID字符串转数组
|
||
// 从上传返回的URL中提取文件ID,拼成数组格式:["id1", "id2"]
|
||
const getFileIdsArray = (fileIds: string | string[]): string[] => {
|
||
if (!fileIds) return [];
|
||
if (Array.isArray(fileIds)) return fileIds;
|
||
|
||
const urls = fileIds.split(',').filter(url => url.trim());
|
||
const ids: string[] = [];
|
||
|
||
urls.forEach(url => {
|
||
try {
|
||
// 尝试解析为URL
|
||
const urlObj = new URL(url, window.location.origin);
|
||
// 优先从URL参数中获取id
|
||
let id = urlObj.searchParams.get('id');
|
||
|
||
// 如果没有id参数,尝试从路径中提取(可能是直接的文件ID)
|
||
if (!id) {
|
||
const pathParts = urlObj.pathname.split('/').filter(p => p);
|
||
// 检查最后一个路径段是否是32位十六进制字符串(文件ID格式)
|
||
const lastPart = pathParts[pathParts.length - 1];
|
||
if (lastPart && /^[a-f0-9]{32}$/i.test(lastPart)) {
|
||
id = lastPart;
|
||
} else if (lastPart) {
|
||
id = lastPart;
|
||
}
|
||
}
|
||
|
||
if (id) {
|
||
ids.push(id);
|
||
} else {
|
||
// 如果URL解析失败,检查原始字符串是否是ID格式
|
||
if (/^[a-f0-9]{32}$/i.test(url.trim())) {
|
||
ids.push(url.trim());
|
||
} else {
|
||
ids.push(url);
|
||
}
|
||
}
|
||
} catch {
|
||
// URL解析失败,检查是否是直接的ID格式(32位十六进制)
|
||
if (/^[a-f0-9]{32}$/i.test(url.trim())) {
|
||
ids.push(url.trim());
|
||
} else {
|
||
// 否则直接使用原始字符串
|
||
ids.push(url);
|
||
}
|
||
}
|
||
});
|
||
|
||
return ids;
|
||
};
|
||
|
||
// 提交
|
||
const handleSubmit = async () => {
|
||
if (loading.value) return;
|
||
loading.value = true;
|
||
|
||
try {
|
||
const valid = await formRef.value?.validate().catch(() => {});
|
||
if (!valid) {
|
||
loading.value = false;
|
||
return false;
|
||
}
|
||
|
||
const submitData: any = {
|
||
...dataForm,
|
||
};
|
||
|
||
// 处理所有文件字段
|
||
const fileFields = [
|
||
'businessNegotiationTable', 'marketPurchaseMinutes', 'onlineMallMaterials',
|
||
'serviceDirectSelect', 'servicePublicSelect', 'purchaseRequirementTemplate',
|
||
'serviceInviteSelect', 'servicePublicSelectAuto', 'purchaseRequirement',
|
||
'meetingMinutes', 'feasibilityReport', 'meetingMinutesUrgent',
|
||
'meetingMinutesSingle', 'meetingMinutesImport', 'singleSourceProof', 'importApplication',
|
||
'governmentPurchaseIntent', 'servicePublicSelectSchool', 'serviceInviteSelectSchool',
|
||
'servicePublicSelectSchoolAuto', 'otherMaterials'
|
||
];
|
||
|
||
fileFields.forEach(field => {
|
||
if (submitData[field]) {
|
||
submitData[field] = getFileIdsArray(submitData[field]);
|
||
}
|
||
});
|
||
|
||
await addObj(submitData);
|
||
useMessage().success('提交成功');
|
||
|
||
// 如果是在 iframe 中,向父窗口发送消息
|
||
if (window.parent !== window) {
|
||
window.parent.postMessage({
|
||
type: 'purchasingrequisition:submitSuccess'
|
||
}, '*');
|
||
} else {
|
||
router.push('/finance/purchasingrequisition');
|
||
}
|
||
} catch (err: any) {
|
||
useMessage().error(err.msg || '提交失败');
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 暂存
|
||
const handleTempStore = async () => {
|
||
if (loading.value) return;
|
||
loading.value = true;
|
||
|
||
try {
|
||
const valid = await formRef.value?.validate().catch(() => {});
|
||
if (!valid) {
|
||
loading.value = false;
|
||
return false;
|
||
}
|
||
|
||
const submitData: any = {
|
||
...dataForm,
|
||
};
|
||
|
||
const fileFields = [
|
||
'businessNegotiationTable', 'marketPurchaseMinutes', 'onlineMallMaterials',
|
||
'serviceDirectSelect', 'servicePublicSelect', 'purchaseRequirementTemplate',
|
||
'serviceInviteSelect', 'servicePublicSelectAuto', 'purchaseRequirement',
|
||
'meetingMinutes', 'feasibilityReport', 'meetingMinutesUrgent',
|
||
'meetingMinutesSingle', 'meetingMinutesImport', 'singleSourceProof', 'importApplication',
|
||
'governmentPurchaseIntent', 'servicePublicSelectSchool', 'serviceInviteSelectSchool',
|
||
'servicePublicSelectSchoolAuto', 'otherMaterials'
|
||
];
|
||
|
||
fileFields.forEach(field => {
|
||
if (submitData[field]) {
|
||
submitData[field] = getFileIdsArray(submitData[field]);
|
||
}
|
||
});
|
||
|
||
await tempStore(submitData);
|
||
useMessage().success('暂存成功');
|
||
|
||
// 如果是在 iframe 中,向父窗口发送消息
|
||
if (window.parent !== window) {
|
||
window.parent.postMessage({
|
||
type: 'purchasingrequisition:submitSuccess'
|
||
}, '*');
|
||
} else {
|
||
router.push('/finance/purchasingrequisition');
|
||
}
|
||
} catch (err: any) {
|
||
useMessage().error(err.msg || '暂存失败');
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 设置品目编码回显路径
|
||
const setCategoryCodePath = () => {
|
||
if (dataForm.categoryCode && categoryTreeData.value.length > 0) {
|
||
const path = findCategoryPath(categoryTreeData.value, dataForm.categoryCode);
|
||
if (path) {
|
||
categoryCodePath.value = path;
|
||
} else {
|
||
categoryCodePath.value = [];
|
||
}
|
||
} else {
|
||
categoryCodePath.value = [];
|
||
}
|
||
};
|
||
|
||
// 监听 categoryTreeData 变化,设置回显路径
|
||
watch(() => categoryTreeData.value, () => {
|
||
if (dataForm.categoryCode) {
|
||
setCategoryCodePath();
|
||
}
|
||
}, { deep: true });
|
||
|
||
// 初始化
|
||
onMounted(async () => {
|
||
// 检测是否在 iframe 中,如果是,则修改相关元素的 overflow 样式以支持滚动
|
||
if (window.parent !== window) {
|
||
nextTick(() => {
|
||
// 修改 html 和 body
|
||
document.documentElement.style.overflow = 'auto';
|
||
document.documentElement.style.height = 'auto';
|
||
document.body.style.overflow = 'auto';
|
||
document.body.style.height = 'auto';
|
||
|
||
// 修改 html 和 body,添加类名
|
||
document.documentElement.classList.add('iframe-mode');
|
||
document.body.classList.add('iframe-mode');
|
||
|
||
// 修改 #app
|
||
const app = document.getElementById('app');
|
||
if (app) {
|
||
app.style.overflow = 'auto';
|
||
app.style.height = 'auto';
|
||
app.style.minHeight = '100%';
|
||
// 添加一个类名标记,方便样式控制
|
||
app.classList.add('iframe-mode');
|
||
}
|
||
});
|
||
}
|
||
|
||
await Promise.all([
|
||
getCategoryTreeData(),
|
||
getFundSourceDict(),
|
||
getIsCentralizedDict(),
|
||
getIsSpecialDict(),
|
||
getPurchaseTypeDeptDict(),
|
||
getPurchaseModeSchoolDict(),
|
||
getPurchaseTypeUnionDict(),
|
||
getBusinessDeptListData(),
|
||
getSchoolLeaderListData(),
|
||
]);
|
||
|
||
// 新增模式下设置默认值(只有在没有 id 的情况下才设置)
|
||
if (!dataForm.id) {
|
||
// 填报日期默认为当天
|
||
const today = new Date();
|
||
const year = today.getFullYear();
|
||
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||
const day = String(today.getDate()).padStart(2, '0');
|
||
dataForm.applyDate = `${year}-${month}-${day}`;
|
||
|
||
// 是否集采默认为"否"(value: '0')
|
||
dataForm.isCentralized = '0';
|
||
|
||
// 是否特殊情况默认为"否"(value: '0')
|
||
dataForm.isSpecial = '0';
|
||
}
|
||
|
||
// 如果有 categoryCode,设置回显路径
|
||
if (dataForm.categoryCode) {
|
||
setCategoryCodePath();
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
@import '/@/assets/styles/modern-page.scss';
|
||
|
||
.mb20 {
|
||
margin-bottom: 20px;
|
||
}
|
||
.mb10 {
|
||
margin-bottom: 10px;
|
||
}
|
||
.mt5 {
|
||
margin-top: 5px;
|
||
}
|
||
.step-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: var(--el-text-color-primary);
|
||
padding-bottom: 10px;
|
||
border-bottom: 1px solid var(--el-border-color-light);
|
||
margin-bottom: 20px;
|
||
}
|
||
.template-note {
|
||
margin-top: 5px;
|
||
color: var(--el-text-color-secondary);
|
||
}
|
||
.form-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 10px;
|
||
padding-top: 20px;
|
||
border-top: 1px solid var(--el-border-color-light);
|
||
}
|
||
</style>
|
||
|
||
<style>
|
||
/* 当页面在 iframe 中时,允许滚动 */
|
||
html.iframe-mode,
|
||
body.iframe-mode {
|
||
overflow: auto !important;
|
||
height: 100% !important;
|
||
min-height: 100% !important;
|
||
}
|
||
|
||
#app.iframe-mode {
|
||
overflow: auto !important;
|
||
height: auto !important;
|
||
min-height: 100% !important;
|
||
}
|
||
|
||
/* 在 iframe 模式下,修改页面容器样式 */
|
||
.iframe-mode .modern-page-container {
|
||
min-height: auto !important;
|
||
height: auto !important;
|
||
overflow: visible !important;
|
||
}
|
||
</style>
|
||
|