diff --git a/src/assets/file/采购申请流程字段整理.docx b/src/assets/file/采购申请流程字段整理.docx new file mode 100644 index 0000000..638f9b8 Binary files /dev/null and b/src/assets/file/采购申请流程字段整理.docx differ diff --git a/src/views/finance/purchasingrequisition/add.vue b/src/views/finance/purchasingrequisition/add.vue new file mode 100644 index 0000000..969bc00 --- /dev/null +++ b/src/views/finance/purchasingrequisition/add.vue @@ -0,0 +1,1124 @@ + + + + + + diff --git a/src/views/finance/purchasingrequisition/form.vue b/src/views/finance/purchasingrequisition/form.vue index 8ce2072..3055e13 100644 --- a/src/views/finance/purchasingrequisition/form.vue +++ b/src/views/finance/purchasingrequisition/form.vue @@ -2,7 +2,7 @@
@@ -97,10 +97,11 @@ clearable :disabled="isView" style="width: 100%"> - - - - + @@ -112,7 +113,7 @@ v-model="dataForm.categoryCode" :data="categoryTreeData" :props="{ value: 'code', label: 'name', children: 'children' }" - placeholder="请选择品目编码(包含项目类别)" + placeholder="请选择品目编码(需要显示所有的层级)" clearable check-strictly :render-after-expand="false" @@ -125,9 +126,9 @@
- -
-
内部自行采购
+ +
+
部门自行采购
- - - - - + - - - -
- - 下载默认需求表 - + + + + + 下载商务洽谈表模版 + + + + + + + + 下载市场采购纪要模版 + + + + + + + +
+ 请上传zip格式的压缩包
-
- - 下载直选模版需求表 - -
- - 如无意向单位,则需要上传公开比选需求模版 - + + + + + + +
学校统一采购
- + + + + + + + + + + + + + + + + + + + + + + + + 下载《采购需求填报模板》模版 + - - - + + - 下载《项目可行性论证报告模板.doc》 + 下载《服务商城项目需求模板(公开比选)》模版 - - - - 下载《进口产品申请及专家论证意见表.doc》 - - - - - - 下载《单一来源论专家证附件.docx》 - - + + - - - - - -
@@ -382,26 +608,43 @@ const dataForm = reactive({ isCentralized: '', isSpecial: '', purchaseMode: '', - purchaseSchool: '', purchaseType: '', + purchaseTypeUnion: '', categoryCode: '', - fileIds: '', remark: '', status: '', - // 第二步新增字段 - requirementTemplate: '', + // 部门自行采购字段 + businessNegotiationTable: '', + marketPurchaseMinutes: '', + onlineMallMaterials: '', + entrustCenterType: '', + hasSupplier: '', + serviceDirectSelect: '', + servicePublicSelect: '', + purchaseRequirementTemplate: '', + hasRecommendedSupplier: '', + recommendedSuppliers: '', + serviceInviteSelect: '', + servicePublicSelectAuto: '', + // 学校统一采购字段 purchaseRequirement: '', meetingMinutes: '', feasibilityReport: '', - importApplication: '', + meetingMinutesSingle: '', + meetingMinutesImport: '', singleSourceProof: '', + importApplication: '', governmentPurchaseIntent: '', - businessDept: '', - otherMaterials: '', + servicePublicSelectSchool: '', }); const categoryTreeData = ref([]); +const selectedCategory = ref(null); const fundSourceList = ref([]); const isCentralizedList = ref([]); +const isSpecialList = ref([]); +const purchaseTypeDeptList = ref([]); +const purchaseModeSchoolList = ref([]); +const purchaseTypeUnionList = ref([]); const visible = ref(false); const loading = ref(false); const dialogType = ref<'add' | 'edit' | 'view'>('add'); @@ -414,85 +657,101 @@ const dialogTitle = computed(() => { return '新增采购申请'; }); -// 判断是否为内部采购 -const isInternalPurchase = computed(() => { - // 内部采购条件:非集采 + 非特殊 + 金额 < 50000 - const isNotCentralized = dataForm.isCentralized === '0'; - const isNotSpecial = dataForm.isSpecial === '0'; - const isLessThan50000 = dataForm.budget !== null && dataForm.budget < 50000; - return isNotCentralized && isNotSpecial && isLessThan50000; +// 判断是否为部门自行采购(是否集采=0) +const isDeptPurchase = computed(() => { + return dataForm.isCentralized === '0'; }); // 第二步标题 const stepTwoTitle = computed(() => { - return isInternalPurchase.value ? '内部自行采购' : '学校统一采购'; + return isDeptPurchase.value ? '部门自行采购' : '学校统一采购'; }); -// 判断是否显示默认需求表(委托采购中心其他方式 + 货物类) -const showDefaultRequirementTemplate = computed(() => { - return dataForm.purchaseType === 'entrust_other' && getProjectTypeFromCategory() === 'A'; -}); - -// 判断是否显示直选模版(委托采购中心服务类网上商城 + 服务类) -const showDirectSelectTemplate = computed(() => { - if (dataForm.purchaseType !== 'entrust_service_online') return false; - const projectType = getProjectTypeFromCategory(); - if (projectType !== 'C') return false; - // 如果项目类型为服务类,且在约定的特殊服务类目中,且预算金额大于等于5万且小于100万,跳过此规则 - if (isSpecialServiceCategory() && dataForm.budget && dataForm.budget >= 50000 && dataForm.budget < 1000000) { - return false; +// 从品目编码中获取项目类型和属性 +const getCategoryInfo = () => { + if (!dataForm.categoryCode || categoryTreeData.value.length === 0) { + return null; } - return true; -}); - -// 判断是否显示邀请比选模版(服务类 + 特殊服务类目 + 5万<=金额<100万) -const showInviteSelectTemplate = computed(() => { - const projectType = getProjectTypeFromCategory(); - if (projectType !== 'C') return false; - return isSpecialServiceCategory() && dataForm.budget && dataForm.budget >= 50000 && dataForm.budget < 1000000; -}); - -// 从品目编码中获取项目类型(A:货物, B:工程, C:服务) -const getProjectTypeFromCategory = (): string => { - // 这里需要根据实际的品目编码数据结构来判断 - // 假设品目编码的前缀或某个字段包含项目类型信息 - // 暂时返回空,需要根据实际数据结构调整 - if (dataForm.categoryCode) { - // 可以根据categoryCode查询categoryTreeData来获取类型 - 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; - } + const findCategory = (data: any[], code: string): any => { + for (const item of data) { + if (item.code === code) { + return item; } - return null; - }; - const category = findCategory(categoryTreeData.value, dataForm.categoryCode); - // 假设category对象有type字段,或者根据name判断 - // 这里需要根据实际数据结构调整 - return dataForm.projectType || ''; - } - return dataForm.projectType || ''; + 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 isSpecialServiceCategory = (): boolean => { - // 这里需要根据实际的特殊服务类目列表来判断 - // 暂时返回false,需要根据实际业务规则调整 - return false; -}; +// 判断是否为服务类 +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 = { - 'default_requirement': { fileName: '服务商城项目需求模板(直选).doc', displayName: '默认需求表.doc' }, - 'direct_select': { fileName: '服务商城项目需求模板(直选).doc', displayName: '直选模版需求表.doc' }, - 'public_select': { fileName: '服务商城项目需求模板(公开比选).doc', displayName: '公开比选需求模版.doc' }, - 'invite_select': { fileName: '服务商城项目需求模板(邀请比选).doc', displayName: '邀请比选模版.doc' }, + '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' }, @@ -505,7 +764,6 @@ const downloadTemplate = async (type: string) => { } try { - // 如果模版在assets/file目录下 const fileUrl = new URL(`../../assets/file/${template.fileName}`, import.meta.url).href; const response = await fetch(fileUrl); if (!response.ok) { @@ -522,7 +780,6 @@ const downloadTemplate = async (type: string) => { document.body.removeChild(link); useMessage().success('模版下载成功'); } catch (error) { - // 如果本地文件不存在,尝试从后端下载 try { await other.downBlobFile( `/purchase/purchasingfiles/downloadTemplate?type=${type}`, @@ -571,7 +828,6 @@ const dataRules = reactive({ // 下一步 const nextStep = async () => { try { - // 验证第一步的必填项 const fieldsToValidate = ['projectName', 'applyDate', 'fundSource', 'budget', 'isCentralized', 'isSpecial', 'categoryCode']; await formRef.value?.validateField(fieldsToValidate); currentStep.value = 1; @@ -603,27 +859,42 @@ const openDialog = async (type: 'add' | 'edit' | 'view', rowData?: any) => { isCentralized: '', isSpecial: '', purchaseMode: '', - purchaseSchool: '', purchaseType: '', + purchaseTypeUnion: '', categoryCode: '', - fileIds: '', remark: '', status: '', - requirementTemplate: '', + businessNegotiationTable: '', + marketPurchaseMinutes: '', + onlineMallMaterials: '', + entrustCenterType: '', + hasSupplier: '', + serviceDirectSelect: '', + servicePublicSelect: '', + purchaseRequirementTemplate: '', + hasRecommendedSupplier: '', + recommendedSuppliers: '', + serviceInviteSelect: '', + servicePublicSelectAuto: '', purchaseRequirement: '', meetingMinutes: '', feasibilityReport: '', - importApplication: '', + meetingMinutesSingle: '', + meetingMinutesImport: '', singleSourceProof: '', + importApplication: '', governmentPurchaseIntent: '', - businessDept: '', - otherMaterials: '', + servicePublicSelectSchool: '', }); await Promise.all([ getCategoryTreeData(), getFundSourceDict(), getIsCentralizedDict(), + getIsSpecialDict(), + getPurchaseTypeDeptDict(), + getPurchaseModeSchoolDict(), + getPurchaseTypeUnionDict(), ]); nextTick(() => { @@ -632,34 +903,7 @@ const openDialog = async (type: 'add' | 'edit' | 'view', rowData?: any) => { loading.value = true; getObj(rowData.id) .then((res) => { - Object.assign(dataForm, { - id: res.data.id || '', - projectName: res.data.projectName || '', - projectType: res.data.projectType || '', - projectContent: res.data.projectContent || '', - applyDate: res.data.applyDate || '', - fundSource: res.data.fundSource || '', - budget: res.data.budget || null, - isCentralized: res.data.isCentralized || '', - isSpecial: res.data.isSpecial || '', - purchaseMode: res.data.purchaseMode || '', - purchaseSchool: res.data.purchaseSchool || '', - purchaseType: res.data.purchaseType || '', - categoryCode: res.data.categoryCode || '', - fileIds: res.data.fileIds ? (Array.isArray(res.data.fileIds) ? res.data.fileIds.join(',') : res.data.fileIds) : '', - remark: res.data.remark || '', - status: res.data.status || '', - requirementTemplate: res.data.requirementTemplate || '', - purchaseRequirement: res.data.purchaseRequirement || '', - meetingMinutes: res.data.meetingMinutes || '', - feasibilityReport: res.data.feasibilityReport || '', - importApplication: res.data.importApplication || '', - singleSourceProof: res.data.singleSourceProof || '', - governmentPurchaseIntent: res.data.governmentPurchaseIntent || '', - businessDept: res.data.businessDept || '', - otherMaterials: res.data.otherMaterials || '', - }); - // 编辑或查看时,直接显示第二步 + Object.assign(dataForm, res.data || {}); if (type === 'edit' || type === 'view') { currentStep.value = 1; } @@ -691,23 +935,48 @@ const getCategoryTreeData = async () => { // 获取资金来源字典 const getFundSourceDict = async () => { try { - const res = await getDicts('fund_source'); + 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) { - console.error('获取资金来源字典失败', 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('is_centralized'); + const res = await getDicts('PURCHASE_IS_CEN'); isCentralizedList.value = []; if (res.data && Array.isArray(res.data)) { isCentralizedList.value = res.data.map((item: any) => ({ @@ -715,21 +984,105 @@ const getIsCentralizedDict = async () => { value: item.value || item.dictValue || item.code })); } else { - // 如果没有字典数据,使用默认值 isCentralizedList.value = [ { label: '否', value: '0' }, - { label: '是', value: '1' } + { label: '政府集中采购', value: '1' }, + { label: '学校集中采购', value: '2' } ]; } } catch (err) { - // 如果获取失败,使用默认值 isCentralizedList.value = [ { label: '否', value: '0' }, - { label: '是', value: '1' } + { 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 []; @@ -771,19 +1124,26 @@ const handleSubmit = async () => { return false; } - const submitData = { + const submitData: any = { ...dataForm, - fileIds: getFileIdsArray(dataForm.fileIds), - requirementTemplate: getFileIdsArray(dataForm.requirementTemplate), - purchaseRequirement: getFileIdsArray(dataForm.purchaseRequirement), - meetingMinutes: getFileIdsArray(dataForm.meetingMinutes), - feasibilityReport: getFileIdsArray(dataForm.feasibilityReport), - importApplication: getFileIdsArray(dataForm.importApplication), - singleSourceProof: getFileIdsArray(dataForm.singleSourceProof), - governmentPurchaseIntent: getFileIdsArray(dataForm.governmentPurchaseIntent), - otherMaterials: getFileIdsArray(dataForm.otherMaterials), }; + // 处理所有文件字段 + 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('保存成功'); @@ -812,19 +1172,25 @@ const handleTempStore = async () => { return false; } - const submitData = { + const submitData: any = { ...dataForm, - fileIds: getFileIdsArray(dataForm.fileIds), - requirementTemplate: getFileIdsArray(dataForm.requirementTemplate), - purchaseRequirement: getFileIdsArray(dataForm.purchaseRequirement), - meetingMinutes: getFileIdsArray(dataForm.meetingMinutes), - feasibilityReport: getFileIdsArray(dataForm.feasibilityReport), - importApplication: getFileIdsArray(dataForm.importApplication), - singleSourceProof: getFileIdsArray(dataForm.singleSourceProof), - governmentPurchaseIntent: getFileIdsArray(dataForm.governmentPurchaseIntent), - otherMaterials: getFileIdsArray(dataForm.otherMaterials), }; + 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; diff --git a/src/views/finance/purchasingrequisition/index.vue b/src/views/finance/purchasingrequisition/index.vue index 73157c4..43e71af 100644 --- a/src/views/finance/purchasingrequisition/index.vue +++ b/src/views/finance/purchasingrequisition/index.vue @@ -80,7 +80,7 @@ + @click="handleAdd"> 新增 @@ -220,6 +220,7 @@