Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer

This commit is contained in:
zhoutianchi
2026-02-26 18:36:35 +08:00
25 changed files with 672 additions and 327 deletions

View File

@@ -2,7 +2,7 @@
<div class="modern-page-container">
<div class="page-wrapper">
<el-card class="content-card" shadow="never">
<template #header>
<template #header v-if="isEditMode">
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
@@ -11,6 +11,11 @@
</div>
</template>
<div v-loading="loading" style="padding-bottom: 20px;">
<div class="form-toolbar mb12" style="text-align: right;">
<el-button type="primary" link icon="QuestionFilled" @click="helpDialogVisible = true">
帮助
</el-button>
</div>
<el-form
ref="formRef"
:model="dataForm"
@@ -19,112 +24,51 @@
:disabled="isViewMode || flowFormDisabled"
class="compact-form">
<!-- 基本信息与采购详情同页展示 -->
<div>
<el-row :gutter="24">
<el-col :span="12" class="mb16">
<!-- 基本信息三列紧凑 -->
<div class="form-section-compact">
<el-row :gutter="16">
<el-col :span="8" class="mb12">
<el-form-item label="采购项目名称" prop="projectName">
<el-input
v-model="dataForm.projectName"
placeholder="请输入采购项目名称"
clearable
:disabled="flowFieldDisabled('projectName')" />
<el-input v-model="dataForm.projectName" placeholder="请输入采购项目名称" clearable :disabled="flowFieldDisabled('projectName')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb16">
<el-col :span="8" class="mb12">
<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%"
:disabled="flowFieldDisabled('applyDate')" />
<el-date-picker v-model="dataForm.applyDate" type="date" placeholder="请选择填报日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 100%" :disabled="flowFieldDisabled('applyDate')" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12" class="mb16">
<el-col :span="8" class="mb12">
<el-form-item label="资金来源" prop="fundSource">
<el-select
v-model="dataForm.fundSource"
placeholder="请选择资金来源"
clearable
style="width: 100%"
:disabled="flowFieldDisabled('fundSource')">
<el-option
v-for="item in fundSourceList"
:key="item.value"
:label="item.label"
:value="item.value" />
<el-select v-model="dataForm.fundSource" placeholder="请选择资金来源" clearable style="width: 100%" :disabled="flowFieldDisabled('fundSource')">
<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="mb16">
<el-col :span="8" class="mb12">
<el-form-item label="预算金额" prop="budget">
<div class="budget-yuan-wrap">
<el-input-number
v-model="dataForm.budget"
:min="0.01"
:precision="2"
placeholder="请输入金额"
:controls="false"
style="width: 100%"
:disabled="flowFieldDisabled('budget')" />
<el-input-number v-model="dataForm.budget" :min="0.01" :precision="2" placeholder="请输入金额" :controls="false" style="width: 100%" :disabled="flowFieldDisabled('budget')" />
<span class="budget-unit"></span>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12" class="mb16">
<el-col :span="8" class="mb12">
<el-form-item label="是否集采" prop="isCentralized">
<el-select
v-model="dataForm.isCentralized"
placeholder="请选择是否集采"
clearable
style="width: 100%"
:disabled="flowFieldDisabled('isCentralized')">
<el-option
v-for="item in isCentralizedList"
:key="item.value"
:label="item.label"
:value="item.value" />
<el-select v-model="dataForm.isCentralized" placeholder="请选择是否集采" clearable style="width: 100%" :disabled="flowFieldDisabled('isCentralized')">
<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="mb16">
<el-col :span="8" class="mb12">
<el-form-item label="是否特殊情况" prop="isSpecial">
<el-select
v-model="dataForm.isSpecial"
placeholder="请选择是否特殊情况"
clearable
style="width: 100%"
:disabled="flowFieldDisabled('isSpecial')">
<el-option
v-for="item in isSpecialList"
:key="item.value"
:label="item.label"
:value="item.value" />
<el-select v-model="dataForm.isSpecial" placeholder="请选择是否特殊情况" clearable style="width: 100%" :disabled="flowFieldDisabled('isSpecial')">
<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="mb16">
<el-col :span="24" class="mb12">
<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%"
:disabled="flowFieldDisabled('categoryCode')"
@change="handleCategoryChange" />
<el-cascader v-model="categoryCodePath" :options="categoryTreeData" :props="{ value: 'code', label: 'name', children: 'children', checkStrictly: false }" placeholder="请选择品目编码(仅最后一级)" clearable filterable :show-all-levels="true" style="width: 100%" :disabled="flowFieldDisabled('categoryCode')" @change="handleCategoryChange" />
</el-form-item>
</el-col>
</el-row>
@@ -135,24 +79,19 @@
<el-alert v-if="!showPurchaseDetailBlocks" type="info" :closable="false" class="mb16" show-icon>
请先填写上方是否特殊情况是否集采预算金额系统将根据填写结果展示部门自行采购学校统一采购表单
</el-alert>
<!-- 分支一部门自行采购单行两列流式布局条件显示的项自动填满两列 -->
<div class="mb20" v-if="showPurchaseDetailBlocks && isDeptPurchase">
<div class="step-title mb16">部门自行采购</div>
<el-row :gutter="24">
<el-col :span="12" class="mb16">
<el-form-item label="采购内容" prop="projectContent" class="mb16">
<el-input
v-model="dataForm.projectContent"
type="textarea"
:rows="3"
:maxlength="1000"
show-word-limit
placeholder="请输入采购内容限制1000字"
clearable
:disabled="flowFieldDisabled('projectContent')" />
<!-- 分支一部门自行采购三列紧凑textarea 单独一行 -->
<div class="mb20 form-section-compact" v-if="showPurchaseDetailBlocks && isDeptPurchase">
<div class="step-title mb12">部门自行采购</div>
<el-row :gutter="16">
<el-col :span="8" class="mb12">
<el-form-item label="采购途径" prop="purchaseChannel">
<el-select v-model="dataForm.purchaseChannel" placeholder="请选择采购途径" clearable :disabled="flowFieldDisabled('purchaseChannel')" style="width: 100%">
<el-option :label="'自行采购'" :value="PURCHASE_CHANNEL.SELF" />
<el-option :label="'委托采购中心采购'" :value="PURCHASE_CHANNEL.ENTRUST_CENTER" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16">
<el-col :span="8" class="mb12" v-if="!isEntrustCenterChannel || (isFlowEmbed && isPurchaseCenter)">
<el-form-item label="采购方式" prop="purchaseType" required class="mb16">
<el-select
v-model="dataForm.purchaseType"
@@ -166,182 +105,69 @@
:label="item.label"
:value="item.value" />
</el-select>
<div
v-if="isFlowEmbed && isPurchaseCenter && isEntrustCenterChannel && isAutoSelectPurchaseType"
class="template-note mt5"
>
<el-text type="info" size="small">服务网上商城</el-text>
</div>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.BUSINESS_NEGOTIATION)">
<el-form-item label="商务洽谈表" prop="businessNegotiationTable" required class="mb16">
<el-col :span="24" class="mb12">
<el-form-item label="采购内容" prop="projectContent">
<el-input v-model="dataForm.projectContent" type="textarea" :rows="3" :maxlength="1000" show-word-limit placeholder="请输入采购内容限制1000字" clearable :disabled="flowFieldDisabled('projectContent')" />
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.BUSINESS_NEGOTIATION)">
<el-form-item label="商务洽谈表" prop="businessNegotiationTable" required>
<upload-file v-model="dataForm.businessNegotiationTable" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.businessNegotiationTable }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('businessNegotiationTable')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('business_negotiation')" style="margin-top: 8px; display: inline-block">下载商务洽谈表模版</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.MARKET_PURCHASE)">
<el-form-item label="市场采购纪要" prop="marketPurchaseMinutes" required class="mb16">
<el-col :span="8" class="mb12" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.MARKET_PURCHASE)">
<el-form-item label="市场采购纪要" prop="marketPurchaseMinutes" required>
<upload-file v-model="dataForm.marketPurchaseMinutes" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.marketPurchaseMinutes }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('marketPurchaseMinutes')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('market_purchase_minutes')" style="margin-top: 8px; display: inline-block">下载市场采购纪要模版</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.ONLINE_MALL)">
<el-form-item label="网上商城采购相关材料" prop="onlineMallMaterials" required class="mb16">
<el-col :span="8" class="mb12" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.ONLINE_MALL)">
<el-form-item label="网上商城采购相关材料" prop="onlineMallMaterials" required>
<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" :disabled="flowFieldDisabled('onlineMallMaterials')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isInquiryPurchaseType">
<el-form-item label="询价模板" prop="inquiryTemplate" required class="mb16">
<el-col :span="8" class="mb12" v-if="isInquiryPurchaseType">
<el-form-item label="询价模板" prop="inquiryTemplate" required>
<upload-file v-model="dataForm.inquiryTemplate" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.inquiryTemplate }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('inquiryTemplate')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('inquiry')" style="margin-top: 8px; display: inline-block">下载部门采购询价模版模版</el-button>
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('inquiry')" style="margin-top: 8px; display: inline-block">下载部门采购询价模版模版</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.ENTRUST_CENTER)">
<el-form-item label="委托采购中心方式" prop="entrustCenterType" class="mb16">
<el-radio-group v-model="dataForm.entrustCenterType" disabled>
<el-radio label="service_online">服务类网上商城</el-radio>
<el-radio label="other">其他方式</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col
:span="12"
class="mb16"
v-if="
isPurchaseType(DEPT_PURCHASE_TYPE.ENTRUST_CENTER) &&
dataForm.entrustCenterType === 'service_online' &&
categoryCodePath &&
categoryCodePath[0] === 'C'
"
>
<el-form-item label="是否有供应商" prop="hasSupplier" class="mb16">
<el-col :span="8" class="mb12" v-if="isEntrustCenterChannel && dataForm.entrustCenterType === 'service_online' && categoryCodePath && categoryCodePath[0] === 'C'">
<el-form-item label="是否有供应商" prop="hasSupplier">
<el-radio-group v-model="dataForm.hasSupplier" :disabled="flowFieldDisabled('hasSupplier')">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.ENTRUST_CENTER) && dataForm.entrustCenterType === 'service_online' && categoryCodePath && categoryCodePath[0] === 'C' && dataForm.hasSupplier === '1'">
<el-form-item label="需求文件" prop="serviceDirectSelect" required class="mb16">
<el-col :span="8" class="mb12" v-if="isEntrustCenterChannel && dataForm.entrustCenterType === 'service_online' && categoryCodePath && categoryCodePath[0] === 'C' && dataForm.hasSupplier === '1'">
<el-form-item label="需求文件" prop="serviceDirectSelect" required>
<upload-file v-model="dataForm.serviceDirectSelect" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.serviceDirectSelect }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('serviceDirectSelect')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('direct_select')" style="margin-top: 8px; display: inline-block">下载服务商城项目需求模板直选模版</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.ENTRUST_CENTER) && dataForm.entrustCenterType === 'service_online' && categoryCodePath && categoryCodePath[0] === 'C' && dataForm.hasSupplier === '0'">
<el-form-item label="需求文件" prop="serviceInviteSelect" required class="mb16">
<el-col :span="8" class="mb12" v-if="isEntrustCenterChannel && dataForm.entrustCenterType === 'service_online' && categoryCodePath && categoryCodePath[0] === 'C' && dataForm.hasSupplier === '0'">
<el-form-item label="需求文件" prop="serviceInviteSelect" required>
<upload-file v-model="dataForm.serviceInviteSelect" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.serviceInviteSelect }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('serviceInviteSelect')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('public_select')" style="margin-top: 8px; display: inline-block">下载服务商城项目需求模板公开比选模版</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.ENTRUST_CENTER) && dataForm.entrustCenterType === 'other' && categoryCodePath && categoryCodePath[0] === 'A'">
<el-form-item label="需求模板" prop="purchaseRequirementTemplate" required class="mb16">
<el-col :span="8" class="mb12" v-if="isEntrustCenterChannel && dataForm.entrustCenterType === 'other' && categoryCodePath && categoryCodePath[0] === 'A'">
<el-form-item label="需求模板" prop="purchaseRequirementTemplate" required>
<upload-file v-model="dataForm.purchaseRequirementTemplate" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.purchaseRequirementTemplate }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('purchaseRequirementTemplate')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('purchase_requirement')" style="margin-top: 8px; display: inline-block">下载表1需求模板模版</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="showAutoInviteSelect">
<el-form-item label="是否有推荐供应商" prop="hasSupplier" class="mb16">
<el-radio-group v-model="dataForm.hasSupplier" :disabled="flowFieldDisabled('hasSupplier')">
<el-radio label="1">有</el-radio>
<el-radio label="0">无</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<!-- <el-col :span="12" class="mb16" v-if="showAutoInviteSelect && dataForm.hasSupplier === '1'">
<el-form-item label="推荐供应商" prop="suppliers" class="mb16">
<el-input v-model="dataForm.suppliers" placeholder="请输入三家供应商名称,用逗号分隔" clearable />
<div class="template-note mt5"><el-text type="info" size="small">请输入三家供应商名称,用逗号分隔</el-text></div>
</el-form-item>
</el-col> -->
<el-col :span="12" class="mb16" v-if="showAutoInviteSelect && dataForm.hasSupplier === '1'">
<el-form-item label="服务商城项目需求模板(邀请比选)" prop="serviceInviteSelect" required class="mb16">
<upload-file v-model="dataForm.serviceInviteSelect" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.serviceInviteSelect }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('serviceInviteSelect')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('invite_select')" style="margin-top: 8px; display: inline-block">下载《服务商城项目需求模板(邀请比选)》模版</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="showAutoInviteSelect && dataForm.hasSupplier === '0'">
<el-form-item label="服务商城项目需求模板(公开比选)" prop="servicePublicSelectAuto" required class="mb16">
<upload-file v-model="dataForm.servicePublicSelectAuto" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.servicePublicSelectAuto }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('servicePublicSelectAuto')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('public_select')" style="margin-top: 8px; display: inline-block">下载《服务商城项目需求模板(公开比选)》模版</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16">
<el-form-item label="其他材料" prop="otherMaterials" class="mb16">
<upload-file v-model="dataForm.otherMaterials" :limit="1" :file-type="['zip']" :data="{ fileType: FILE_TYPE_MAP.otherMaterials }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('otherMaterials')" />
<div class="template-note">支持上传zip格式的压缩包文件</div>
</el-form-item>
</el-col>
</el-row>
</div>
<!-- 分支二:学校统一采购(单行两列流式布局);申请时隐藏采购方式,由审批环节采购中心补充 -->
<div class="mb20" v-if="showPurchaseDetailBlocks && !isDeptPurchase">
<div class="step-title mb16">学校统一采购</div>
<el-row :gutter="24">
<el-col :span="12" class="mb16">
<el-form-item label="采购形式" prop="purchaseMode">
<el-radio-group v-model="dataForm.purchaseMode" :disabled="schoolUnifiedPurchaseFormDisabled">
<el-radio v-for="item in purchaseModeSchoolList" :key="item.value" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
<!-- <div v-if="schoolUnifiedPurchaseFormDefault != null" class="template-note mt5"><el-text type="info" size="small">根据预算金额与是否集采由系统自动选择</el-text></div> -->
</el-form-item>
</el-col>
<!-- 学校统一采购时申请阶段不显示采购方式;审批环节(流程嵌入)时显示,由采购中心补充选择、其他节点只读 -->
<el-col :span="12" class="mb16" v-if="isDeptPurchase || isFlowEmbed">
<el-form-item label="采购方式" prop="purchaseType" :required="!isDeptPurchase">
<el-select v-model="dataForm.purchaseType" placeholder="请选择采购方式" clearable :disabled="(isFlowEmbed && isPurchaseCenter) ? false : (isAutoSelectPurchaseTypeUnion || flowFieldDisabled('purchaseType'))" 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-col>
<el-col :span="12" class="mb16">
<el-form-item label="业务分管处室" prop="deptClassifyUserId" :required="!isDeptPurchase">
<el-select v-model="dataForm.deptClassifyUserId" placeholder="请选择业务分管处室" clearable filterable @change="handleBusinessDeptChange" style="width: 100%" :disabled="flowFieldDisabled('deptClassifyUserId')">
<el-option v-for="item in businessDeptList" :key="item.id" :label="item.deptName" :value="item.userId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16">
<el-form-item label="分管校领导" prop="schoolLeaderUserId" :required="!isDeptPurchase">
<el-select v-model="dataForm.schoolLeaderUserId" placeholder="请选择分管校领导" clearable filterable @change="handleSchoolLeaderChange" style="width: 100%" :disabled="flowFieldDisabled('schoolLeaderUserId')">
<el-option v-for="item in schoolLeaderList" :key="item.id" :label="item.name" :value="item.userId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="dataForm.budget && dataForm.budget >= 300000">
<el-form-item label="项目可行性论证报告" prop="feasibilityReport" required>
<upload-file v-model="dataForm.feasibilityReport" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.feasibilityReport }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('feasibilityReport')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('feasibility_report')" style="margin-top: 8px; display: inline-block">下载《项目可行性论证报告模板.doc》</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="dataForm.budget && dataForm.budget >= 300000 && !isUrgentSpecial && !isSpecialType('2') && !isSpecialType('3')">
<el-form-item label="会议纪要" prop="meetingMinutes" required>
<upload-file v-model="dataForm.meetingMinutes" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.meetingMinutes }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('meetingMinutes')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isUrgentSpecial">
<el-form-item label="会议纪要" prop="meetingMinutesUrgent" required>
<upload-file v-model="dataForm.meetingMinutesUrgent" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.meetingMinutesUrgent }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('meetingMinutesUrgent')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isSpecialType('2')">
<el-form-item label="单一来源论专家证附件" prop="singleSourceProof" required>
<upload-file v-model="dataForm.singleSourceProof" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.singleSourceProof }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('singleSourceProof')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('single_source')" style="margin-top: 8px; display: inline-block">下载《单一来源论专家证附件.docx》</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isSpecialType('2')">
<el-form-item label="会议纪要" prop="meetingMinutesSingle" required>
<upload-file v-model="dataForm.meetingMinutesSingle" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.meetingMinutesSingle }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('meetingMinutesSingle')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isSpecialType('3')">
<el-form-item label="进口产品申请及专家论证意见表" prop="importApplication" required>
<upload-file v-model="dataForm.importApplication" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.importApplication }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('importApplication')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('import_application')" style="margin-top: 8px; display: inline-block">下载《进口产品申请及专家论证意见表.doc》</el-button>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="isSpecialType('3')">
<el-form-item label="会议纪要" prop="meetingMinutesImport" required>
<upload-file v-model="dataForm.meetingMinutesImport" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.meetingMinutesImport }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('meetingMinutesImport')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="showAutoInviteSelectSchool">
<el-col :span="8" class="mb12" v-if="showAutoInviteSelect">
<el-form-item label="是否有推荐供应商" prop="hasSupplier">
<el-radio-group v-model="dataForm.hasSupplier" :disabled="flowFieldDisabled('hasSupplier')">
<el-radio label="1"></el-radio>
@@ -349,13 +175,119 @@
</el-radio-group>
</el-form-item>
</el-col>
<!-- <el-col :span="12" class="mb16" v-if="showAutoInviteSelectSchool && dataForm.hasSupplier === '1'">
<!-- <el-col :span="8" class="mb12" v-if="showAutoInviteSelect && dataForm.hasSupplier === '1'">
<el-form-item label="推荐供应商" prop="suppliers" class="mb16">
<el-input v-model="dataForm.suppliers" placeholder="请输入三家供应商名称,用逗号分隔" clearable />
<div class="template-note mt5"><el-text type="info" size="small">请输入三家供应商名称用逗号分隔</el-text></div>
</el-form-item>
</el-col> -->
<el-col :span="8" class="mb12" v-if="showAutoInviteSelect && dataForm.hasSupplier === '1'">
<el-form-item label="服务商城项目需求模板(邀请比选)" prop="serviceInviteSelect" required>
<upload-file v-model="dataForm.serviceInviteSelect" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.serviceInviteSelect }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('serviceInviteSelect')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('invite_select')" style="margin-top: 8px; display: inline-block">下载服务商城项目需求模板邀请比选模版</el-button>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="showAutoInviteSelect && dataForm.hasSupplier === '0'">
<el-form-item label="服务商城项目需求模板(公开比选)" prop="servicePublicSelectAuto" required>
<upload-file v-model="dataForm.servicePublicSelectAuto" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.servicePublicSelectAuto }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('servicePublicSelectAuto')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('public_select')" style="margin-top: 8px; display: inline-block">下载服务商城项目需求模板公开比选模版</el-button>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12">
<el-form-item label="其他材料" prop="otherMaterials">
<upload-file v-model="dataForm.otherMaterials" :limit="1" :file-type="['zip']" :data="{ fileType: FILE_TYPE_MAP.otherMaterials }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('otherMaterials')" />
<div class="template-note">支持上传zip格式的压缩包文件</div>
</el-form-item>
</el-col>
</el-row>
</div>
<!-- 分支二学校统一采购三列紧凑 -->
<div class="mb20 form-section-compact" v-if="showPurchaseDetailBlocks && !isDeptPurchase">
<div class="step-title mb12">学校统一采购</div>
<el-row :gutter="16">
<el-col :span="8" class="mb12">
<el-form-item label="采购形式" prop="purchaseMode">
<el-radio-group v-model="dataForm.purchaseMode" :disabled="schoolUnifiedPurchaseFormDisabled">
<el-radio v-for="item in purchaseModeSchoolList" :key="item.value" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
<!-- <div v-if="schoolUnifiedPurchaseFormDefault != null" class="template-note mt5"><el-text type="info" size="small">根据预算金额与是否集采由系统自动选择</el-text></div> -->
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="isDeptPurchase || isFlowEmbed">
<el-form-item label="采购方式" prop="purchaseType" :required="!isDeptPurchase">
<el-select v-model="dataForm.purchaseType" placeholder="请选择采购方式" clearable :disabled="(isFlowEmbed && isPurchaseCenter) ? false : (isAutoSelectPurchaseTypeUnion || flowFieldDisabled('purchaseType'))" 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-col>
<el-col :span="8" class="mb12">
<el-form-item label="业务分管处室" prop="deptClassifyUserId" :required="!isDeptPurchase">
<el-select v-model="dataForm.deptClassifyUserId" placeholder="请选择业务分管处室" clearable filterable @change="handleBusinessDeptChange" style="width: 100%" :disabled="flowFieldDisabled('deptClassifyUserId')">
<el-option v-for="item in businessDeptList" :key="item.id" :label="item.deptName" :value="item.userId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12">
<el-form-item label="分管校领导" prop="schoolLeaderUserId" :required="!isDeptPurchase">
<el-select v-model="dataForm.schoolLeaderUserId" placeholder="请选择分管校领导" clearable filterable @change="handleSchoolLeaderChange" style="width: 100%" :disabled="flowFieldDisabled('schoolLeaderUserId')">
<el-option v-for="item in schoolLeaderList" :key="item.id" :label="item.name" :value="item.userId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="dataForm.budget && dataForm.budget >= BUDGET_FEASIBILITY_THRESHOLD">
<el-form-item label="项目可行性论证报告" prop="feasibilityReport" required>
<upload-file v-model="dataForm.feasibilityReport" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.feasibilityReport }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('feasibilityReport')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('feasibility_report')" style="margin-top: 8px; display: inline-block">下载项目可行性论证报告模板.doc</el-button>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="dataForm.budget && dataForm.budget >= BUDGET_FEASIBILITY_THRESHOLD && !isUrgentSpecial && !isSpecialType('2') && !isSpecialType('3')">
<el-form-item label="会议纪要" prop="meetingMinutes" required>
<upload-file v-model="dataForm.meetingMinutes" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.meetingMinutes }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('meetingMinutes')" />
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="isUrgentSpecial">
<el-form-item label="会议纪要" prop="meetingMinutesUrgent" required>
<upload-file v-model="dataForm.meetingMinutesUrgent" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.meetingMinutesUrgent }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('meetingMinutesUrgent')" />
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="isSpecialType('2')">
<el-form-item label="单一来源论专家证附件" prop="singleSourceProof" required>
<upload-file v-model="dataForm.singleSourceProof" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.singleSourceProof }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('singleSourceProof')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('single_source')" style="margin-top: 8px; display: inline-block">下载单一来源论专家证附件.docx</el-button>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="isSpecialType('2')">
<el-form-item label="会议纪要" prop="meetingMinutesSingle" required>
<upload-file v-model="dataForm.meetingMinutesSingle" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.meetingMinutesSingle }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('meetingMinutesSingle')" />
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="isSpecialType('3')">
<el-form-item label="进口产品申请及专家论证意见表" prop="importApplication" required>
<upload-file v-model="dataForm.importApplication" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.importApplication }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('importApplication')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('import_application')" style="margin-top: 8px; display: inline-block">下载进口产品申请及专家论证意见表.doc</el-button>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="isSpecialType('3')">
<el-form-item label="会议纪要" prop="meetingMinutesImport" required>
<upload-file v-model="dataForm.meetingMinutesImport" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.meetingMinutesImport }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('meetingMinutesImport')" />
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="showAutoInviteSelectSchool">
<el-form-item label="是否有推荐供应商" prop="hasSupplier">
<el-radio-group v-model="dataForm.hasSupplier" :disabled="flowFieldDisabled('hasSupplier')">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<!-- <el-col :span="8" class="mb12" v-if="showAutoInviteSelectSchool && dataForm.hasSupplier === '1'">
<el-form-item label="推荐供应商" prop="suppliers">
<el-input v-model="dataForm.suppliers" type="textarea" :rows="2" placeholder="请输入至少三家供应商名称,用逗号或分号分隔" clearable :disabled="flowFieldDisabled('suppliers')" />
<div class="template-note mt5"><el-text type="info" size="small">请输入至少三家供应商名称用逗号或分号分隔</el-text></div>
</el-form-item>
</el-col> -->
<el-col :span="12" class="mb16">
<el-col :span="8" class="mb12">
<el-form-item label="需求文件" :prop="getRequirementFileProp()" required class="mb16">
<template v-if="showAutoInviteSelectSchool">
<template v-if="dataForm.hasSupplier === '1'">
@@ -377,12 +309,12 @@
</template>
</el-form-item>
</el-col>
<el-col :span="12" class="mb16" v-if="dataForm.budget && dataForm.budget >= 1000000">
<el-col :span="8" class="mb12" v-if="dataForm.budget && dataForm.budget >= BUDGET_GOV_PURCHASE_THRESHOLD">
<el-form-item label="政府采购意向申请表" prop="governmentPurchaseIntent" required>
<upload-file v-model="dataForm.governmentPurchaseIntent" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.governmentPurchaseIntent }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('governmentPurchaseIntent')" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb16">
<el-col :span="8" class="mb12">
<el-form-item label="其他材料" prop="otherMaterials">
<upload-file v-model="dataForm.otherMaterials" :limit="1" :file-type="['zip']" :data="{ fileType: FILE_TYPE_MAP.otherMaterials }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('otherMaterials')" />
<div class="template-note">支持上传zip格式的压缩包文件</div>
@@ -392,16 +324,10 @@
</div>
</div>
<el-row :gutter="24">
<el-col :span="12" class="mb16">
<el-form-item label="备注" prop="remark" class="mb16">
<el-input
v-model="dataForm.remark"
type="textarea"
:rows="2"
placeholder="请输入备注"
clearable
:disabled="flowFieldDisabled('remark')" />
<el-row :gutter="16">
<el-col :span="24" class="mb12">
<el-form-item label="备注" prop="remark">
<el-input v-model="dataForm.remark" type="textarea" :rows="2" placeholder="请输入备注" clearable :disabled="flowFieldDisabled('remark')" />
</el-form-item>
</el-col>
</el-row>
@@ -410,22 +336,81 @@
<div v-if="isViewMode && dataForm.agentName" class="implement-info-block mb20">
<el-divider content-position="left">招标代理</el-divider>
<el-row :gutter="24">
<el-col :span="12" class="mb16">
<el-col :span="8" class="mb12">
<div class="view-label">代理名称</div>
<div class="view-value">{{ dataForm.agentName }}</div>
</el-col>
</el-row>
</div>
<!-- 帮助弹窗展示金额与规则说明 -->
<el-dialog
v-model="helpDialogVisible"
title="帮助 - 采购规则说明"
width="620px"
destroy-on-close
>
<el-alert
type="info"
:closable="false"
show-icon
class="mb12"
>
以下规则仅作为自动推荐与必填校验依据实际录入时可在允许范围内调整
</el-alert>
<el-scrollbar max-height="360px">
<ul class="help-rules-list">
<li>
<strong>部门自行采购 / 学校统一采购划分</strong>
是否特殊情况 = 是否集采 = 且预算金额 &lt; 5 {{ BUDGET_DEPT_PURCHASE_THRESHOLD }} 系统判定为部门自行采购否则为学校统一采购
</li>
<li>
<strong>学校统一采购 - 采购形式默认值</strong>
当预算金额 100 {{ BUDGET_GOV_PURCHASE_THRESHOLD }} 时默认政府采购
5 预算金额 &lt; 100 万时根据是否集采自动推荐政府采购学校自主采购
但申请人可以在界面上自行修改
</li>
<li>
<strong>部门自行采购 - 自动网上商城</strong>
部门自行采购且为服务类特殊品目满足服务类且为商城/项目服务
5 预算金额 &lt; 40 {{ BUDGET_PUBLIC_SELECT_THRESHOLD }} 系统会自动将采购方式推荐为网上商城同时要求上传相应模板文件
</li>
<li>
<strong>学校统一采购 - 自动邀请/公开比选模板</strong>
对服务类特殊品目
5 预算金额 &lt; 40 万时系统根据是否有推荐供应商在邀请比选 / 公开比选模板之间自动切换必填文件
40 预算金额 &lt; 100 万时系统默认要求使用公开比选模板
</li>
<li>
<strong>可行性论证与会议纪要学校统一采购</strong>
当预算金额 30 {{ BUDGET_FEASIBILITY_THRESHOLD }} 且不是紧急/单一/进口等特殊情况时
需要上传项目可行性论证报告会议纪要紧急单一来源进口等特殊情况对应有单独的证明材料与会议纪要要求
</li>
<li>
<strong>政府采购意向表</strong>
当预算金额 100 {{ BUDGET_GOV_PURCHASE_THRESHOLD }} 且为学校统一采购时需要填写政府采购意向表并上传对应模板文件
</li>
<li>
<strong>委托采购中心采购</strong>
部门自行采购中选择委托采购中心采购作为采购途径时申请阶段隐藏采购方式由采购中心在审核环节选择
对服务类特殊品目且金额在 5 ~ 40 万区间时采购中心会优先推荐网上商城服务网上商城方式
</li>
</ul>
</el-scrollbar>
<template #footer>
<el-button type="primary" @click="helpDialogVisible = false">我知道了</el-button>
</template>
</el-dialog>
<!-- 查看时实施采购信息 -->
<div v-if="isViewMode && (dataForm.implementType || viewImplementPurchaseFiles.length)" class="implement-info-block mb20">
<el-divider content-position="left">实施采购信息</el-divider>
<el-row :gutter="24">
<el-col :span="12" class="mb16" v-if="dataForm.implementType">
<el-col :span="8" class="mb12" v-if="dataForm.implementType">
<div class="view-label">实施采购方式</div>
<div class="view-value">{{ dataForm.implementType === '1' ? '自行组织采购' : dataForm.implementType === '2' ? '委托代理采购' : dataForm.implementType || '—' }}</div>
</el-col>
<el-col :span="12" class="mb16" v-if="dataForm.fileFlowInstId">
<el-col :span="8" class="mb12" v-if="dataForm.fileFlowInstId">
<div class="view-label">文件审批状态</div>
<div class="view-value">
<el-tag v-if="dataForm.fileFlowStatus === '-2'" type="info">撤回</el-tag>
@@ -495,7 +480,7 @@ 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 { Document, Download, QuestionFilled } from '@element-plus/icons-vue';
import { fetchList as getBusinessDeptList } from '/@/api/purchase/purchasingBusinessDept';
import { getPage as getSchoolLeaderPage } from '/@/api/finance/purchasingschoolleader';
import { Session } from '/@/utils/storage';
@@ -555,9 +540,10 @@ const flowSubmitDisabled = ref(false);
/** 流程嵌入时当前节点是否为采购中心(仅采购中心可编辑采购方式/采购形式) */
const isPurchaseCenter = ref(false);
/** 流程嵌入且为采购中心时,除采购方式/采购形式外其余字段均只读 */
function flowFieldDisabled(key: string) {
return isFlowEmbed.value && isPurchaseCenter.value && !['purchaseType', 'purchaseMode'].includes(key);
/** 流程嵌入时:采购中心审核节点放开所有字段编辑;非采购中心节点只读 */
function flowFieldDisabled(_key: string) {
if (isFlowEmbed.value && isPurchaseCenter.value) return false;
return !!isFlowEmbed.value;
}
// 定义变量内容
@@ -575,6 +561,7 @@ const dataForm = reactive({
purchaseMode: '',
purchaseType: '',
purchaseTypeUnion: '',
purchaseChannel: '',
categoryCode: '',
remark: '',
status: '',
@@ -634,6 +621,7 @@ const purchaseTypeUnionList = ref<any[]>([]);
const businessDeptList = ref<any[]>([]);
const schoolLeaderList = ref<any[]>([]);
const loading = ref(false);
const helpDialogVisible = ref(false);
// 文件类型映射(对应数据库 file_type 字段)
// 10:商务洽谈纪要 20:市场采购纪要 30:网上商城采购相关材料 40:可行性论证报告 50:会议记录 60:其他材料 70:单一来源专家论证表 90:进口产品专家论证表 100:政府采购意向表 110:履约验收单 120:采购需求表 130:采购文件
@@ -670,13 +658,19 @@ Object.entries(FILE_TYPE_MAP).forEach(([field, type]) => {
FILE_TYPE_TO_FIELDS[type].push(field);
});
// 金额阈值常量(与后端 PurchaseConstants 保持一致)
const BUDGET_DEPT_PURCHASE_THRESHOLD = 50000; // 部门自行采购上限(< 5 万)
const BUDGET_FEASIBILITY_THRESHOLD = 300000; // 可行性论证/会议纪要起点(≥ 30 万)
const BUDGET_PUBLIC_SELECT_THRESHOLD = 400000; // 公开比选起点(≥ 40 万)
const BUDGET_GOV_PURCHASE_THRESHOLD = 1000000; // 政府采购起点(≥ 100 万)
// 部门采购方式字典 value与 DeptPurchaseTypeEnum 一致)
const DEPT_PURCHASE_TYPE = {
ONLINE_MALL: '1',
MARKET_PURCHASE: '2',
BUSINESS_NEGOTIATION: '3',
ENTRUST_CENTER: '4',
INQUIRY: '6',
INQUIRY: '5',
} as const;
// 学校统一采购方式字典 value与 PurchaseTypeEnum 一致)
@@ -700,6 +694,12 @@ const isSpecialType = (dictValue: string) => {
return item ? dataForm.isSpecial === item.value : dataForm.isSpecial === dictValue;
};
// 采购途径(与后端 purchasing_apply.purchase_channel 一致1 自行采购 2 委托采购中心采购)
const PURCHASE_CHANNEL = {
SELF: '1',
ENTRUST_CENTER: '2',
} as const;
// 判断是否为部门自行采购
// 条件:特殊情况=否 且 集采=否 且 预算金额<5万 → 部门自行采购
// 其他情况 → 学校统一采购
@@ -714,6 +714,9 @@ const isDeptPurchase = computed(() => {
dataForm.budget && dataForm.budget < 50000);
});
// 是否为“委托采购中心采购”途径
const isEntrustCenterChannel = computed(() => dataForm.purchaseChannel === PURCHASE_CHANNEL.ENTRUST_CENTER);
// 是否已填入“是否特殊情况”“是否集采”“预算金额”,从而能明确是部门自行采购还是学校统一采购(新增时先隐藏采购详情,填完后再显示)
const isPurchaseTypeDetermined = computed(() => {
return dataForm.isSpecial !== '' && dataForm.isCentralized !== '' &&
@@ -728,8 +731,8 @@ const showPurchaseDetailBlocks = computed(() => {
const schoolUnifiedPurchaseFormDefault = computed(() => {
if (isDeptPurchase.value || dataForm.budget == null) return null;
const budget = Number(dataForm.budget);
if (budget >= 1000000) return '1'; // 政府采购
if (budget >= 50000 && budget < 1000000) {
if (budget >= BUDGET_GOV_PURCHASE_THRESHOLD) return '1'; // 政府采购
if (budget >= BUDGET_DEPT_PURCHASE_THRESHOLD && budget < BUDGET_GOV_PURCHASE_THRESHOLD) {
if (dataForm.isCentralized === '0') return '2'; // 集采=否 → 学校自主采购
if (dataForm.isCentralized === '1') return '1'; // 政府集中采购 → 政府采购
if (dataForm.isCentralized === '2') return '2'; // 学校集中采购 → 学校自主采购
@@ -737,8 +740,20 @@ const schoolUnifiedPurchaseFormDefault = computed(() => {
return null;
});
// 学校统一采购时采购形式是否禁用(由规则自动选择,不由用户选择)
const schoolUnifiedPurchaseFormDisabled = computed(() => flowFieldDisabled('purchaseMode') || (schoolUnifiedPurchaseFormDefault.value != null));
// 学校统一采购时采购形式是否禁用
// 申请阶段:始终可选(根据默认值自动选中后,允许用户自行修改)
// 流程嵌入:采购中心节点可编辑,其他节点只读
const schoolUnifiedPurchaseFormDisabled = computed(() => {
if (!isFlowEmbed.value) {
return false;
}
// 流程嵌入且为采购中心:放开编辑
if (isPurchaseCenter.value) {
return false;
}
// 其他流程节点只读
return true;
});
// 特殊情况字典 value0否 1紧急 2单一 3进口
const isUrgentSpecial = computed(() => dataForm.isSpecial === '1');
@@ -823,7 +838,7 @@ const isSpecialServiceCategory = computed(() => {
// - 服务类:若末级节点 isMallService=0 且 isMallProject=0则选“其他方式”否则选“服务类网上商城”
// - 非服务类:默认选“其他方式”
const calcEntrustCenterType = (): 'service_online' | 'other' | '' => {
if (!isPurchaseType(DEPT_PURCHASE_TYPE.ENTRUST_CENTER)) return '';
if (!isEntrustCenterChannel.value) return '';
if (!dataForm.categoryCode) return '';
const category = getCategoryInfo();
@@ -839,9 +854,9 @@ const calcEntrustCenterType = (): 'service_online' | 'other' | '' => {
return 'other';
};
// 监听品目/采购方式变化,自动设置委托采购中心方式,并清理无关字段
// 监听品目/采购途径变化,自动设置委托采购中心方式,并清理无关字段
watch(
[() => dataForm.purchaseType, () => dataForm.categoryCode, () => categoryTreeData.value],
[() => dataForm.purchaseChannel, () => dataForm.categoryCode, () => categoryTreeData.value],
() => {
const nextType = calcEntrustCenterType();
if (!nextType) return;
@@ -870,7 +885,7 @@ watch(
const isAutoSelectPurchaseType = computed(() => {
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= 50000 && budget < 400000 && isServiceCategory.value && isSpecialServiceCategory.value;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD && budget < BUDGET_PUBLIC_SELECT_THRESHOLD && isServiceCategory.value && isSpecialServiceCategory.value;
});
// 判断是否显示自动邀请比选模版5万<=金额<40万服务类目特殊服务类目
@@ -878,7 +893,7 @@ 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;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD && budget < BUDGET_PUBLIC_SELECT_THRESHOLD && isServiceCategory.value && isSpecialServiceCategory.value;
});
// 判断是否显示学校统一采购的自动邀请比选模版5万<=金额<40万服务类目特殊服务类目
@@ -886,7 +901,7 @@ const showAutoInviteSelectSchool = computed(() => {
if (isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= 50000 && budget < 400000 && isSpecialServiceCategory.value;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD && budget < BUDGET_PUBLIC_SELECT_THRESHOLD && isSpecialServiceCategory.value;
});
// 判断是否显示自动公开比选模版40万<=金额<100万特殊服务类目isMallService=1、isProjectService=1
@@ -894,7 +909,7 @@ const showAutoPublicSelect = computed(() => {
if (isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= 400000 && budget < 1000000 && isSpecialServiceCategory.value;
return budget >= BUDGET_PUBLIC_SELECT_THRESHOLD && budget < BUDGET_GOV_PURCHASE_THRESHOLD && isSpecialServiceCategory.value;
});
// 获取需求文件的 prop 名称(用于表单验证)
@@ -916,25 +931,41 @@ const isAutoSelectPurchaseTypeUnion = computed(() => {
if (isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= 50000 && budget < 400000 && isSpecialServiceCategory.value;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD && budget < BUDGET_PUBLIC_SELECT_THRESHOLD && isSpecialServiceCategory.value;
});
// 监听品目编码、预算金额采购类型变化,自动设置/清空采购方式
watch([() => dataForm.categoryCode, () => dataForm.budget, () => isDeptPurchase.value, () => isFlowEmbed.value], () => {
// 监听品目编码、预算金额采购类型及采购途径变化,自动设置/清空采购方式
watch(
[() => dataForm.categoryCode, () => dataForm.budget, () => isDeptPurchase.value, () => isFlowEmbed.value, () => dataForm.purchaseChannel, () => isPurchaseCenter.value],
() => {
// 学校统一采购申请阶段:采购方式隐藏,由审批环节采购中心补充,此处不自动写入且清空已有值
if (!isDeptPurchase.value && !isFlowEmbed.value) {
dataForm.purchaseType = '';
return;
}
// 部门自行采购:自动设置网上商城
if (isAutoSelectPurchaseType.value && isDeptPurchase.value) {
// 部门自行采购 & 采购途径为“委托采购中心采购”且为申请阶段:采购方式隐藏且不设置
if (isDeptPurchase.value && isEntrustCenterChannel.value && !isFlowEmbed.value) {
dataForm.purchaseType = '';
return;
}
// 部门自行采购 & 采购途径为“自行采购”:自动设置网上商城
if (isAutoSelectPurchaseType.value && isDeptPurchase.value && !isEntrustCenterChannel.value) {
const onlineMallOption = purchaseTypeDeptList.value.find(item => item.value === DEPT_PURCHASE_TYPE.ONLINE_MALL);
if (onlineMallOption && dataForm.purchaseType !== onlineMallOption.value) {
dataForm.purchaseType = onlineMallOption.value;
}
}
// 部门自行采购 & 采购途径为“委托采购中心采购” & 采购中心审批节点:根据特殊服务类目自动设置网上商城
if (isAutoSelectPurchaseType.value && isDeptPurchase.value && isEntrustCenterChannel.value && isFlowEmbed.value && isPurchaseCenter.value) {
const onlineMallOption = purchaseTypeDeptList.value.find(item => item.value === DEPT_PURCHASE_TYPE.ONLINE_MALL);
if (onlineMallOption && dataForm.purchaseType !== onlineMallOption.value) {
dataForm.purchaseType = onlineMallOption.value;
}
}
// 学校统一采购审批阶段:自动设置网上商城采购方式
if (isAutoSelectPurchaseTypeUnion.value && !isDeptPurchase.value && isFlowEmbed.value) {
const onlineMallOption = purchaseTypeUnionList.value.find(item => item.value === UNION_PURCHASE_TYPE.ONLINE_MALL);
@@ -942,7 +973,8 @@ watch([() => dataForm.categoryCode, () => dataForm.budget, () => isDeptPurchase.
dataForm.purchaseType = onlineMallOption.value;
}
}
}, { immediate: true });
},
{ immediate: true });
// 学校统一采购:采购形式由规则默认选择,随预算与集采变化自动更新
watch([() => schoolUnifiedPurchaseFormDefault.value, () => isDeptPurchase.value], () => {
@@ -953,21 +985,21 @@ watch([() => schoolUnifiedPurchaseFormDefault.value, () => isDeptPurchase.value]
}
}, { immediate: true });
// 下载模版
// 下载模版:统一走后端接口,按原始文件下载(避免前端静态资源被当成 HTML 返回)
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' },
'inquiry': { fileName: '部门采购询价模版.docx', displayName: '部门采购询价模版.docx' },
'direct_select': { fileName: '服务商城项目需求模板(直选).doc', displayName: '服务商城项目需求模板(直选).doc' },
'public_select': { fileName: '服务商城项目需求模板(公开比选).doc', displayName: '服务商城项目需求模板(公开比选).doc' },
'invite_select': { fileName: '服务商城项目需求模板(邀请比选).doc', displayName: '服务商城项目需求模板(邀请比选).doc' },
'purchase_requirement': { fileName: '默认需求模板.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 templateMap: Record<string, { displayName: string }> = {
business_negotiation: { displayName: '商务洽谈表.doc' },
market_purchase_minutes: { displayName: '部门自行采购市场采购纪要.doc' },
inquiry: { displayName: '部门采购询价模版.doc' },
direct_select: { displayName: '服务商城项目需求模板(直选).doc' },
public_select: { displayName: '服务商城项目需求模板(公开比选).doc' },
invite_select: { displayName: '服务商城项目需求模板(邀请比选).doc' },
purchase_requirement: { displayName: '采购需求填报模板.doc' },
import_application: { displayName: '进口产品申请及专家论证意见表.doc' },
single_source: { displayName: '单一来源论专家证附件.docx' },
feasibility_report: { displayName: '项目可行性论证报告模板.doc' },
};
const template = templateMap[type];
if (!template) {
useMessage().error('模版不存在');
@@ -975,32 +1007,14 @@ const downloadTemplate = async (type: string) => {
}
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);
await other.downBlobFile(
`/purchase/purchasingtemplate/download?type=${encodeURIComponent(type)}`,
{},
template.displayName,
);
useMessage().success('模版下载成功');
} catch (error) {
try {
await other.downBlobFile(
`/purchase/purchasingfiles/downloadTemplate?type=${type}`,
{},
template.displayName
);
useMessage().success('模版下载成功');
} catch (err) {
useMessage().error('模版下载失败,请先维护模版文件');
}
} catch (err) {
useMessage().error('模版下载失败,请联系管理员维护模版文件');
}
};
@@ -1031,8 +1045,38 @@ const dataRules = reactive({
{ required: true, message: '采购内容不能为空', trigger: 'blur' },
{ max: 1000, message: '采购内容不能超过1000字', trigger: 'blur' }
],
purchaseChannel: [
{
validator: (_rule: any, value: string, callback: (e?: Error) => void) => {
if (!isDeptPurchase.value) {
callback();
return;
}
if (!value || String(value).trim() === '') {
callback(new Error('请选择采购途径'));
return;
}
callback();
},
trigger: 'change',
},
],
purchaseType: [
{ required: true, message: '请选择采购方式', trigger: 'change' }
{
validator: (_rule: any, value: string, callback: (e?: Error) => void) => {
// 部门自行采购且采购途径为“委托采购中心采购”并且为申请阶段:此处不校验采购方式
if (isDeptPurchase.value && isEntrustCenterChannel.value && !isFlowEmbed.value) {
callback();
return;
}
if (!value || String(value).trim() === '') {
callback(new Error('请选择采购方式'));
return;
}
callback();
},
trigger: 'change',
},
],
// 学校统一采购时必填
deptClassifyUserId: [
@@ -1098,7 +1142,8 @@ async function loadDetail(applyId: string | number) {
isCentralized: detail.isCentralized != null ? String(detail.isCentralized) : '',
isSpecial: detail.isSpecial != null ? String(detail.isSpecial) : '',
purchaseMode: detail.purchaseMode ?? '',
purchaseType: detail.purchaseType ?? '',
purchaseType: detail.purchaseType === DEPT_PURCHASE_TYPE.ENTRUST_CENTER ? '' : (detail.purchaseType ?? ''),
purchaseChannel: (detail as any).purchaseChannel ?? (detail.purchaseType === DEPT_PURCHASE_TYPE.ENTRUST_CENTER ? PURCHASE_CHANNEL.ENTRUST_CENTER : ''),
purchaseTypeUnion: detail.purchaseTypeUnion ?? '',
categoryCode: detail.categoryCode ?? '',
remark: detail.remark ?? '',
@@ -1346,16 +1391,18 @@ const getIsSpecialDict = async () => {
}
};
// 获取部门采购方式字典
// 获取部门采购方式字典(过滤掉“委托采购中心采购”,由采购途径字段控制)
const getPurchaseTypeDeptDict = async () => {
try {
const res = await getDicts('PURCHASE_TYPE_DEPT');
purchaseTypeDeptList.value = res.data && Array.isArray(res.data)
? res.data.map((item: any) => ({
id: item.id,
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code
}))
? res.data
.map((item: any) => ({
id: item.id,
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code,
}))
.filter((item: any) => item.value !== DEPT_PURCHASE_TYPE.ENTRUST_CENTER)
: [];
} catch (err) {
purchaseTypeDeptList.value = [];

View File

@@ -0,0 +1,250 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<div class="header-actions">
<el-button type="primary" icon="FolderAdd" @click="openUploadDialog()">
新增模板
</el-button>
</div>
</div>
</template>
<el-table :data="tableData" v-loading="loading" stripe class="modern-table">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
</el-table-column>
<el-table-column prop="templateTitle" label="模板类型名称" min-width="180" show-overflow-tooltip />
<el-table-column prop="templateType" label="模板类型编码" min-width="180" show-overflow-tooltip />
<el-table-column prop="templateName" label="模板名称" min-width="220" show-overflow-tooltip />
<el-table-column prop="updateTime" label="更新时间" width="180" show-overflow-tooltip />
<el-table-column label="操作" width="260" align="center" fixed="right">
<template #default="{ row }">
<el-button type="primary" link icon="Download" @click="handleDownload(row)">
下载
</el-button>
<el-button type="primary" link icon="UploadFilled" @click="openUploadDialog(row)">
重新上传
</el-button>
<el-button type="primary" link icon="Edit" @click="openEditDialog(row)">
编辑
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="uploadDialogVisible" title="上传模板" width="450px" destroy-on-close>
<el-form :model="uploadForm" label-width="100px">
<el-form-item label="模板类型编码" required>
<el-input
v-model="uploadForm.templateType"
placeholder="例如: business_negotiation, inquiry"
:disabled="!!uploadForm.lockType"
/>
</el-form-item>
<el-form-item label="模板类型名称">
<el-input
v-model="uploadForm.templateTitle"
placeholder="例如: 部门采购询价模版"
/>
</el-form-item>
<el-form-item label="模板文件" required>
<el-upload
class="upload-block"
:auto-upload="false"
:limit="1"
:file-list="fileList"
:on-change="handleFileChange"
:on-remove="handleFileRemove"
>
<el-button type="primary" icon="UploadFilled">选择文件</el-button>
<template #tip>
<div class="el-upload__tip">支持 docdocx Word 模板文件上传后前端下载将使用该文件</div>
</template>
</el-upload>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="uploadDialogVisible = false"> </el-button>
<el-button type="primary" :loading="uploading" @click="handleUploadConfirm"> </el-button>
</template>
</el-dialog>
<el-dialog v-model="editDialogVisible" title="编辑模板" width="420px" destroy-on-close>
<el-form :model="editForm" label-width="100px">
<el-form-item label="模板类型编码">
<el-input v-model="editForm.templateType" disabled />
</el-form-item>
<el-form-item label="模板类型名称" required>
<el-input v-model="editForm.templateTitle" placeholder="请输入模板类型名称" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="editDialogVisible = false"> </el-button>
<el-button type="primary" :loading="editing" @click="handleEditConfirm"> </el-button>
</template>
</el-dialog>
</div>
</div>
</template>
<script setup lang="ts" name="PurchasingTemplateManage">
import { ref, reactive, onMounted } from 'vue';
import { Document, List, UploadFilled, Download, Edit } from '@element-plus/icons-vue';
import { listTemplates, uploadTemplate, getTemplateDownloadUrl, updateTemplateTitle } from '/@/api/finance/purchasingtemplate';
import { useMessage } from '/@/hooks/message';
const loading = ref(false);
const tableData = ref<any[]>([]);
const uploadDialogVisible = ref(false);
const uploading = ref(false);
const uploadForm = reactive<{
templateType: string;
templateTitle: string;
lockType?: boolean;
}>({
templateType: '',
templateTitle: '',
lockType: false,
});
const editDialogVisible = ref(false);
const editing = ref(false);
const editForm = reactive<{
id: number | null;
templateType: string;
templateTitle: string;
}>({
id: null,
templateType: '',
templateTitle: '',
});
const fileList = ref<any[]>([]);
const currentFile = ref<File | null>(null);
const fetchData = async () => {
loading.value = true;
try {
const res = await listTemplates();
tableData.value = (res && res.data) || [];
} catch (e) {
tableData.value = [];
} finally {
loading.value = false;
}
};
onMounted(fetchData);
const openUploadDialog = (row?: any) => {
uploadDialogVisible.value = true;
uploadForm.templateType = row?.templateType || '';
uploadForm.templateTitle = row?.templateTitle || '';
uploadForm.lockType = !!row?.templateType;
fileList.value = [];
currentFile.value = null;
};
const handleFileChange = (file: any, files: any[]) => {
fileList.value = files.slice(-1);
currentFile.value = file.raw || null;
};
const handleFileRemove = () => {
fileList.value = [];
currentFile.value = null;
};
const handleUploadConfirm = async () => {
if (!uploadForm.templateType || !uploadForm.templateType.trim()) {
useMessage().error('请填写模板类型编码');
return;
}
if (!currentFile.value) {
useMessage().error('请选择要上传的模板文件');
return;
}
uploading.value = true;
try {
const formData = new FormData();
formData.append('type', uploadForm.templateType.trim());
if (uploadForm.templateTitle && uploadForm.templateTitle.trim()) {
formData.append('title', uploadForm.templateTitle.trim());
}
formData.append('file', currentFile.value);
await uploadTemplate(formData);
useMessage().success('模板上传成功');
uploadDialogVisible.value = false;
await fetchData();
} catch (e) {
useMessage().error('模板上传失败');
} finally {
uploading.value = false;
}
};
const handleDownload = async (row: any) => {
if (!row?.templateType) {
useMessage().error('缺少模板类型编码');
return;
}
const url = getTemplateDownloadUrl(row.templateType);
const fileName = row.templateName || row.templateTitle || row.templateType;
try {
await (window as any).other?.downBlobFile?.(url, {}, fileName) ||
// 兼容直接使用工具函数
(await import('/@/utils/other')).default.downBlobFile(url, {}, fileName);
} catch (e) {
// 如果工具函数不可用,则退回 window.open
window.open(url, '_blank');
}
};
const openEditDialog = (row: any) => {
editDialogVisible.value = true;
editForm.id = row?.id ?? null;
editForm.templateType = row?.templateType || '';
editForm.templateTitle = row?.templateTitle || '';
};
const handleEditConfirm = async () => {
if (!editForm.id) {
useMessage().error('缺少模板ID');
return;
}
if (!editForm.templateTitle || !editForm.templateTitle.trim()) {
useMessage().error('请输入模板类型名称');
return;
}
editing.value = true;
try {
await updateTemplateTitle({
id: editForm.id,
templateTitle: editForm.templateTitle.trim(),
});
useMessage().success('保存成功');
editDialogVisible.value = false;
await fetchData();
} catch (e) {
useMessage().error('保存失败');
} finally {
editing.value = false;
}
};
</script>
<style scoped>
.upload-block {
display: inline-block;
}
</style>