Files
school-developer/src/views/finance/purchasingrequisition/form.vue
2026-02-01 19:56:07 +08:00

1233 lines
43 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>
<el-dialog
:title="dialogTitle"
v-model="visible"
width="1200px"
:close-on-click-modal="false"
draggable>
<div v-loading="loading">
<!-- 步骤条 -->
<el-steps :active="currentStep" finish-status="success" style="margin-bottom: 30px;">
<el-step title="基本信息" />
<el-step :title="stepTwoTitle" />
</el-steps>
<el-form
ref="formRef"
:model="dataForm"
:rules="dataRules"
label-width="140px">
<!-- 第一步基本信息 -->
<div v-show="currentStep === 0">
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="采购项目名称" prop="projectName">
<el-input
v-model="dataForm.projectName"
placeholder="请输入采购项目名称"
clearable
:disabled="isView" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="填报日期" prop="applyDate">
<el-date-picker
v-model="dataForm.applyDate"
type="date"
placeholder="请选择填报日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
:disabled="isView"
style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="资金来源" prop="fundSource">
<el-select
v-model="dataForm.fundSource"
placeholder="请选择资金来源"
clearable
:disabled="isView"
style="width: 100%">
<el-option
v-for="item in fundSourceList"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="预算金额(元)" prop="budget">
<el-input-number
v-model="dataForm.budget"
:min="0.01"
:precision="2"
placeholder="请输入预算金额"
:disabled="isView"
style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="是否集采" prop="isCentralized">
<el-select
v-model="dataForm.isCentralized"
placeholder="请选择是否集采"
clearable
:disabled="isView"
style="width: 100%">
<el-option
v-for="item in isCentralizedList"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="是否特殊情况" prop="isSpecial">
<el-select
v-model="dataForm.isSpecial"
placeholder="请选择是否特殊情况"
clearable
:disabled="isView"
style="width: 100%">
<el-option
v-for="item in isSpecialList"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="24" class="mb20">
<el-form-item label="品目编码" prop="categoryCode">
<el-tree-select
v-model="dataForm.categoryCode"
:data="categoryTreeData"
:props="{ value: 'code', label: 'name', children: 'children' }"
placeholder="请选择品目编码(需要显示所有的层级)"
clearable
check-strictly
:render-after-expand="false"
:disabled="isView"
style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
</div>
<!-- 第二步采购详情 -->
<div v-show="currentStep === 1">
<!-- 分支一部门自行采购 -->
<div v-if="isDeptPurchase">
<div class="step-title mb20">部门自行采购</div>
<el-form-item label="采购内容" prop="projectContent" class="mb20">
<el-input
v-model="dataForm.projectContent"
type="textarea"
:rows="4"
:maxlength="1000"
show-word-limit
placeholder="请输入采购内容限制1000字"
clearable
:disabled="isView" />
</el-form-item>
<el-form-item label="采购方式" prop="purchaseType" class="mb20">
<el-select
v-model="dataForm.purchaseType"
placeholder="请选择采购方式"
clearable
:disabled="isView || isAutoSelectPurchaseType"
style="width: 100%">
<el-option
v-for="item in purchaseTypeDeptList"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 商务洽谈 -->
<el-form-item
v-if="dataForm.purchaseType === 'business_negotiation'"
label="商务洽谈表"
prop="businessNegotiationTable"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('business_negotiation')"
:disabled="isView"
class="mb10">
下载商务洽谈表模版
</el-button>
<upload-file
v-model="dataForm.businessNegotiationTable"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
<!-- 市场采购 -->
<el-form-item
v-if="dataForm.purchaseType === 'market_purchase'"
label="市场采购纪要"
prop="marketPurchaseMinutes"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('market_purchase_minutes')"
:disabled="isView"
class="mb10">
下载市场采购纪要模版
</el-button>
<upload-file
v-model="dataForm.marketPurchaseMinutes"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
<!-- 网上商城 -->
<el-form-item
v-if="dataForm.purchaseType === 'online_mall'"
label="网上商城采购相关材料"
prop="onlineMallMaterials"
class="mb20">
<upload-file
v-model="dataForm.onlineMallMaterials"
:limit="1"
accept=".zip"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
<div class="template-note mt5">
<el-text type="info" size="small">请上传zip格式的压缩包</el-text>
</div>
</el-form-item>
<!-- 委托采购中心 -->
<template v-if="dataForm.purchaseType === 'entrust_center'">
<el-form-item label="委托采购中心方式" prop="entrustCenterType" class="mb20">
<el-radio-group v-model="dataForm.entrustCenterType" :disabled="isView">
<el-radio label="service_online">服务类网上商城</el-radio>
<el-radio label="other">其他方式</el-radio>
</el-radio-group>
</el-form-item>
<!-- 服务类网上商城 -->
<template v-if="dataForm.entrustCenterType === 'service_online' && isServiceCategory">
<el-form-item label="是否有供应商" prop="hasSupplier" class="mb20">
<el-radio-group v-model="dataForm.hasSupplier" :disabled="isView">
<el-radio label="yes"></el-radio>
<el-radio label="no"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="dataForm.hasSupplier === 'yes'"
label="服务商城项目需求模板(直选)"
prop="serviceDirectSelect"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('direct_select')"
:disabled="isView"
class="mb10">
下载服务商城项目需求模板直选模版
</el-button>
<upload-file
v-model="dataForm.serviceDirectSelect"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
<el-form-item
v-if="dataForm.hasSupplier === 'no'"
label="服务商城项目需求模板(公开比选)"
prop="servicePublicSelect"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('public_select')"
:disabled="isView"
class="mb10">
下载服务商城项目需求模板公开比选模版
</el-button>
<upload-file
v-model="dataForm.servicePublicSelect"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
</template>
<!-- 其他方式货物类 -->
<el-form-item
v-if="dataForm.entrustCenterType === 'other' && isGoodsCategory"
label="采购需求填报模板"
prop="purchaseRequirementTemplate"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('purchase_requirement')"
:disabled="isView"
class="mb10">
下载表1采购需求填报模板模版
</el-button>
<upload-file
v-model="dataForm.purchaseRequirementTemplate"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
</template>
<!-- 特殊规则5<=金额<40万服务类目自动使用邀请比选模版 -->
<template v-if="showAutoInviteSelect">
<el-form-item label="是否有推荐供应商" prop="hasRecommendedSupplier" class="mb20">
<el-radio-group v-model="dataForm.hasRecommendedSupplier" :disabled="isView">
<el-radio label="yes"></el-radio>
<el-radio label="no"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="dataForm.hasRecommendedSupplier === 'yes'"
label="推荐供应商"
prop="recommendedSuppliers"
class="mb20">
<el-input
v-model="dataForm.recommendedSuppliers"
placeholder="请输入三家供应商名称,用逗号分隔"
clearable
:disabled="isView" />
<div class="template-note mt5">
<el-text type="info" size="small">请输入三家供应商名称用逗号分隔</el-text>
</div>
</el-form-item>
<el-form-item
label="服务商城项目需求模板(邀请比选)"
prop="serviceInviteSelect"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('invite_select')"
:disabled="isView"
class="mb10">
下载服务商城项目需求模板邀请比选模版
</el-button>
<upload-file
v-model="dataForm.serviceInviteSelect"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
<el-form-item
v-if="dataForm.hasRecommendedSupplier === 'no'"
label="服务商城项目需求模板(公开比选)"
prop="servicePublicSelectAuto"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('public_select')"
:disabled="isView"
class="mb10">
下载服务商城项目需求模板公开比选模版
</el-button>
<upload-file
v-model="dataForm.servicePublicSelectAuto"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
</template>
</div>
<!-- 分支二学校统一采购 -->
<div v-else>
<div class="step-title mb20">学校统一采购</div>
<el-form-item label="采购形式" prop="purchaseMode" class="mb20">
<el-select
v-model="dataForm.purchaseMode"
placeholder="请选择采购形式"
clearable
:disabled="isView"
style="width: 100%">
<el-option
v-for="item in purchaseModeSchoolList"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="采购方式" prop="purchaseTypeUnion" class="mb20">
<el-select
v-model="dataForm.purchaseTypeUnion"
placeholder="请选择采购方式"
clearable
:disabled="isView"
style="width: 100%">
<el-option
v-for="item in purchaseTypeUnionList"
:key="item.value"
:label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 金额>=30显示可行性论证报告和会议纪要 -->
<template v-if="dataForm.budget && dataForm.budget >= 300000">
<el-form-item
label="项目可行性论证报告"
prop="feasibilityReport"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('feasibility_report')"
:disabled="isView"
class="mb10">
下载项目可行性论证报告模板.doc
</el-button>
<upload-file
v-model="dataForm.feasibilityReport"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
<el-form-item label="会议纪要" prop="meetingMinutes" class="mb20">
<upload-file
v-model="dataForm.meetingMinutes"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
</template>
<!-- 特殊情况单一来源 -->
<template v-if="dataForm.isSpecial === '2'">
<el-form-item
label="单一来源论专家证附件"
prop="singleSourceProof"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('single_source')"
:disabled="isView"
class="mb10">
下载单一来源论专家证附件.docx
</el-button>
<upload-file
v-model="dataForm.singleSourceProof"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
<el-form-item label="会议纪要" prop="meetingMinutesSingle" class="mb20">
<upload-file
v-model="dataForm.meetingMinutesSingle"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
</template>
<!-- 特殊情况进口 -->
<template v-if="dataForm.isSpecial === '3'">
<el-form-item
label="进口产品申请及专家论证意见表"
prop="importApplication"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('import_application')"
:disabled="isView"
class="mb10">
下载进口产品申请及专家论证意见表.doc
</el-button>
<upload-file
v-model="dataForm.importApplication"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
<el-form-item label="会议纪要" prop="meetingMinutesImport" class="mb20">
<upload-file
v-model="dataForm.meetingMinutesImport"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
</template>
<!-- 需求文件默认 -->
<el-form-item label="需求文件" prop="purchaseRequirement" class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('purchase_requirement')"
:disabled="isView"
class="mb10">
下载采购需求填报模板模版
</el-button>
<upload-file
v-model="dataForm.purchaseRequirement"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
<!-- 特殊规则40<=金额<100万服务类目自动使用公开比选需求模版 -->
<el-form-item
v-if="showAutoPublicSelect"
label="服务商城项目需求模板(公开比选)"
prop="servicePublicSelectSchool"
class="mb20">
<el-button
type="primary"
link
icon="Download"
@click="downloadTemplate('public_select')"
:disabled="isView"
class="mb10">
下载服务商城项目需求模板公开比选模版
</el-button>
<upload-file
v-model="dataForm.servicePublicSelectSchool"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
<!-- 金额>=100政府采购意向表 -->
<el-form-item
v-if="dataForm.budget && dataForm.budget >= 1000000"
label="政府采购意向申请表"
prop="governmentPurchaseIntent"
class="mb20">
<upload-file
v-model="dataForm.governmentPurchaseIntent"
:limit="5"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
</div>
</div>
<el-form-item label="备注" prop="remark" v-if="currentStep === 1">
<el-input
v-model="dataForm.remark"
type="textarea"
:rows="3"
placeholder="请输入备注"
clearable
:disabled="isView" />
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">{{ isView ? '关闭' : '取消' }}</el-button>
<template v-if="!isView">
<el-button v-if="currentStep > 0" @click="prevStep">上一步</el-button>
<el-button
v-if="currentStep < 1"
type="primary"
@click="nextStep"
:disabled="loading">
下一步
</el-button>
<el-button
v-if="currentStep === 1 && dataForm.id && dataForm.status === '-1'"
type="warning"
@click="handleTempStore"
:disabled="loading">
暂存
</el-button>
<el-button
v-if="currentStep === 1"
type="primary"
@click="handleSubmit"
:disabled="loading">
{{ dataForm.id ? '保存' : '提交' }}
</el-button>
</template>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="PurchasingRequisitionForm">
import { reactive, ref, nextTick, computed, watch } from 'vue'
import { getObj, addObj, editObj, tempStore, submitObj } from '/@/api/finance/purchasingrequisition';
import { getTree } from '/@/api/finance/purchasingcategory';
import { getDicts } from '/@/api/admin/dict';
import { useMessage } from '/@/hooks/message';
import UploadFile from '/@/components/Upload/index.vue';
import other from '/@/utils/other';
// 定义子组件向父组件传值/事件
const emit = defineEmits(['refresh']);
// 定义变量内容
const formRef = ref();
const currentStep = ref(0);
const dataForm = reactive({
id: '',
projectName: '',
projectType: '',
projectContent: '',
applyDate: '',
fundSource: '',
budget: null as number | null,
isCentralized: '',
isSpecial: '',
purchaseMode: '',
purchaseType: '',
purchaseTypeUnion: '',
categoryCode: '',
remark: '',
status: '',
// 部门自行采购字段
businessNegotiationTable: '',
marketPurchaseMinutes: '',
onlineMallMaterials: '',
entrustCenterType: '',
hasSupplier: '',
serviceDirectSelect: '',
servicePublicSelect: '',
purchaseRequirementTemplate: '',
hasRecommendedSupplier: '',
recommendedSuppliers: '',
serviceInviteSelect: '',
servicePublicSelectAuto: '',
// 学校统一采购字段
purchaseRequirement: '',
meetingMinutes: '',
feasibilityReport: '',
meetingMinutesSingle: '',
meetingMinutesImport: '',
singleSourceProof: '',
importApplication: '',
governmentPurchaseIntent: '',
servicePublicSelectSchool: '',
});
const categoryTreeData = ref<any[]>([]);
const selectedCategory = ref<any>(null);
const fundSourceList = ref<any[]>([]);
const isCentralizedList = ref<any[]>([]);
const isSpecialList = ref<any[]>([]);
const purchaseTypeDeptList = ref<any[]>([]);
const purchaseModeSchoolList = ref<any[]>([]);
const purchaseTypeUnionList = ref<any[]>([]);
const visible = ref(false);
const loading = ref(false);
const dialogType = ref<'add' | 'edit' | 'view'>('add');
const isView = computed(() => dialogType.value === 'view');
const dialogTitle = computed(() => {
if (dialogType.value === 'view') return '查看采购申请';
if (dialogType.value === 'edit') return '编辑采购申请';
return '新增采购申请';
});
// 判断是否为部门自行采购(是否集采=0
const isDeptPurchase = computed(() => {
return dataForm.isCentralized === '0';
});
// 第二步标题
const stepTwoTitle = computed(() => {
return isDeptPurchase.value ? '部门自行采购' : '学校统一采购';
});
// 从品目编码中获取项目类型和属性
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(() => {
const category = getCategoryInfo();
if (!category) return false;
// 假设category有type字段C表示服务类
return category.type === 'C' || category.projectType === 'C';
});
// 判断是否为货物类
const isGoodsCategory = computed(() => {
const category = getCategoryInfo();
if (!category) return false;
return category.type === 'A' || category.projectType === 'A';
});
// 判断是否为特殊服务类目isMallService=1、isProjectService=1
const isSpecialServiceCategory = computed(() => {
const category = getCategoryInfo();
if (!category) return false;
return category.isMallService === 1 && category.isProjectService === 1;
});
// 判断是否自动选择网上商城采购方式5万<=金额<40万服务类目特殊服务类目
const isAutoSelectPurchaseType = computed(() => {
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= 50000 && budget < 400000 && isServiceCategory.value && isSpecialServiceCategory.value;
});
// 判断是否显示自动邀请比选模版5万<=金额<40万服务类目特殊服务类目
const showAutoInviteSelect = computed(() => {
if (!isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= 50000 && budget < 400000 && isServiceCategory.value && isSpecialServiceCategory.value;
});
// 判断是否显示自动公开比选模版40万<=金额<100万服务类目特殊服务类目
const showAutoPublicSelect = computed(() => {
if (isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= 400000 && budget < 1000000 && isServiceCategory.value && isSpecialServiceCategory.value;
});
// 监听品目编码变化,自动设置采购方式
watch(() => dataForm.categoryCode, (newVal) => {
if (isAutoSelectPurchaseType.value && !dataForm.purchaseType) {
// 自动选择网上商城
const onlineMallOption = purchaseTypeDeptList.value.find(item => item.value === 'online_mall');
if (onlineMallOption) {
dataForm.purchaseType = 'online_mall';
}
}
}, { immediate: true });
// 下载模版
const downloadTemplate = async (type: string) => {
const templateMap: Record<string, { fileName: string, displayName: string }> = {
'business_negotiation': { fileName: '商务洽谈表.xlsx', displayName: '商务洽谈表.xlsx' },
'market_purchase_minutes': { fileName: '市场采购纪要.xlsx', displayName: '市场采购纪要.xlsx' },
'direct_select': { fileName: '服务商城项目需求模板(直选).doc', displayName: '服务商城项目需求模板(直选).doc' },
'public_select': { fileName: '服务商城项目需求模板(公开比选).doc', displayName: '服务商城项目需求模板(公开比选).doc' },
'invite_select': { fileName: '服务商城项目需求模板(邀请比选).doc', displayName: '服务商城项目需求模板(邀请比选).doc' },
'purchase_requirement': { fileName: '表1采购需求填报模板.xlsx', displayName: '采购需求填报模板.xlsx' },
'import_application': { fileName: '附件1进口产品申请及专家论证意见表.doc', displayName: '进口产品申请及专家论证意见表.doc' },
'single_source': { fileName: '表7单一来源论专家证附件.docx', displayName: '单一来源论专家证附件.docx' },
'feasibility_report': { fileName: '表6项目可行性论证报告模板.doc', displayName: '项目可行性论证报告模板.doc' },
};
const template = templateMap[type];
if (!template) {
useMessage().error('模版不存在');
return;
}
try {
const fileUrl = new URL(`../../assets/file/${template.fileName}`, import.meta.url).href;
const response = await fetch(fileUrl);
if (!response.ok) {
throw new Error('文件下载失败');
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = template.displayName;
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(link);
useMessage().success('模版下载成功');
} catch (error) {
try {
await other.downBlobFile(
`/purchase/purchasingfiles/downloadTemplate?type=${type}`,
{},
template.displayName
);
useMessage().success('模版下载成功');
} catch (err) {
useMessage().error('模版下载失败,请先维护模版文件');
}
}
};
const dataRules = reactive({
projectName: [
{ required: true, message: '采购项目名称不能为空', trigger: 'blur' }
],
applyDate: [
{ required: true, message: '填报日期不能为空', trigger: 'change' }
],
fundSource: [
{ required: true, message: '资金来源不能为空', trigger: 'change' }
],
budget: [
{ required: true, message: '预算金额不能为空', trigger: 'blur' },
{ type: 'number', min: 0.01, message: '预算金额必须大于0.01', trigger: 'blur' }
],
isCentralized: [
{ required: true, message: '请选择是否集采', trigger: 'change' }
],
isSpecial: [
{ required: true, message: '请选择是否特殊情况', trigger: 'change' }
],
categoryCode: [
{ required: true, message: '品目编码不能为空', trigger: 'change' }
],
projectContent: [
{ required: true, message: '采购内容不能为空', trigger: 'blur' },
{ max: 1000, message: '采购内容不能超过1000字', trigger: 'blur' }
],
purchaseType: [
{ required: true, message: '请选择采购方式', trigger: 'change' }
],
});
// 下一步
const nextStep = async () => {
try {
const fieldsToValidate = ['projectName', 'applyDate', 'fundSource', 'budget', 'isCentralized', 'isSpecial', 'categoryCode'];
await formRef.value?.validateField(fieldsToValidate);
currentStep.value = 1;
} catch (error) {
useMessage().warning('请完善第一步信息');
}
};
// 上一步
const prevStep = () => {
currentStep.value = 0;
};
// 打开弹窗
const openDialog = async (type: 'add' | 'edit' | 'view', rowData?: any) => {
dialogType.value = type;
visible.value = true;
currentStep.value = 0;
// 重置表单
Object.assign(dataForm, {
id: '',
projectName: '',
projectType: '',
projectContent: '',
applyDate: '',
fundSource: '',
budget: null,
isCentralized: '',
isSpecial: '',
purchaseMode: '',
purchaseType: '',
purchaseTypeUnion: '',
categoryCode: '',
remark: '',
status: '',
businessNegotiationTable: '',
marketPurchaseMinutes: '',
onlineMallMaterials: '',
entrustCenterType: '',
hasSupplier: '',
serviceDirectSelect: '',
servicePublicSelect: '',
purchaseRequirementTemplate: '',
hasRecommendedSupplier: '',
recommendedSuppliers: '',
serviceInviteSelect: '',
servicePublicSelectAuto: '',
purchaseRequirement: '',
meetingMinutes: '',
feasibilityReport: '',
meetingMinutesSingle: '',
meetingMinutesImport: '',
singleSourceProof: '',
importApplication: '',
governmentPurchaseIntent: '',
servicePublicSelectSchool: '',
});
await Promise.all([
getCategoryTreeData(),
getFundSourceDict(),
getIsCentralizedDict(),
getIsSpecialDict(),
getPurchaseTypeDeptDict(),
getPurchaseModeSchoolDict(),
getPurchaseTypeUnionDict(),
]);
nextTick(() => {
formRef.value?.resetFields();
if (type === 'edit' || type === 'view') {
loading.value = true;
getObj(rowData.id)
.then((res) => {
Object.assign(dataForm, res.data || {});
if (type === 'edit' || type === 'view') {
currentStep.value = 1;
}
})
.catch((err: any) => {
useMessage().error(err.msg || '获取详情失败');
})
.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) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code
}));
} else {
// 默认值
fundSourceList.value = [
{ label: '切块经费', value: '0' },
{ label: '设备购置费', value: '1' },
{ label: '专项经费', value: '2' },
{ label: '代办费', value: '3' },
{ label: '培训经费', value: '4' },
{ label: '日常公用经费', value: '5' },
{ label: '技能大赛经费', value: '6' },
{ label: '基本建设资金', value: '7' },
{ label: '暂存款', value: '8' },
{ label: '会议费', value: '9' },
];
}
} catch (err) {
fundSourceList.value = [
{ label: '切块经费', value: '0' },
{ label: '设备购置费', value: '1' },
{ label: '专项经费', value: '2' },
{ label: '代办费', value: '3' },
{ label: '培训经费', value: '4' },
{ label: '日常公用经费', value: '5' },
{ label: '技能大赛经费', value: '6' },
{ label: '基本建设资金', value: '7' },
{ label: '暂存款', value: '8' },
{ label: '会议费', value: '9' },
];
}
};
// 获取是否集采字典
const getIsCentralizedDict = async () => {
try {
const res = await getDicts('PURCHASE_IS_CEN');
isCentralizedList.value = [];
if (res.data && Array.isArray(res.data)) {
isCentralizedList.value = res.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code
}));
} else {
isCentralizedList.value = [
{ label: '否', value: '0' },
{ label: '政府集中采购', value: '1' },
{ label: '学校集中采购', value: '2' }
];
}
} catch (err) {
isCentralizedList.value = [
{ label: '否', value: '0' },
{ label: '政府集中采购', value: '1' },
{ label: '学校集中采购', value: '2' }
];
}
};
// 获取是否特殊情况字典
const getIsSpecialDict = async () => {
try {
const res = await getDicts('PURCHASE_IS_SPECIAL');
isSpecialList.value = [];
if (res.data && Array.isArray(res.data)) {
isSpecialList.value = res.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code
}));
} else {
isSpecialList.value = [
{ label: '否', value: '0' },
{ label: '紧急', value: '1' },
{ label: '单一', value: '2' },
{ label: '进口', value: '3' }
];
}
} catch (err) {
isSpecialList.value = [
{ label: '否', value: '0' },
{ label: '紧急', value: '1' },
{ label: '单一', value: '2' },
{ label: '进口', value: '3' }
];
}
};
// 获取部门采购方式字典
const getPurchaseTypeDeptDict = async () => {
try {
const res = await getDicts('PURCHASE_TYPE_DEPT');
purchaseTypeDeptList.value = [];
if (res.data && Array.isArray(res.data)) {
purchaseTypeDeptList.value = res.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code
}));
}
} catch (err) {
console.error('获取部门采购方式字典失败', err);
}
};
// 获取学校采购形式字典
const getPurchaseModeSchoolDict = async () => {
try {
const res = await getDicts('PURCHASE_MODE_SCHOOL');
purchaseModeSchoolList.value = [];
if (res.data && Array.isArray(res.data)) {
purchaseModeSchoolList.value = res.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code
}));
} else {
purchaseModeSchoolList.value = [
{ label: '政府采购', value: '1' },
{ label: '学校自主采购', value: '2' }
];
}
} catch (err) {
purchaseModeSchoolList.value = [
{ label: '政府采购', value: '1' },
{ label: '学校自主采购', value: '2' }
];
}
};
// 获取学校统一采购方式字典
const getPurchaseTypeUnionDict = async () => {
try {
const res = await getDicts('PURCHASE_TYPE_UNION');
purchaseTypeUnionList.value = [];
if (res.data && Array.isArray(res.data)) {
purchaseTypeUnionList.value = res.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code
}));
}
} catch (err) {
console.error('获取学校统一采购方式字典失败', err);
}
};
// 处理文件ID字符串转数组
const getFileIdsArray = (fileIds: string | string[]): string[] => {
if (!fileIds) return [];
if (Array.isArray(fileIds)) return fileIds;
const urls = fileIds.split(',').filter(url => url.trim());
const ids: string[] = [];
urls.forEach(url => {
try {
const urlObj = new URL(url, window.location.origin);
const id = urlObj.searchParams.get('id') || urlObj.searchParams.get('fileName');
if (id) {
ids.push(id);
} else {
const pathParts = urlObj.pathname.split('/');
const lastPart = pathParts[pathParts.length - 1];
if (lastPart) {
ids.push(lastPart);
}
}
} catch {
ids.push(url);
}
});
return ids;
};
// 提交(新增或编辑)
const handleSubmit = async () => {
if (loading.value) return;
loading.value = true;
try {
const valid = await formRef.value?.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
const submitData: any = {
...dataForm,
};
// 处理所有文件字段
const fileFields = [
'businessNegotiationTable', 'marketPurchaseMinutes', 'onlineMallMaterials',
'serviceDirectSelect', 'servicePublicSelect', 'purchaseRequirementTemplate',
'serviceInviteSelect', 'servicePublicSelectAuto', 'purchaseRequirement',
'meetingMinutes', 'feasibilityReport', 'meetingMinutesSingle',
'meetingMinutesImport', 'singleSourceProof', 'importApplication',
'governmentPurchaseIntent', 'servicePublicSelectSchool'
];
fileFields.forEach(field => {
if (submitData[field]) {
submitData[field] = getFileIdsArray(submitData[field]);
}
});
if (dataForm.id) {
await editObj(submitData);
useMessage().success('保存成功');
} else {
await addObj(submitData);
useMessage().success('提交成功');
}
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg || (dataForm.id ? '保存失败' : '提交失败'));
} finally {
loading.value = false;
}
};
// 暂存
const handleTempStore = async () => {
if (loading.value) return;
loading.value = true;
try {
const valid = await formRef.value?.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
const submitData: any = {
...dataForm,
};
const fileFields = [
'businessNegotiationTable', 'marketPurchaseMinutes', 'onlineMallMaterials',
'serviceDirectSelect', 'servicePublicSelect', 'purchaseRequirementTemplate',
'serviceInviteSelect', 'servicePublicSelectAuto', 'purchaseRequirement',
'meetingMinutes', 'feasibilityReport', 'meetingMinutesSingle',
'meetingMinutesImport', 'singleSourceProof', 'importApplication',
'governmentPurchaseIntent', 'servicePublicSelectSchool'
];
fileFields.forEach(field => {
if (submitData[field]) {
submitData[field] = getFileIdsArray(submitData[field]);
}
});
await tempStore(submitData);
useMessage().success('暂存成功');
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg || '暂存失败');
} finally {
loading.value = false;
}
};
// 暴露变量
defineExpose({
openDialog,
});
</script>
<style scoped>
.mb20 {
margin-bottom: 20px;
}
.mb10 {
margin-bottom: 10px;
}
.mt5 {
margin-top: 5px;
}
.step-title {
font-size: 16px;
font-weight: 600;
color: var(--el-text-color-primary);
padding-bottom: 10px;
border-bottom: 1px solid var(--el-border-color-light);
}
.template-note {
margin-top: 5px;
color: var(--el-text-color-secondary);
}
</style>