Files
school-developer/src/views/purchase/purchasingrequisition/add.vue
2026-03-09 23:15:00 +08:00

2974 lines
106 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="modern-page-container">
<div class="page-wrapper">
<el-card class="content-card" shadow="never">
<template #header v-if="isEditMode">
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
{{ pageTitle }}
</span>
</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"
:rules="dataRules"
label-width="150px"
:disabled="isViewMode || flowFormDisabled"
class="compact-form"
>
<!-- 基础信息 -->
<el-divider content-position="left">
<span class="section-title">基础信息</span>
</el-divider>
<el-text v-if="!isEditMode && !isViewMode" type="info" size="small" class="mb12" style="display: block">
暂存时基础信息必填
</el-text>
<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-form-item>
</el-col>
<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-form-item>
</el-col>
<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>
</el-form-item>
</el-col>
<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')"
/>
<span class="budget-unit"></span>
</div>
</el-form-item>
</el-col>
<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>
</el-form-item>
</el-col>
<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>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12">
<el-form-item label="是否有资产" prop="hasAssets">
<el-radio-group v-model="dataForm.hasAssets" :disabled="flowFieldDisabled('hasAssets')">
<el-radio label="1"></el-radio>
<el-radio label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<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: false }"
placeholder="请选择品目编码(仅最后一级)"
clearable
filterable
:show-all-levels="true"
style="width: 100%"
:disabled="flowFieldDisabled('categoryCode')"
@change="handleCategoryChange"
/>
<el-text v-if="isSpecialServiceCategory && dataForm.categoryCode" type="warning" size="small" style="margin-top: 4px">
当前选择品目为服务商城品目
</el-text>
</el-form-item>
</el-col>
</el-row>
<!-- 采购详情新增时需先填是否特殊情况是否集采预算金额后才显示 -->
<div>
<el-alert v-if="!showPurchaseDetailBlocks" type="info" :closable="false" class="mb16" show-icon>
请先填写上方是否特殊情况是否集采预算金额系统将根据填写结果展示部门自行采购学校统一采购表单
</el-alert>
<!-- 分支一部门自行采购三列紧凑textarea 单独一行 -->
<div class="mb20 form-section-compact" v-if="showPurchaseDetailBlocks && isDeptPurchase">
<div class="step-title mb12">部门自行采购</div>
<!-- 部门采购会议纪要预算超过2000元必须上传 -->
<el-row :gutter="16" v-if="dataForm.budget != null && dataForm.budget >= BUDGET_DEPT_SELF_MEETING_MINUTES">
<el-col :span="8" class="mb12">
<el-form-item label="部门采购会议纪要" prop="deptSelfMeetingMinutes" required>
<upload-file
v-model="dataForm.deptSelfMeetingMinutes"
:limit="1"
:file-type="['pdf', 'jpg', 'jpeg', 'png']"
:data="{ fileType: FILE_TYPE_MAP.deptSelfMeetingMinutes }"
upload-file-url="/purchase/purchasingfiles/upload"
:disabled="flowFieldDisabled('deptSelfMeetingMinutes')"
/>
</el-form-item>
</el-col>
</el-row>
<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-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="8" class="mb12" v-if="showPurchaseTypeField">
<el-form-item label="采购方式" prop="purchaseType" :required="isPurchaseTypeRequired">
<el-select
v-model="dataForm.purchaseType"
placeholder="请选择采购方式"
clearable
:disabled="isPurchaseTypeDisabled"
style="width: 100%"
>
<el-option v-for="item in purchaseTypeDeptOptions" :key="item.value" :label="item.label" :value="item.value" :disabled="item.disabled" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="24" class="mb12" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.BUSINESS_NEGOTIATION)">
<el-form-item label="洽谈理由" prop="negotiationReason" required>
<el-input
v-model="dataForm.negotiationReason"
type="textarea"
:rows="3"
:maxlength="500"
show-word-limit
placeholder="请输入洽谈理由限制500字"
clearable
:disabled="flowFieldDisabled('negotiationReason')"
/>
</el-form-item>
</el-col>
<!-- 委托采购中心采购 - 特殊服务品目显示是否有供应商选择 -->
<el-col
:span="8"
class="mb12"
v-if="isEntrustCenterChannel && isSpecialServiceCategory && isServiceCategory && dataForm.categoryCode"
>
<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="
isEntrustCenterChannel && isSpecialServiceCategory && isServiceCategory && dataForm.hasSupplier === '1' && dataForm.categoryCode
"
>
<el-form-item label="需求文件" prop="serviceDirectSelect" required>
<upload-file
v-model="dataForm.serviceDirectSelect"
:limit="1"
:file-type="['doc', 'docx']"
: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"
>下载《{{ getTemplateName('direct_select') }}》模版
</el-button>
</el-form-item>
</el-col>
<!-- 委托采购中心采购 - 特殊服务品目 + 无供应商:公开比选模板 -->
<el-col
:span="8"
class="mb12"
v-if="
isEntrustCenterChannel && isSpecialServiceCategory && isServiceCategory && dataForm.hasSupplier === '0' && dataForm.categoryCode
"
>
<el-form-item label="需求文件" prop="serviceInviteSelect" required>
<upload-file
v-model="dataForm.serviceInviteSelect"
:limit="1"
:file-type="['doc', 'docx']"
: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"
>下载《{{ getTemplateName('public_select') }}》模版
</el-button>
</el-form-item>
</el-col>
<!-- 委托采购中心采购 - 其他情况(非特殊服务品目):默认需求模板 -->
<el-col :span="8" class="mb12" v-if="isEntrustCenterChannel && !isSpecialServiceCategory && dataForm.categoryCode">
<el-form-item label="需求文件" prop="purchaseRequirementTemplate" required>
<upload-file
v-model="dataForm.purchaseRequirementTemplate"
:limit="1"
:file-type="['doc', 'docx']"
: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"
>下载《{{ getTemplateName('purchase_requirement') }}》模版
</el-button>
</el-form-item>
</el-col>
<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>
<el-radio label="0">无</el-radio>
</el-radio-group>
</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']"
: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"
>下载《{{ getTemplateName('invite_select') }}》模版
</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']"
: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"
>下载《{{ getTemplateName('public_select') }}》模版
</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>
<!-- 部门采购会议纪要预算超过2000元必须上传 -->
<el-row :gutter="16" v-if="dataForm.budget != null && dataForm.budget >= BUDGET_DEPT_SELF_MEETING_MINUTES">
<el-col :span="8" class="mb12">
<el-form-item label="部门采购会议纪要" prop="deptSelfMeetingMinutes" required>
<upload-file
v-model="dataForm.deptSelfMeetingMinutes"
:limit="1"
:file-type="['pdf', 'jpg', 'jpeg', 'png']"
:data="{ fileType: FILE_TYPE_MAP.deptSelfMeetingMinutes }"
upload-file-url="/purchase/purchasingfiles/upload"
:disabled="flowFieldDisabled('deptSelfMeetingMinutes')"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="8" class="mb12">
<el-form-item label="组织采购形式" prop="purchaseSchool" :required="isPurchaseSchoolRequired">
<el-radio-group v-model="dataForm.purchaseSchool" :disabled="schoolUnifiedPurchaseFormDisabled">
<el-radio v-for="item in purchaseModeSchoolList" :key="item.value" :label="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12">
<el-form-item label="采购方式" prop="purchaseType" :required="isPurchaseTypeUnionRequired">
<el-select
v-model="dataForm.purchaseType"
placeholder="请选择采购方式"
clearable
:disabled="
isFlowEmbed && isPurchaseCenter
? false
: isAutoSelectPurchaseTypeUnion || flowFieldDisabled('purchaseType') || !isPurchaseCenter
"
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" v-if="!isSpecialCase">
<el-form-item label="业务分管校领导" prop="schoolBusinessLeaderUserId" :required="!isDeptPurchase">
<el-select
v-model="dataForm.schoolBusinessLeaderUserId"
placeholder="请选择业务分管校领导"
clearable
filterable
@change="handleBusinessLeaderChange"
style="width: 100%"
:disabled="flowFieldDisabled('schoolBusinessLeaderUserId')"
>
<el-option v-for="item in businessLeaderList" :key="item.id" :label="item.name" :value="item.userId" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12">
<el-form-item label="校党委" prop="schoolLeaderUserId" v-if="isSpecialCase" :required="!isDeptPurchase && isSpecialCase">
<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">
<el-form-item label="采购分管校领导" prop="purchaseManagerUserId" :required="!isDeptPurchase">
<el-select
v-model="dataForm.purchaseManagerUserId"
placeholder="请选择采购分管校领导"
clearable
filterable
@change="handlePurchaseManagerChange"
style="width: 100%"
:disabled="flowFieldDisabled('purchaseManagerUserId')"
>
<el-option v-for="item in purchasingManagerList" :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="['pdf', 'jpg', 'jpeg', 'png']"
: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"
>下载《{{ getTemplateName('feasibility_report') }}》模版
</el-button>
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="dataForm.budget && dataForm.budget >= BUDGET_FEASIBILITY_THRESHOLD && isSpecialType('0')">
<el-form-item label="校党委会议纪要" prop="meetingMinutes" required>
<upload-file
v-model="dataForm.meetingMinutes"
:limit="1"
:file-type="['pdf', 'jpg', 'jpeg', 'png']"
: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="isSpecialType('2')">
<el-form-item label="单一来源论专家证附件" prop="singleSourceProof" required>
<upload-file
v-model="dataForm.singleSourceProof"
:limit="1"
:file-type="['pdf', 'jpg', 'jpeg', 'png']"
: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"
>下载《{{ getTemplateName('single_source') }}》模版
</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="['pdf', 'jpg', 'jpeg', 'png']"
: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="['pdf', 'jpg', 'jpeg', 'png']"
: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"
>下载《{{ getTemplateName('import_application') }}》模版
</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="['pdf', 'jpg', 'jpeg', 'png']"
: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="8" class="mb12">
<el-form-item label="需求文件" :prop="getRequirementFileProp()" required class="mb16">
<template v-if="showAutoInviteSelectSchool">
<template v-if="dataForm.hasSupplier === '1'">
<upload-file
v-model="dataForm.serviceInviteSelectSchool"
:limit="1"
:file-type="['doc', 'docx']"
:data="{ fileType: FILE_TYPE_MAP.serviceInviteSelectSchool }"
upload-file-url="/purchase/purchasingfiles/upload"
:disabled="flowFieldDisabled('serviceInviteSelectSchool')"
/>
<el-button
type="primary"
link
icon="Download"
size="small"
@click="downloadTemplate('invite_select')"
style="margin-top: 8px; display: inline-block"
>下载《{{ getTemplateName('invite_select') }}》模版
</el-button>
</template>
<template v-else-if="dataForm.hasSupplier === '0'">
<upload-file
v-model="dataForm.servicePublicSelectSchoolAuto"
:limit="1"
:file-type="['doc', 'docx']"
:data="{ fileType: FILE_TYPE_MAP.servicePublicSelectSchoolAuto }"
upload-file-url="/purchase/purchasingfiles/upload"
:disabled="flowFieldDisabled('servicePublicSelectSchoolAuto')"
/>
<el-button
type="primary"
link
icon="Download"
size="small"
@click="downloadTemplate('public_select')"
style="margin-top: 8px; display: inline-block"
>下载《{{ getTemplateName('public_select') }}》模版
</el-button>
</template>
</template>
<template v-else-if="showAutoPublicSelect">
<upload-file
v-model="dataForm.servicePublicSelectSchool"
:limit="1"
:file-type="['doc', 'docx']"
:data="{ fileType: FILE_TYPE_MAP.servicePublicSelectSchool }"
upload-file-url="/purchase/purchasingfiles/upload"
:disabled="flowFieldDisabled('servicePublicSelectSchool')"
/>
<el-button
type="primary"
link
icon="Download"
size="small"
@click="downloadTemplate('public_select')"
style="margin-top: 8px; display: inline-block"
>下载《{{ getTemplateName('public_select') }}》模版
</el-button>
</template>
<template v-else>
<upload-file
v-model="dataForm.purchaseRequirement"
:limit="1"
:file-type="['doc', 'docx']"
:data="{ fileType: FILE_TYPE_MAP.purchaseRequirement }"
upload-file-url="/purchase/purchasingfiles/upload"
:disabled="flowFieldDisabled('purchaseRequirement')"
/>
<el-button
type="primary"
link
icon="Download"
size="small"
@click="downloadTemplate('purchase_requirement')"
style="margin-top: 8px; display: inline-block"
>下载《{{ getTemplateName('purchase_requirement') }}》模版
</el-button>
</template>
</el-form-item>
</el-col>
<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="['pdf', 'jpg', 'jpeg', 'png']"
:data="{ fileType: FILE_TYPE_MAP.governmentPurchaseIntent }"
upload-file-url="/purchase/purchasingfiles/upload"
:disabled="flowFieldDisabled('governmentPurchaseIntent')"
/>
<el-button
type="primary"
link
icon="Download"
size="small"
@click="downloadTemplate('gov_pur_int')"
style="margin-top: 8px; display: inline-block"
>下载《{{ getTemplateName('gov_pur_int') }}》模版
</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>
<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>
<!-- 查看时:招标代理(与实施采购信息同风格:分割线+标签值同行) -->
<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="8" class="mb12">
<div class="view-label">代理名称</div>
<div class="view-value">{{ dataForm.agentName }}</div>
</el-col>
</el-row>
</div>
<div v-if="isViewMode && dataForm.representorName" class="implement-info-block mb20">
<el-divider content-position="left">采购代表/评委</el-divider>
<el-row :gutter="24">
<el-col :span="8" class="mb12">
<div class="view-label">参与人员</div>
<div class="view-value">
{{ dataForm.representorName }}
<el-tag v-if="dataForm.representorType" type="info" size="small" style="margin-left: 8px">
{{ dataForm.representorType }}
</el-tag>
</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>
在「部门自行采购」且为服务类特殊品目时,系统会自动将采购方式推荐为「网上商城」(服务类),同时要求上传相应模板文件。
</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>
在「部门自行采购」中选择「委托采购中心采购」作为采购途径时,申请阶段隐藏采购方式,由采购中心在审核环节选择;
对服务类特殊品目,采购中心会优先推荐「网上商城(服务网上商城)」方式。
</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="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="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>
<el-tag v-else-if="dataForm.fileFlowStatus === '-1'" type="warning">暂存</el-tag>
<el-tag v-else-if="dataForm.fileFlowStatus === '0'" type="primary">运行中</el-tag>
<el-tag v-else-if="dataForm.fileFlowStatus === '1'" type="success">完成</el-tag>
<el-tag v-else-if="dataForm.fileFlowStatus === '2'" type="danger">作废</el-tag>
<el-tag v-else-if="dataForm.fileFlowStatus === '3'" type="info">终止</el-tag>
<span v-else>—</span>
</div>
</el-col>
</el-row>
<div v-if="viewImplementPurchaseFiles.length" class="mb16">
<div class="view-label mb8">招标文件</div>
<el-table :data="viewImplementPurchaseFiles" border size="small" max-height="240">
<el-table-column type="index" label="版本" width="70" align="center">
<template #default="{ $index }">V{{ $index + 1 }}</template>
</el-table-column>
<el-table-column prop="fileTitle" label="文件名" min-width="180" show-overflow-tooltip />
<el-table-column label="上传时间" width="165" align="center">
<template #default="{ row }">{{ formatImplementFileTime(row.createTime) }}</template>
</el-table-column>
<el-table-column label="操作" width="90" align="center">
<template #default="{ row }">
<el-button type="primary" link size="small" @click="downloadImplementFile(row)">下载</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</el-form>
<!-- 操作按钮(流程嵌入时隐藏取消/返回,由流程页处理) -->
<div class="form-footer">
<template v-if="isViewMode">
<el-button v-if="!isFlowEmbed" @click="handleCancel">返回</el-button>
</template>
<template v-else>
<el-button v-if="!isFlowEmbed" @click="handleCancel">取消</el-button>
<el-button v-if="!isFlowEmbed && !flowSubmitDisabled" type="warning" @click="handleTempStore" :disabled="loading"> 暂存 </el-button>
<el-button v-if="!isFlowEmbed" type="primary" @click="handleSubmitFlow" :disabled="loading"> 提交 </el-button>
</template>
</div>
</div>
</el-card>
</div>
</div>
</template>
<script setup lang="ts" name="PurchasingRequisitionAdd">
import { reactive, ref, onMounted, computed, watch, nextTick } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { addObj, tempStore, getObj, editObj, getApplyFiles, submitObj } from '/@/api/purchase/purchasingrequisition';
import { getTree } from '/@/api/purchase/purchasingcategory';
import { getDicts } from '/@/api/admin/dict';
import { useMessage, useMessageBox } from '/@/hooks/message';
import { useUserInfo } from '/@/stores/userInfo';
import { usePurchaseRulesSingleton } from '/@/hooks/usePurchaseRules';
import UploadFile from '/@/components/Upload/index.vue';
import other from '/@/utils/other';
import { Document, Download, QuestionFilled } from '@element-plus/icons-vue';
import { fetchList as getBusinessDeptList } from '/@/api/purchase/purchasingBusinessDept';
import { getPage as getSchoolLeaderPage } from '/@/api/purchase/purchasingschoolleader';
import { fetchList as getPurchasingManagerList } from '/@/api/purchase/purchasingPurchaseManager';
import { fetchList as getBusinessLeaderList } from '/@/api/purchase/purchasingBusinessLeader';
import { Session } from '/@/utils/storage';
import * as orderVue from '/@/api/order/order-key-vue';
import { listTemplates } from '/@/api/purchase/purchasingtemplate';
/** 采购中心角色编码:审批时仅该角色可编辑采购方式/采购形式 */
const PURCHASE_CENTER_ROLE_CODE = 'PURCHASE_CENTER';
const PURCHASE_DEPT_APPLY_ROLE_CODE = 'ROLE_PURCHASE_DEPT_APPLY';
// 兼容流程 dynamic-link 引用:接收 currJob / currElTab并支持 handleJob 事件
const props = defineProps({
currJob: { type: Object, default: null },
currElTab: { type: Object, default: null },
});
const emit = defineEmits(['handleJob']);
// 路由
const router = useRouter();
const route = useRoute();
/** 是否被流程 handle 页通过 dynamic-link 嵌入 */
const isFlowEmbed = computed(() => !!props.currJob);
/** 当前使用的申请单 ID优先来自流程 currJob.orderId否则来自 route.query.id */
const effectiveQueryId = computed(() => {
if (props.currJob?.orderId != null && props.currJob?.orderId !== '') {
return String(props.currJob.orderId);
}
const q = route.query.id;
return q ? String(q) : '';
});
// 模式add | edit | viewURL 参数 或 流程嵌入时的 currJob/currElTab
const isEditMode = computed(() => {
if (isFlowEmbed.value && props.currElTab) {
return !!effectiveQueryId.value && props.currElTab.isFormEdit !== '0' && !props.currJob?.hiJob;
}
return String(route.query.mode) === 'edit';
});
const isViewMode = computed(() => {
if (isFlowEmbed.value && props.currJob) {
if (props.currJob.hiJob) return true;
if (props.currElTab?.isFormEdit === '0') return true;
return false;
}
return String(route.query.mode) === 'view';
});
const pageTitle = computed(() => {
if (isViewMode.value) return '查看采购申请';
if (isEditMode.value) return '编辑采购申请';
return '新增采购申请';
});
/** 流程嵌入时,由 currElTabIsView 控制的只读/禁用提交 */
const flowFormDisabled = ref(false);
const flowSubmitDisabled = ref(false);
/** 流程嵌入时当前节点是否为采购中心(仅采购中心可编辑采购方式/采购形式) */
const isPurchaseCenter = ref(false);
/** 流程嵌入时:采购中心审核节点放开所有字段编辑;非采购中心节点只读 */
function flowFieldDisabled(_key: string) {
if (isFlowEmbed.value && isPurchaseCenter.value) return false;
if (isFlowEmbed.value && isApplicant.value) return false;
return !!isFlowEmbed.value;
}
/** 当前用户是否为申请人(在流程中可编辑) */
const isApplicant = computed(() => {
const stores = useUserInfo();
const currentUser = stores.userInfos?.user || {};
if (!dataForm.createBy) return false;
// const stores = useUserInfo();
// const currentUser = stores.userInfos?.user || {};
return dataForm.createBy === currentUser.username;
});
// 定义变量内容
const formRef = ref();
const dataForm = reactive({
id: '',
projectName: '',
projectType: '',
projectContent: '',
applyDate: '',
fundSource: '',
budget: null as number | null,
isCentralized: '',
isSpecial: '',
hasAssets: '0',
purchaseMode: '',
purchaseSchool: '',
purchaseType: '',
purchaseTypeUnion: '',
purchaseChannel: '',
categoryCode: '',
remark: '',
status: '',
createBy: '', // 创建人用户名,用于判断是否为申请人
// 部门自行采购字段
businessNegotiationTable: '',
marketPurchaseMinutes: '',
onlineMallMaterials: '',
inquiryTemplate: '', // 询价模板
entrustCenterType: '',
hasSupplier: '0',
suppliers: '', // 供应商名称(逗号或分号分隔)
serviceDirectSelect: '',
servicePublicSelect: '',
purchaseRequirementTemplate: '',
serviceInviteSelect: '',
servicePublicSelectAuto: '',
deptSelfMeetingMinutes: '', // 部门会议纪要
// 学校统一采购字段
purchaseRequirement: '',
meetingMinutes: '',
feasibilityReport: '',
meetingMinutesUrgent: '',
meetingMinutesSingle: '',
meetingMinutesImport: '',
singleSourceProof: '',
importApplication: '',
governmentPurchaseIntent: '',
servicePublicSelectSchool: '',
// 学校统一采购特殊规则字段5万<=金额<30万
serviceInviteSelectSchool: '',
servicePublicSelectSchoolAuto: '',
// 业务分管处室和分管校领导
deptClassifyUserId: '',
deptClassifyName: '',
schoolLeaderUserId: '',
schoolLeaderName: '',
// 业务分管校领导
schoolBusinessLeaderUserId: '',
schoolBusinessLeaderName: '',
// 采购分管
purchaseManagerUserId: '',
purchaseManagerName: '',
// 其他材料zip压缩包
otherMaterials: '',
// 实施采购信息(查看时展示)
implementType: '',
fileFlowInstId: '',
fileFlowStatus: '',
agentId: '',
agentName: '',
representorName: '',
representorType: '',
negotiationReason: '',
});
/** 查看时展示的招标文件列表(实施采购上传的 type=130 */
const viewImplementPurchaseFiles = ref<{ id: string; fileTitle?: string; createTime?: string; remark?: string }[]>([]);
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 purchaseTypeDeptDelegationList = ref<any[]>([]);
const purchaseModeSchoolList = ref<any[]>([]);
const purchaseTypeUnionList = ref<any[]>([]);
const businessDeptList = ref<any[]>([]);
const schoolLeaderList = ref<any[]>([]);
const purchasingManagerList = ref<any[]>([]);
const businessLeaderList = ref<any[]>([]);
/** 人员类型字典(采购代表/评委) */
const representorTypeList = ref<any[]>([]);
/** 模版列表(从后台获取,用于显示模版名称) */
const templateList = ref<any[]>([]);
const loading = ref(false);
const helpDialogVisible = ref(false);
/** 数据是否已加载完成(用于控制自动设置采购方式的逻辑) */
const initialDataLoaded = ref(false);
// 文件类型映射(对应数据库 file_type 字段)
// 10:商务洽谈纪要 20:市场采购纪要 30:网上商城采购相关材料 40:可行性论证报告 50:会议记录 60:其他材料 70:单一来源专家论证表 90:进口产品专家论证表 100:政府采购意向表 110:履约验收单 120:采购需求表 130:招标文件 140: 部门会议纪要
const FILE_TYPE_MAP: Record<string, string> = {
businessNegotiationTable: '10', // 商务洽谈纪要
marketPurchaseMinutes: '20', // 市场采购纪要
inquiryTemplate: '20', // 询价模板(归类到市场采购纪要)
onlineMallMaterials: '30', // 网上商城采购相关材料
feasibilityReport: '40', // 可行性论证报告
meetingMinutes: '50', // 会议记录
meetingMinutesUrgent: '50', // 会议记录
meetingMinutesSingle: '50', // 会议记录
meetingMinutesImport: '50', // 会议记录
otherMaterials: '60', // 其他材料
singleSourceProof: '70', // 单一来源专家论证表
importApplication: '90', // 进口产品申请表
governmentPurchaseIntent: '100', // 政府采购意向表
// 需求文件相关 - 所有需求模板都应该是 120采购需求表
serviceDirectSelect: '120', // 服务商城项目需求模板(直选)- 采购需求表
serviceInviteSelect: '120', // 服务商城项目需求模板(邀请比选)- 采购需求表
servicePublicSelect: '120', // 服务商城项目需求模板(公开比选)- 采购需求表
servicePublicSelectAuto: '120', // 服务商城项目需求模板(公开比选 - 自动)- 采购需求表
purchaseRequirementTemplate: '120', // 需求模板 - 采购需求表
purchaseRequirement: '120', // 需求模板 - 采购需求表
serviceInviteSelectSchool: '120', // 服务商城项目需求模板(邀请比选 - 学校)- 采购需求表
servicePublicSelectSchoolAuto: '120', // 服务商城项目需求模板(公开比选 - 学校 - 自动)- 采购需求表
servicePublicSelectSchool: '120', // 服务商城项目需求模板(公开比选 - 学校)- 采购需求表
deptSelfMeetingMinutes: '140', // 部门会议纪要
};
// fileType -> 表单字段名数组(顺序与回填分配一致,同类型多字段时按此顺序分配)
const FILE_TYPE_TO_FIELDS: Record<string, string[]> = {};
Object.entries(FILE_TYPE_MAP).forEach(([field, type]) => {
if (!FILE_TYPE_TO_FIELDS[type]) FILE_TYPE_TO_FIELDS[type] = [];
FILE_TYPE_TO_FIELDS[type].push(field);
});
// 金额阈值(从规则配置动态获取,默认值与后端 PurchaseConstants 保持一致)
const { rules: purchaseRules, getThresholds, evaluate: evaluatePurchaseRules } = usePurchaseRulesSingleton();
const BUDGET_DEPT_PURCHASE_THRESHOLD = computed(() => getThresholds().deptPurchase);
const BUDGET_FEASIBILITY_THRESHOLD = computed(() => getThresholds().feasibility);
const BUDGET_PUBLIC_SELECT_THRESHOLD = computed(() => getThresholds().publicSelect);
const BUDGET_GOV_PURCHASE_THRESHOLD = computed(() => getThresholds().govPurchase);
const BUDGET_DEPT_SELF_MEETING_MINUTES = computed(() => getThresholds().deptSelfMeetingMinutes);
// 部门采购方式字典 value与 DeptPurchaseTypeEnum 一致)
const DEPT_PURCHASE_TYPE = {
ONLINE_MALL: '1',
MARKET_PURCHASE: '2',
BUSINESS_NEGOTIATION: '3',
ENTRUST_CENTER: '4',
INQUIRY: '5',
DIRECT_PURCHASE: '6',
OPEN_TENDERING: '100',
} as const;
// 学校统一采购方式字典 value与 PurchaseTypeEnum 一致)
const UNION_PURCHASE_TYPE = {
ONLINE_MALL: '8',
} as const;
// 辅助函数:判断当前采购方式是否为指定类型(通过字典 value 匹配)
const isPurchaseType = (dictValue: string) => {
if (!dataForm.purchaseType) return false;
return dataForm.purchaseType === dictValue;
};
// 辅助函数:判断当前采购方式是否为"询价"(通过 value 匹配)
const isInquiryPurchaseType = computed(() => isPurchaseType(DEPT_PURCHASE_TYPE.INQUIRY));
// 辅助函数:判断特殊情况是否为指定类型(通过字典 value 匹配)
const isSpecialType = (dictValue: string) => {
if (!dataForm.isSpecial) return false;
const item = isSpecialList.value.find((item) => item.value === dictValue);
return item ? dataForm.isSpecial === item.value : dataForm.isSpecial === dictValue;
};
// 辅助函数:根据人员类型值获取标签(采购代表/评委类型)
const getRepresentorTypeLabel = (value: string): string => {
const item = representorTypeList.value.find((item) => item.value === value);
return item ? item.label : value;
};
// 采购途径(与后端 purchasing_apply.purchase_channel 一致1 自行采购 2 委托采购中心采购)
const PURCHASE_CHANNEL = {
SELF: '1',
ENTRUST_CENTER: '2',
} as const;
// 判断是否为部门自行采购
// 条件:特殊情况=否 且 集采=否 且 预算金额<5万 → 部门自行采购
// 其他情况 → 学校统一采购
const isDeptPurchase = computed(() => {
const isSpecialNo = isSpecialList.value.find((item) => item.value === '0');
const isCentralizedNo = isCentralizedList.value.find((item) => item.value === '0');
const isSpecialNoValue = isSpecialNo ? isSpecialNo.value : null;
const isCentralizedNoValue = isCentralizedNo ? isCentralizedNo.value : null;
return !!(
isSpecialNoValue &&
isCentralizedNoValue &&
dataForm.isSpecial === isSpecialNoValue &&
dataForm.isCentralized === isCentralizedNoValue &&
dataForm.budget &&
dataForm.budget < BUDGET_DEPT_PURCHASE_THRESHOLD.value
);
});
// 是否为“委托采购中心采购”途径
const isEntrustCenterChannel = computed(() => dataForm.purchaseChannel === PURCHASE_CHANNEL.ENTRUST_CENTER);
// 是否显示采购方式字段
// 申请阶段:部门自行采购 + 自行采购 时显示
// 审核阶段:采购中心始终可见;其他角色在委托采购中心采购且有值时也可见
const showPurchaseTypeField = computed(() => {
// 审核阶段:采购中心始终可见
if (isFlowEmbed.value && isPurchaseCenter.value) {
return true;
}
// 审核阶段(非采购中心):委托采购中心采购时,如果已有采购方式值则显示(只读查看)
if (isFlowEmbed.value && !isPurchaseCenter.value && isEntrustCenterChannel.value && dataForm.purchaseType) {
return true;
}
// 申请阶段:仅部门自行采购 + 自行采购时显示
if (isDeptPurchase.value && !isEntrustCenterChannel.value) {
return true;
}
return false;
});
// 部门自行采购 - 采购方式是否必填
const isPurchaseTypeRequired = computed(() => {
// 审核阶段:采购中心 + 委托采购中心采购 → 必填
if (isFlowEmbed.value && isPurchaseCenter.value && isEntrustCenterChannel.value) {
return true;
}
// 申请阶段:部门自行采购 + 自行采购时必填
if (isDeptPurchase.value && !isEntrustCenterChannel.value) {
return true;
}
return false;
});
// 学校统一采购 - 采购方式是否必填
const isPurchaseTypeUnionRequired = computed(() => {
// 审核阶段:采购中心必填
if (isFlowEmbed.value && isPurchaseCenter.value) {
return true;
}
return false;
});
// 学校统一采购 - 组织采购形式是否必填
const isPurchaseSchoolRequired = computed(() => {
// 审核阶段:采购中心必填
if (isFlowEmbed.value && isPurchaseCenter.value) {
return true;
}
return false;
});
// 采购方式是否禁用
const isPurchaseTypeDisabled = computed(() => {
// 审核阶段:采购中心可编辑
if (isFlowEmbed.value && isPurchaseCenter.value) {
return false;
}
// 审核阶段(非采购中心):委托采购中心采购时,只读
if (isFlowEmbed.value && !isPurchaseCenter.value && isEntrustCenterChannel.value) {
return true;
}
// 申请阶段:根据其他条件禁用
return isDeptSelfMallLocked.value || isAutoSelectPurchaseType.value;
});
// 是否已填入“是否特殊情况”“是否集采”“预算金额”,从而能明确是部门自行采购还是学校统一采购(新增时先隐藏采购详情,填完后再显示)
const isPurchaseTypeDetermined = computed(() => {
return dataForm.isSpecial !== '' && dataForm.isCentralized !== '' && dataForm.budget != null && Number(dataForm.budget) > 0;
});
// 是否显示“部门自行采购/学校统一采购”区块:查看/编辑/流程嵌入/已保存过 时直接显示;新增时仅当类型已明确后显示
const showPurchaseDetailBlocks = computed(() => {
return !!isViewMode.value || !!isFlowEmbed.value || !!dataForm.id || isPurchaseTypeDetermined.value;
});
// 学校统一采购时组织采购形式是否禁用
// 申请阶段:始终可选(根据默认值自动选中后,允许用户自行修改)
// 流程嵌入:采购中心节点可编辑,其他节点只读
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');
// 是否为特殊情况非0即为特殊情况紧急、单一、进口或预算金额>=30万
const isSpecialCase = computed(() => {
// 特殊情况或预算金额>=30万时需要选择校党委
const isSpecial = dataForm.isSpecial && dataForm.isSpecial !== '0';
const isHighBudget = dataForm.budget != null && dataForm.budget >= BUDGET_FEASIBILITY_THRESHOLD.value;
return isSpecial || isHighBudget;
});
// 第二步标题
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';
});
// 判断是否为货物类
const isGoodsCategory = computed(() => {
const category = getCategoryInfo();
if (!category) return false;
return category.type === 'A';
});
// 判断是否为工程类
const isEngineeringCategory = computed(() => {
// 通过 categoryCodePath 判断:第一个字符为 'B' 表示工程类
if (categoryCodePath.value && categoryCodePath.value.length > 0) {
return categoryCodePath.value[0] === 'B';
}
// 备用判断:通过 category 对象判断
const category = getCategoryInfo();
if (!category) return false;
return category.type === 'B';
});
// 判断是否为特殊服务类目isMallService=1、isProjectService=1
const isSpecialServiceCategory = computed(() => {
const category = getCategoryInfo();
if (!category) return false;
return Number(category.isMallService) === 1 || Number(category.isProjectService) === 1;
});
// 部门自行采购 & 采购途径=自行采购 & 特殊服务类目 → 采购方式固定网上商城
const isDeptSelfMallLocked = computed(() => {
return isDeptPurchase.value && !isEntrustCenterChannel.value && isSpecialServiceCategory.value;
});
// 委托采购中心方式自动判断:
// - 服务类:若末级节点 isMallService=0 且 isMallProject=0则选“其他方式”否则选“服务类网上商城”
// - 非服务类:默认选“其他方式”
const calcEntrustCenterType = (): 'service_online' | 'other' | '' => {
if (!isEntrustCenterChannel.value) return '';
if (!dataForm.categoryCode) return '';
const category = getCategoryInfo();
if (!category) return '';
// 兼容字段:接口可能为 isMallProject也可能历史字段为 isProjectService
const mallService = Number(category.isMallService ?? 0);
const mallProject = Number(category.isMallProject ?? category.isProjectService ?? 0);
if (isServiceCategory.value) {
return mallService === 0 && mallProject === 0 ? 'other' : 'service_online';
}
return 'other';
};
// 监听采购途径变化清理所有与采购方式相关的材料字段同时如果已选择品目则重新计算entrustCenterType
watch(
() => dataForm.purchaseChannel,
(newChannel, oldChannel) => {
// 仅在采购途径发生变化时清理
if (newChannel === oldChannel) return;
// 编辑模式dataForm.id存在如果是首次加载数据oldChannel为空不清理
// 首次加载时保持原有数据,用户手动切换时需要清理
if (dataForm.id && !oldChannel) return;
// 采购途径变化时清空采购方式和所有材料字段
// 避免"自行采购"切换回"委托采购中心采购"时,仍显示原"网上商城"对应的字段
dataForm.purchaseType = '';
dataForm.entrustCenterType = '';
// 自行采购的材料字段
dataForm.businessNegotiationTable = '';
dataForm.marketPurchaseMinutes = '';
dataForm.onlineMallMaterials = '';
dataForm.inquiryTemplate = '';
dataForm.serviceInviteSelect = '';
dataForm.servicePublicSelectAuto = '';
// 委托采购中心的材料字段
dataForm.serviceDirectSelect = '';
dataForm.servicePublicSelect = '';
dataForm.purchaseRequirementTemplate = '';
dataForm.hasSupplier = '0';
dataForm.suppliers = '';
// 如果切换到委托采购中心采购且已选择品目重新计算entrustCenterType
if (newChannel === PURCHASE_CHANNEL.ENTRUST_CENTER && dataForm.categoryCode) {
nextTick(() => {
const type = calcEntrustCenterType();
if (type) {
dataForm.entrustCenterType = type;
}
});
}
},
{ immediate: true }
);
// 监听品目变化,自动设置委托采购中心方式
watch(
[() => dataForm.categoryCode, () => categoryTreeData.value],
() => {
const nextType = calcEntrustCenterType();
if (!nextType) return;
const prevType = dataForm.entrustCenterType as any;
if (prevType === nextType) return;
dataForm.entrustCenterType = nextType;
// 品目变化时清理不相关字段
if (nextType === 'other') {
dataForm.hasSupplier = '0';
dataForm.suppliers = '';
dataForm.serviceDirectSelect = '';
dataForm.serviceInviteSelect = '';
dataForm.servicePublicSelect = '';
dataForm.servicePublicSelectAuto = '';
} else if (nextType === 'service_online') {
dataForm.purchaseRequirementTemplate = '';
}
},
{ immediate: true }
);
watch(
() => dataForm.budget,
(budget) => {
const budgetNum = Number(budget) || 0;
if (budgetNum >= 2000 && dataForm.purchaseType === DEPT_PURCHASE_TYPE.DIRECT_PURCHASE) {
dataForm.purchaseType = '';
}
}
);
// 判断是否自动选择网上商城采购方式5万<=金额<30万服务类目特殊服务类目
const isAutoSelectPurchaseType = computed(() => {
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return (
budget >= BUDGET_DEPT_PURCHASE_THRESHOLD.value &&
budget < BUDGET_PUBLIC_SELECT_THRESHOLD.value &&
isServiceCategory.value &&
isSpecialServiceCategory.value
);
});
// 判断是否显示自动邀请比选模版5万<=金额<30万服务类目特殊服务类目
const showAutoInviteSelect = computed(() => {
if (!isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return (
budget >= BUDGET_DEPT_PURCHASE_THRESHOLD.value &&
budget < BUDGET_PUBLIC_SELECT_THRESHOLD.value &&
isServiceCategory.value &&
isSpecialServiceCategory.value
);
});
// 判断是否显示学校统一采购的自动邀请比选模版5万<=金额<30万服务类目特殊服务类目
const showAutoInviteSelectSchool = computed(() => {
if (isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD.value && budget < BUDGET_PUBLIC_SELECT_THRESHOLD.value && isSpecialServiceCategory.value;
});
// 判断是否显示自动公开比选模版30万<=金额<100万特殊服务类目isMallService=1、isProjectService=1
const showAutoPublicSelect = computed(() => {
if (isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= BUDGET_PUBLIC_SELECT_THRESHOLD.value && budget < BUDGET_GOV_PURCHASE_THRESHOLD.value && isSpecialServiceCategory.value;
});
// 获取需求文件的 prop 名称(用于表单验证)
const getRequirementFileProp = () => {
if (showAutoInviteSelectSchool.value) {
if (dataForm.hasSupplier === '1') {
return 'serviceInviteSelectSchool';
} else if (dataForm.hasSupplier === '0') {
return 'servicePublicSelectSchoolAuto';
}
} else if (showAutoPublicSelect.value) {
return 'servicePublicSelectSchool';
}
return 'purchaseRequirement';
};
// 判断学校统一采购是否需要自动设置采购方式5万<=金额<30万服务类目特殊服务类目
const isAutoSelectPurchaseTypeUnion = computed(() => {
if (isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD.value && budget < BUDGET_PUBLIC_SELECT_THRESHOLD.value && isSpecialServiceCategory.value;
});
// 监听品目编码、预算金额、采购类型及采购途径变化,自动设置/清空采购方式
watch(
[
() => dataForm.categoryCode,
() => dataForm.budget,
() => isDeptPurchase.value,
() => isFlowEmbed.value,
() => dataForm.purchaseChannel,
() => isPurchaseCenter.value,
],
(newValues, oldValues) => {
// 如果是初始化触发oldValues 为 undefined且是编辑模式dataForm.id 存在),则跳过自动设置
// 这样可以保持从后台加载的采购方式不变
const isInitTrigger = oldValues === undefined || oldValues === null;
const isEditModeWithData = isInitTrigger && dataForm.id;
const oldIsDeptPurchase = oldValues?.[2];
const newIsDeptPurchase = newValues?.[2];
// 从部门自行采购切换到学校统一采购时:清空采购方式(由系统自动设置或采购中心审批时选择)
if (oldIsDeptPurchase === true && newIsDeptPurchase === false) {
dataForm.purchaseType = '';
}
// 部门自行采购 & 采购途径为"委托采购中心采购"且为新增申请阶段:采购方式隐藏且不设置
// 注意:查看模式和编辑模式不清空已有的采购方式
if (isDeptPurchase.value && isEntrustCenterChannel.value && !isFlowEmbed.value && !isViewMode.value && !isEditMode.value) {
dataForm.purchaseType = '';
return;
}
// 部门自行采购 & 采购途径为自行采购 & 特殊服务类目:固定网上商城(无论金额区间)
// 编辑模式下加载数据时保持原有值,用户手动切换时仍自动更新
if (isDeptSelfMallLocked.value && !isEditModeWithData) {
const onlineMallOption = purchaseTypeDeptList.value.find((item) => item.value === DEPT_PURCHASE_TYPE.ONLINE_MALL);
if (onlineMallOption && dataForm.purchaseType !== onlineMallOption.value) {
dataForm.purchaseType = onlineMallOption.value;
}
// 已锁定网上商城,不再走后续自动推荐逻辑
return;
}
// 其他部门自行采购 & 采购途径为"自行采购":在金额区间内自动推荐网上商城
// 编辑模式下加载数据时保持原有值,用户手动切换时仍自动更新
if (isAutoSelectPurchaseType.value && isDeptPurchase.value && !isEntrustCenterChannel.value && !isEditModeWithData) {
const onlineMallOption = purchaseTypeDeptList.value.find((item) => item.value === DEPT_PURCHASE_TYPE.ONLINE_MALL);
if (onlineMallOption && dataForm.purchaseType !== onlineMallOption.value) {
dataForm.purchaseType = onlineMallOption.value;
}
}
// 部门自行采购 & 采购途径为”委托采购中心采购” & 采购中心审批节点:默认设置为网上商城
if (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;
}
return;
}
// 学校统一采购审批阶段:自动设置网上商城采购方式
if (isAutoSelectPurchaseTypeUnion.value && !isDeptPurchase.value && isFlowEmbed.value) {
const onlineMallOption = purchaseTypeUnionList.value.find((item) => item.value === UNION_PURCHASE_TYPE.ONLINE_MALL);
if (onlineMallOption && dataForm.purchaseType !== onlineMallOption.value) {
dataForm.purchaseType = onlineMallOption.value;
}
}
},
{ immediate: true }
);
// 学校统一采购:组织采购形式由规则默认选择,随预算与集采变化自动更新
watch(
[() => isDeptPurchase.value],
() => {
if (isDeptPurchase.value) return;
},
{ immediate: true }
);
// 下载模版:统一走后端接口,按原始文件下载(避免前端静态资源被当成 HTML 返回)
const downloadTemplate = async (type: string) => {
// 从后台模版列表获取模版名称templateName 包含原始文件扩展名)
const backendTemplate = templateList.value.find((t: any) => t.templateType === type);
if (!backendTemplate?.templateName) {
useMessage().error('模版不存在或未维护模版文件,请联系管理员');
return;
}
try {
await other.downBlobFile(`/purchase/purchasingtemplate/download?type=${encodeURIComponent(type)}`, {}, backendTemplate.templateName);
useMessage().success('模版下载成功');
} catch (err) {
useMessage().error('模版下载失败,请联系管理员维护模版文件');
}
};
// 获取模版名称(用于按钮显示,优先从后台获取)
const getTemplateName = (type: string): string => {
const backendTemplate = templateList.value.find((t: any) => t.templateType === type);
if (backendTemplate?.templateTitle) {
return backendTemplate.templateTitle.replace(/\.(doc|docx)$/i, '');
}
// 后备名称
const fallbackNames: Record<string, string> = {
business_negotiation: '商务洽谈表',
market_purchase_minutes: '部门自行采购市场采购纪要',
inquiry: '部门采购询价模版',
direct_select: '服务商城项目需求模板(直选)',
public_select: '服务商城项目需求模板(公开比选)',
invite_select: '服务商城项目需求模板(邀请比选)',
purchase_requirement: '采购需求填报模板',
import_application: '进口产品申请及专家论证意见表',
single_source: '单一来源论专家证附件',
feasibility_report: '项目可行性论证报告模板',
gov_pur_int: '政府采购意向申请表',
};
return fallbackNames[type] || '模版';
};
// 获取模版列表
const getTemplateList = async () => {
try {
const res = await listTemplates();
if (res.data && Array.isArray(res.data)) {
templateList.value = res.data;
}
} catch (err) {
console.error('获取模版列表失败', err);
}
};
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' },
],
purchaseChannel: [
{
required: true,
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: [
{
validator: (_rule: any, value: string, callback: (e?: Error) => void) => {
// 学校统一采购 + 采购中心审核:采购方式必填
if (!isDeptPurchase.value && isFlowEmbed.value && isPurchaseCenter.value) {
if (!value || String(value).trim() === '') {
callback(new Error('请选择采购方式'));
return;
}
}
// 学校统一采购 + 申请阶段:不校验采购方式(由审批环节采购中心补充)
if (!isDeptPurchase.value) {
callback();
return;
}
// 部门自行采购 + 委托采购中心采购 + 采购中心审核:采购方式必填
if (isDeptPurchase.value && isEntrustCenterChannel.value && isFlowEmbed.value && isPurchaseCenter.value) {
if (!value || String(value).trim() === '') {
callback(new Error('请选择采购方式'));
return;
}
}
// 部门自行采购 + 委托采购中心采购 + 申请阶段:不校验(由审核环节选择)
if (isEntrustCenterChannel.value && !isFlowEmbed.value) {
callback();
return;
}
// 部门自行采购 + 自行采购:必填
if (isDeptPurchase.value && !isEntrustCenterChannel.value && (!value || String(value).trim() === '')) {
callback(new Error('请选择采购方式'));
return;
}
callback();
},
trigger: 'change',
},
],
// 学校统一采购 + 采购中心审核:采购形式必填
purchaseSchool: [
{
validator: (_rule: any, value: string, callback: (e?: Error) => void) => {
if (!isDeptPurchase.value && isFlowEmbed.value && isPurchaseCenter.value) {
if (!value || String(value).trim() === '') {
callback(new Error('请选择组织采购形式'));
return;
}
}
callback();
},
trigger: 'change',
},
],
// 学校统一采购时必填
deptClassifyUserId: [
{
validator: (_rule: any, value: string, callback: (e?: Error) => void) => {
if (!isDeptPurchase.value) {
if (!value || String(value).trim() === '') {
callback(new Error('请选择业务分管处室'));
return;
}
}
callback();
},
trigger: 'change',
},
],
schoolLeaderUserId: [
{
validator: (_rule: any, value: string, callback: (e?: Error) => void) => {
if (!isDeptPurchase.value) {
if (!value || String(value).trim() === '') {
callback(new Error('请选择校党委'));
return;
}
}
callback();
},
trigger: 'change',
},
],
schoolBusinessLeaderUserId: [
{
validator: (_rule: any, value: string, callback: (e?: Error) => void) => {
// 仅非特殊情况时校验
if (!isDeptPurchase.value && !isSpecialCase.value) {
if (!value || String(value).trim() === '') {
callback(new Error('请选择业务分管校领导'));
return;
}
}
callback();
},
trigger: 'change',
},
],
purchaseManagerUserId: [
{
validator: (_rule: any, value: string, callback: (e?: Error) => void) => {
// 仅特殊情况时校验
if (!isDeptPurchase.value && isSpecialCase.value) {
if (!value || String(value).trim() === '') {
callback(new Error('请选择采购分管校领导'));
return;
}
}
callback();
},
trigger: 'change',
},
],
negotiationReason: [
{
validator: (_rule: any, value: string, callback: (e?: Error) => void) => {
if (isPurchaseType(DEPT_PURCHASE_TYPE.BUSINESS_NEGOTIATION)) {
if (!value || String(value).trim() === '') {
callback(new Error('采购方式为商务洽谈时,洽谈理由不能为空'));
return;
}
}
callback();
},
trigger: 'blur',
},
],
// 以下字段添加中文验证提示
meetingMinutesSingle: [{ required: true, message: '校党委会议纪要不能为空', trigger: 'change' }],
singleSourceProof: [{ required: true, message: '单一来源论专家证附件不能为空', trigger: 'change' }],
purchaseRequirement: [{ required: true, message: '需求文件不能为空', trigger: 'change' }],
feasibilityReport: [{ required: true, message: '项目可行性论证报告不能为空', trigger: 'change' }],
deptSelfMeetingMinutes: [{ required: true, message: '部门采购会议纪要不能为空', trigger: 'change' }],
importApplication: [{ required: true, message: '进口产品申请及专家论证意见表不能为空', trigger: 'change' }],
});
// 取消
const handleCancel = () => {
// 流程嵌入时由流程页处理返回,不 postMessage
if (isFlowEmbed.value) {
return;
}
if (window.parent !== window) {
window.parent.postMessage(
{
type: 'purchasingrequisition:close',
},
'*'
);
} else {
router.back();
}
};
/** 根据申请单 ID 加载详情并回填表单(供 URL 与流程嵌入共用) */
async function loadDetail(applyId: string | number) {
if (!applyId) return;
try {
const res = await getObj(String(applyId));
const detail = res?.data;
if (detail && typeof detail === 'object') {
Object.assign(dataForm, {
id: detail.id ?? dataForm.id,
projectName: detail.projectName ?? '',
projectType: detail.projectType ?? '',
projectContent: detail.projectContent ?? '',
applyDate: detail.applyDate ?? '',
fundSource: detail.fundSource ?? '',
budget: detail.budget != null ? Number(detail.budget) : null,
isCentralized: detail.isCentralized != null ? String(detail.isCentralized) : '',
isSpecial: detail.isSpecial != null ? String(detail.isSpecial) : '',
purchaseMode: detail.purchaseMode != null ? String(detail.purchaseMode) : '',
purchaseSchool: detail.purchaseSchool != null ? String(detail.purchaseSchool) : '',
purchaseType:
detail.purchaseType === DEPT_PURCHASE_TYPE.ENTRUST_CENTER
? ''
: detail.purchaseType != null
? String(detail.purchaseType)
: detail.purchaseTypeUnion != null
? String(detail.purchaseTypeUnion)
: '',
purchaseChannel:
(detail as any).purchaseChannel ?? (detail.purchaseType === DEPT_PURCHASE_TYPE.ENTRUST_CENTER ? PURCHASE_CHANNEL.ENTRUST_CENTER : ''),
purchaseTypeUnion: detail.purchaseTypeUnion != null ? String(detail.purchaseTypeUnion) : '',
categoryCode: detail.categoryCode ?? '',
remark: detail.remark ?? '',
status: detail.status ?? '',
createBy: detail.createBy ?? '',
businessNegotiationTable: detail.businessNegotiationTable ?? '',
marketPurchaseMinutes: detail.marketPurchaseMinutes ?? '',
onlineMallMaterials: detail.onlineMallMaterials ?? '',
inquiryTemplate: detail.inquiryTemplate ?? '',
entrustCenterType: detail.entrustCenterType ?? '',
hasSupplier: detail.hasSupplier != null && detail.hasSupplier !== '' ? detail.hasSupplier : '0',
suppliers: detail.suppliers ?? '',
serviceDirectSelect: detail.serviceDirectSelect ?? '',
servicePublicSelect: detail.servicePublicSelect ?? '',
purchaseRequirementTemplate: detail.purchaseRequirementTemplate ?? '',
serviceInviteSelect: detail.serviceInviteSelect ?? '',
servicePublicSelectAuto: detail.servicePublicSelectAuto ?? '',
purchaseRequirement: detail.purchaseRequirement ?? '',
meetingMinutes: detail.meetingMinutes ?? '',
feasibilityReport: detail.feasibilityReport ?? '',
meetingMinutesUrgent: detail.meetingMinutesUrgent ?? '',
meetingMinutesSingle: detail.meetingMinutesSingle ?? '',
meetingMinutesImport: detail.meetingMinutesImport ?? '',
singleSourceProof: detail.singleSourceProof ?? '',
importApplication: detail.importApplication ?? '',
governmentPurchaseIntent: detail.governmentPurchaseIntent ?? '',
servicePublicSelectSchool: detail.servicePublicSelectSchool ?? '',
serviceInviteSelectSchool: detail.serviceInviteSelectSchool ?? '',
servicePublicSelectSchoolAuto: detail.servicePublicSelectSchoolAuto ?? '',
deptClassifyUserId: detail.deptClassifyUserId ?? '',
deptClassifyName: detail.deptClassifyName ?? '',
schoolLeaderUserId: detail.schoolLeaderUserId ?? '',
schoolLeaderName: detail.schoolLeaderName ?? '',
schoolBusinessLeaderUserId: detail.schoolBusinessLeaderUserId ?? '',
schoolBusinessLeaderName: detail.schoolBusinessLeaderName ?? '',
purchaseManagerUserId: detail.purchaseManagerUserId ?? '',
purchaseManagerName: detail.purchaseManagerName ?? '',
otherMaterials: detail.otherMaterials ?? '',
implementType: detail.implementType ?? '',
fileFlowInstId: detail.fileFlowInstId ?? '',
fileFlowStatus: detail.fileFlowStatus ?? '',
agentId: detail.agentId ?? '',
agentName: detail.agentName ?? '',
representorName: detail.representorName ?? '',
representorType: detail.representorType ?? '',
negotiationReason: detail.negotiationReason ?? '',
});
setCategoryCodePath();
try {
const fileRes = await getApplyFiles(String(applyId));
const fileList: { id: string; fileType: string; fileTitle?: string }[] = fileRes?.data ?? [];
if (Array.isArray(fileList) && fileList.length > 0) {
const byType: Record<string, { id: string; fileTitle?: string }[]> = {};
fileList.forEach((f: any) => {
const t = String(f.fileType || '');
if (!byType[t]) byType[t] = [];
byType[t].push({ id: f.id, fileTitle: f.fileTitle });
});
Object.entries(byType).forEach(([fileType, files]) => {
const fields = FILE_TYPE_TO_FIELDS[fileType];
if (!fields || fields.length === 0) return;
const fileItems = files.map((f) => ({ id: f.id, name: f.fileTitle || '附件' }));
if (fields.length === 1) {
(dataForm as any)[fields[0]] = fileItems;
} else {
fields.forEach((field) => {
(dataForm as any)[field] = fileItems.length ? [...fileItems] : '';
});
}
});
// 查看时展示实施采购的招标文件列表type=130
const purchaseFiles = fileList
.filter((f: any) => String(f.fileType) === '130')
.map((f: any) => ({
id: f.id,
fileTitle: f.fileTitle || f.file_title || '招标文件',
createTime: f.createTime || f.create_time,
remark: f.remark,
}));
viewImplementPurchaseFiles.value = purchaseFiles;
} else {
viewImplementPurchaseFiles.value = [];
}
} catch (err) {
console.error('加载采购申请附件失败', err);
viewImplementPurchaseFiles.value = [];
}
}
} catch (e) {
console.error('加载采购申请详情失败', e);
useMessage().error('加载详情失败');
}
}
function formatImplementFileTime(t?: string) {
if (!t) return '—';
const d = new Date(t);
return isNaN(d.getTime())
? t
: d.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
});
}
function downloadImplementFile(file: { id?: string; remark?: string; fileTitle?: string }) {
// 优先使用文件ID下载
if (file?.id) {
const url = `/purchase/purchasingfiles/downloadById?fileId=${encodeURIComponent(file.id)}`;
other.downBlobFile(url, {}, file.fileTitle || '招标文件');
return;
}
// 兼容旧数据:使用文件路径下载
if (file?.remark) {
const url = `/purchase/purchasingfiles/download?fileName=${encodeURIComponent(file.remark)}&fileTitle=${encodeURIComponent(
file.fileTitle || '招标文件'
)}`;
other.downBlobFile(url, {}, file.fileTitle || '招标文件');
return;
}
useMessage().warning('无法获取文件信息');
}
/** 流程嵌入时提供给 orderVue.currElTabIsView 的 methods只读/禁用提交) */
const flowMethods = {
disableForm(disabled?: boolean) {
flowFormDisabled.value = !!disabled;
},
disableSubmit() {
flowSubmitDisabled.value = true;
},
enableSubmit() {
flowSubmitDisabled.value = false;
},
};
/** 流程嵌入时采购申请权限根据前端缓存的角色cloud-ui:roleCode判断非采购中心整表只读采购中心仅采购方式/采购形式可编辑;申请人在流程中可编辑 */
function applyPurchaseApplyFormPerm() {
if (!isFlowEmbed.value) return;
const roleCode = Session.getRoleCode() || '';
isPurchaseCenter.value = roleCode === PURCHASE_CENTER_ROLE_CODE;
if (isApplicant.value) {
flowFormDisabled.value = false;
} else {
flowFormDisabled.value = !isPurchaseCenter.value;
}
}
/** 流程嵌入时的“保存”回调:校验后调用 editObj并通知流程已保存 */
async function flowSubmitForm() {
if (loading.value) return;
loading.value = true;
try {
const valid = await formRef.value?.validate().catch(() => false);
if (!valid) {
loading.value = false;
return;
}
const submitData: any = { ...dataForm };
const fileFields = [
'businessNegotiationTable',
'marketPurchaseMinutes',
'onlineMallMaterials',
'inquiryTemplate',
'serviceDirectSelect',
'servicePublicSelect',
'purchaseRequirementTemplate',
'serviceInviteSelect',
'servicePublicSelectAuto',
'deptSelfMeetingMinutes', // 部门会议纪要
'purchaseRequirement',
'meetingMinutes',
'feasibilityReport',
'meetingMinutesUrgent',
'meetingMinutesSingle',
'meetingMinutesImport',
'singleSourceProof',
'importApplication',
'governmentPurchaseIntent',
'servicePublicSelectSchool',
'serviceInviteSelectSchool',
'servicePublicSelectSchoolAuto',
'otherMaterials',
];
const allFileIds: string[] = [];
fileFields.forEach((field) => {
if (submitData[field]) {
allFileIds.push(...getFileIdsArray(submitData[field]));
delete submitData[field];
}
});
if (allFileIds.length > 0) submitData.fileIds = allFileIds;
await editObj(submitData);
useMessage().success('保存成功');
if (props.currJob && props.currElTab?.id) {
orderVue.currElTabIsSave(props.currJob, props.currElTab.id, true, emit);
}
} catch (err: any) {
if (!err?.msg) useMessage().error('保存失败');
} finally {
loading.value = false;
}
}
// 获取品目树形数据
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 =
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,
}))
: [];
} catch (err) {
isCentralizedList.value = [];
}
};
// 获取是否特殊情况字典
const getIsSpecialDict = async () => {
try {
const res = await getDicts('PURCHASE_IS_SPEC');
isSpecialList.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,
}))
: [];
} catch (err) {
isSpecialList.value = [];
}
};
// 获取部门采购方式字典(过滤掉”委托采购中心采购”,由采购途径字段控制)
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: String(item.value || item.dictValue || item.code),
}))
.filter((item: any) => item.value !== DEPT_PURCHASE_TYPE.ENTRUST_CENTER)
: [];
} catch (err) {
purchaseTypeDeptList.value = [];
}
};
// 获取部门采购方式字典(委托采购中心采购时使用)
const getPurchaseTypeDeptDelegationDict = async () => {
try {
const res = await getDicts('PURCHASE_TYPE_DEPT_DELEGATION');
purchaseTypeDeptDelegationList.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,
}))
: [];
} catch (err) {
purchaseTypeDeptDelegationList.value = [];
}
};
/** 部门采购方式下拉选项:根据采购途径动态切换字典,直接采购仅预算<2000时可选 */
const purchaseTypeDeptOptions = computed(() => {
if (isEntrustCenterChannel.value) {
return purchaseTypeDeptDelegationList.value;
}
const budget = Number(dataForm.budget) || 0;
return purchaseTypeDeptList.value.map((item: any) => ({
...item,
disabled: item.value === DEPT_PURCHASE_TYPE.DIRECT_PURCHASE && budget >= 2000,
}));
});
// 获取学校采购形式字典
const getPurchaseModeSchoolDict = async () => {
try {
const res = await getDicts('PURCHASE_MODE_SCHOOL');
purchaseModeSchoolList.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,
}))
: [];
} catch (err) {
purchaseModeSchoolList.value = [];
}
};
// 获取学校统一采购方式字典
const getPurchaseTypeUnionDict = async () => {
try {
const res = await getDicts('PURCHASE_TYPE_UNION');
purchaseTypeUnionList.value =
res.data && Array.isArray(res.data)
? res.data.map((item: any) => ({
id: item.id,
label: item.label || item.dictLabel || item.name,
// 确保 value 为字符串类型,避免 el-select 无法匹配
value: String(item.value || item.dictValue || item.code),
}))
: [];
} catch (err) {
purchaseTypeUnionList.value = [];
}
};
// 获取业务分管处室列表
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,
}));
businessDeptList.value.push({
id: '0',
deptId: '0',
deptName: '无',
userId: '0',
name: '无',
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 getPurchasingManagerListData = async () => {
try {
const res = await getPurchasingManagerList({ records: 1000 }); // 获取所有数据
if (res.data && res.data.records) {
purchasingManagerList.value = res.data.records.map((item: any) => ({
id: item.id,
userId: item.userId,
name: item.name,
username: item.username,
deptName: item.deptName,
}));
}
} catch (err) {
console.error('获取采购分管列表失败:', err);
purchasingManagerList.value = [];
}
};
// 获取业务分管校领导列表
const getBusinessLeaderListData = async () => {
try {
const res = await getBusinessLeaderList({ size: 1000 }); // 获取所有数据
if (res.data && res.data.records) {
businessLeaderList.value = res.data.records.map((item: any) => ({
id: item.id,
userId: item.userId,
name: item.name,
username: item.username,
}));
}
} catch (err) {
console.error('获取业务分管校领导列表失败:', err);
businessLeaderList.value = [];
}
};
// 处理业务分管处室选择变化
const handleBusinessDeptChange = (value: string) => {
if (value) {
const selected = businessDeptList.value.find((item) => item.userId === value);
if (selected) {
dataForm.deptClassifyUserId = selected.userId;
dataForm.deptClassifyName = selected.deptName || '';
}
} else {
dataForm.deptClassifyUserId = '';
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 = '';
}
};
// 处理采购分管选择变化
const handlePurchaseManagerChange = (value: string) => {
if (value) {
const selected = purchasingManagerList.value.find((item) => item.userId === value);
if (selected) {
dataForm.purchaseManagerUserId = selected.userId;
dataForm.purchaseManagerName = selected.name || '';
}
} else {
dataForm.purchaseManagerUserId = '';
dataForm.purchaseManagerName = '';
}
};
// 处理业务分管校领导选择变化
const handleBusinessLeaderChange = (value: string) => {
if (value) {
const selected = businessLeaderList.value.find((item) => item.userId === value);
if (selected) {
dataForm.schoolBusinessLeaderUserId = selected.userId;
dataForm.schoolBusinessLeaderName = selected.name || '';
}
} else {
dataForm.schoolBusinessLeaderUserId = '';
dataForm.schoolBusinessLeaderName = '';
}
};
// 处理文件ID字符串或对象数组转ID数组
// 支持:逗号分隔的字符串、字符串数组、{ id, name? }[](编辑回填时的格式)
const getFileIdsArray = (fileIds: string | string[] | { id?: string; name?: string }[]): string[] => {
if (!fileIds) return [];
if (Array.isArray(fileIds)) {
return fileIds
.map((item: any) => {
if (item && typeof item === 'object' && item.id) return String(item.id).trim();
if (typeof item === 'string') return item.trim();
return '';
})
.filter(Boolean);
}
const items = String(fileIds)
.split(',')
.filter((item: string) => item.trim());
const ids: string[] = [];
items.forEach((item) => {
const trimmed = item.trim();
// 首先检查是否是直接的ID格式32位十六进制字符串
if (/^[a-f0-9]{32}$/i.test(trimmed)) {
ids.push(trimmed);
return;
}
// 如果不是ID格式尝试从URL中提取id参数
try {
const urlObj = new URL(trimmed, window.location.origin);
// 优先从URL参数中获取id
let id = urlObj.searchParams.get('id');
// 如果没有id参数尝试从路径中提取可能是直接的文件ID
if (!id) {
const pathParts = urlObj.pathname.split('/').filter((p) => p);
const lastPart = pathParts[pathParts.length - 1];
if (lastPart && /^[a-f0-9]{32}$/i.test(lastPart)) {
id = lastPart;
}
}
if (id) {
ids.push(id);
} else {
// 如果无法提取ID使用原始字符串可能是URL
// 但这种情况不应该发生因为上传接口应该返回id
console.warn('无法从URL中提取文件ID:', trimmed);
ids.push(trimmed);
}
} catch {
// URL解析失败如果原始字符串是ID格式则使用否则忽略
if (/^[a-f0-9]{32}$/i.test(trimmed)) {
ids.push(trimmed);
} else {
console.warn('无法解析文件标识:', trimmed);
}
}
});
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,
};
// 学校统一采购申请阶段:采购方式由审批环节补充,提交时不写入
if (!isFlowEmbed.value && !isDeptPurchase.value) {
submitData.purchaseType = '';
}
// 处理所有文件字段 - 收集所有文件ID到fileIds数组中
const fileFields = [
'businessNegotiationTable',
'marketPurchaseMinutes',
'onlineMallMaterials',
'inquiryTemplate',
'serviceDirectSelect',
'servicePublicSelect',
'purchaseRequirementTemplate',
'serviceInviteSelect',
'servicePublicSelectAuto',
'deptSelfMeetingMinutes', // 部门会议纪要
'purchaseRequirement',
'meetingMinutes',
'feasibilityReport',
'meetingMinutesUrgent',
'meetingMinutesSingle',
'meetingMinutesImport',
'singleSourceProof',
'importApplication',
'governmentPurchaseIntent',
'servicePublicSelectSchool',
'serviceInviteSelectSchool',
'servicePublicSelectSchoolAuto',
'otherMaterials',
];
// 收集所有文件ID到一个数组中
const allFileIds: string[] = [];
fileFields.forEach((field) => {
if (submitData[field]) {
const ids = getFileIdsArray(submitData[field]);
console.log(`字段 ${field} 的文件ID:`, ids);
// 收集到总数组中
allFileIds.push(...ids);
// 清空原字段,不再单独传递
delete submitData[field];
}
});
// 将所有文件ID统一放到fileIds字段中
if (allFileIds.length > 0) {
submitData.fileIds = allFileIds;
console.log('所有文件ID (fileIds):', allFileIds);
}
console.log('提交数据:', submitData);
if (dataForm.id) {
await editObj(submitData);
useMessage().success('保存成功');
} else {
await addObj(submitData);
useMessage().success('提交成功');
}
// 如果是在 iframe 中,向父窗口发送消息
if (window.parent !== window) {
window.parent.postMessage(
{
type: 'purchasingrequisition:submitSuccess',
},
'*'
);
} else {
router.push('/purchase/purchasingrequisition');
}
} catch (err: any) {
if (!err?._messageShown && !err?.msg) {
useMessage().error('提交失败');
}
} finally {
loading.value = false;
}
};
// 暂存(跳过表单校验,允许部分填写)
const handleTempStore = async () => {
if (loading.value) return;
loading.value = true;
try {
// 暂存模式不进行表单校验,允许用户部分填写后保存
// 提交时才会执行完整的校验规则
const submitData: any = {
...dataForm,
};
// 学校统一采购申请阶段:采购方式由审批环节补充,暂存时不写入
if (!isFlowEmbed.value && !isDeptPurchase.value) {
submitData.purchaseType = '';
}
// 处理所有文件字段 - 收集所有文件ID到fileIds数组中
const fileFields = [
'businessNegotiationTable',
'marketPurchaseMinutes',
'onlineMallMaterials',
'inquiryTemplate',
'serviceDirectSelect',
'servicePublicSelect',
'purchaseRequirementTemplate',
'serviceInviteSelect',
'servicePublicSelectAuto',
'deptSelfMeetingMinutes', // 部门会议纪要
'purchaseRequirement',
'meetingMinutes',
'feasibilityReport',
'meetingMinutesUrgent',
'meetingMinutesSingle',
'meetingMinutesImport',
'singleSourceProof',
'importApplication',
'governmentPurchaseIntent',
'servicePublicSelectSchool',
'serviceInviteSelectSchool',
'servicePublicSelectSchoolAuto',
'otherMaterials',
];
// 收集所有文件ID到一个数组中
const allFileIds: string[] = [];
fileFields.forEach((field) => {
if (submitData[field]) {
const ids = getFileIdsArray(submitData[field]);
// 收集到总数组中
allFileIds.push(...ids);
// 清空原字段,不再单独传递
delete submitData[field];
}
});
// 将所有文件ID统一放到fileIds字段中
if (allFileIds.length > 0) {
submitData.fileIds = allFileIds;
}
await tempStore(submitData);
useMessage().success('暂存成功');
// 流程嵌入时不关闭、不跳转
if (!isFlowEmbed.value) {
if (window.parent !== window) {
window.parent.postMessage(
{
type: 'purchasingrequisition:submitSuccess',
},
'*'
);
} else {
router.push('/purchase/purchasingrequisition');
}
}
} catch (err: any) {
if (!err?._messageShown && !err?.msg) {
useMessage().error('暂存失败');
}
} finally {
loading.value = false;
}
};
// 提交(暂存后启动流程)
const handleSubmitFlow = async () => {
if (loading.value) return;
try {
await useMessageBox().confirm('确定要提交该采购申请并启动流程吗?');
} catch {
return;
}
loading.value = true;
try {
const valid = await formRef.value?.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
const submitData: any = {
...dataForm,
};
// 学校统一采购申请阶段:采购方式由审批环节补充,提交时不写入
if (!isFlowEmbed.value && !isDeptPurchase.value) {
submitData.purchaseType = '';
}
// 处理所有文件字段
const fileFields = [
'businessNegotiationTable',
'marketPurchaseMinutes',
'onlineMallMaterials',
'inquiryTemplate',
'serviceDirectSelect',
'servicePublicSelect',
'purchaseRequirementTemplate',
'serviceInviteSelect',
'servicePublicSelectAuto',
'deptSelfMeetingMinutes',
'purchaseRequirement',
'meetingMinutes',
'feasibilityReport',
'meetingMinutesUrgent',
'meetingMinutesSingle',
'meetingMinutesImport',
'singleSourceProof',
'importApplication',
'governmentPurchaseIntent',
'servicePublicSelectSchool',
'serviceInviteSelectSchool',
'servicePublicSelectSchoolAuto',
'otherMaterials',
];
const allFileIds: string[] = [];
fileFields.forEach((field) => {
if (submitData[field]) {
const ids = getFileIdsArray(submitData[field]);
allFileIds.push(...ids);
delete submitData[field];
}
});
if (allFileIds.length > 0) {
submitData.fileIds = allFileIds;
}
// 先暂存
const tempResult = await tempStore(submitData);
const applyId = tempResult?.data?.id || submitData.id || dataForm.id;
// 再提交
await submitObj({ id: applyId });
useMessage().success('提交成功');
if (window.parent !== window) {
window.parent.postMessage(
{
type: 'purchasingrequisition:submitSuccess',
},
'*'
);
} else {
router.push('/purchase/purchasingrequisition');
}
} catch (err: any) {
if (!err?._messageShown && !err?.msg) {
useMessage().error('提交失败');
}
} 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 }
);
// 流程嵌入:切换工单时重新加载该 tab 对应的申请单
watch(
() => props.currJob?.id,
async (newVal, oldVal) => {
if (!isFlowEmbed.value || !props.currJob?.orderId) return;
if (newVal !== oldVal) {
await loadDetail(props.currJob.orderId);
}
}
);
// 初始化
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(),
getPurchaseTypeDeptDelegationDict(),
getPurchaseModeSchoolDict(),
getPurchaseTypeUnionDict(),
getBusinessDeptListData(),
getSchoolLeaderListData(),
getPurchasingManagerListData(),
getBusinessLeaderListData(),
getTemplateList(),
]);
// 编辑/查看:从 URL 或流程 currJob.orderId 加载详情
const queryId = effectiveQueryId.value;
if (queryId) {
await loadDetail(queryId);
}
// 流程嵌入:注册 tab 显隐与保存回调,供审批页调用
if (isFlowEmbed.value && props.currJob && props.currElTab?.id) {
orderVue.currElTabIsExist(props.currJob, props.currElTab.id);
await orderVue.currElTabIsView(flowMethods, props.currJob, props.currElTab.id, flowSubmitForm);
applyPurchaseApplyFormPerm();
}
// 新增模式下设置默认值(只有在没有 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;
}
.mb16 {
margin-bottom: 16px;
}
.mb8 {
margin-bottom: 8px;
}
.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: 16px;
}
.implement-info-block {
.view-label {
font-size: 14px;
color: var(--el-text-color-secondary);
margin-bottom: 4px;
}
.view-value {
font-size: 14px;
color: var(--el-text-color-primary);
}
}
/* 紧凑表单样式 */
.compact-form {
:deep(.el-form-item) {
margin-bottom: 16px;
}
:deep(.el-form-item__label) {
padding-right: 12px;
font-size: 14px;
}
:deep(.el-input__inner),
:deep(.el-textarea__inner) {
font-size: 14px;
}
}
.template-note {
margin-top: 5px;
color: var(--el-text-color-secondary);
font-size: 12px;
}
/* 预算金额输入框 + 单位元 */
.budget-yuan-wrap {
display: flex;
align-items: center;
width: 100%;
gap: 8px;
:deep(.el-input-number) {
flex: 1;
}
.budget-unit {
color: var(--el-text-color-regular);
font-size: 14px;
flex-shrink: 0;
}
}
.form-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
padding-top: 16px;
border-top: 1px solid var(--el-border-color-light);
margin-top: 16px;
}
</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>