Merge branch 'developer'

This commit is contained in:
吴红兵
2026-03-09 23:31:31 +08:00
2 changed files with 161 additions and 42 deletions

View File

@@ -22,9 +22,14 @@
:disabled="isViewMode || flowFormDisabled" :disabled="isViewMode || flowFormDisabled"
class="compact-form" class="compact-form"
> >
<!-- 信息三列紧凑 --> <!-- 信息 -->
<div class="form-section-compact"> <el-divider content-position="left">
<el-row :gutter="16"> <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-col :span="8" class="mb12">
<el-form-item label="采购项目名称" prop="projectName"> <el-form-item label="采购项目名称" prop="projectName">
<el-input <el-input
@@ -131,31 +136,31 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</div>
<!-- 采购详情新增时需先填是否特殊情况是否集采预算金额后才显示 --> <!-- 采购详情新增时需先填是否特殊情况是否集采预算金额后才显示 -->
<div> <div>
<el-alert v-if="!showPurchaseDetailBlocks" type="info" :closable="false" class="mb16" show-icon> <el-alert v-if="!showPurchaseDetailBlocks" type="info" :closable="false" class="mb16" show-icon>
请先填写上方是否特殊情况是否集采预算金额系统将根据填写结果展示部门自行采购学校统一采购表单 请先填写上方是否特殊情况是否集采预算金额系统将根据填写结果展示部门自行采购学校统一采购表单
</el-alert> </el-alert>
<!-- 部门采购会议纪要预算超过2000元必须上传不区分部门自行采购/学校统一采购 -->
<el-row :gutter="16" v-if="showPurchaseDetailBlocks && 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>
<!-- 分支一部门自行采购三列紧凑textarea 单独一行 --> <!-- 分支一部门自行采购三列紧凑textarea 单独一行 -->
<div class="mb20 form-section-compact" v-if="showPurchaseDetailBlocks && isDeptPurchase"> <div class="mb20 form-section-compact" v-if="showPurchaseDetailBlocks && isDeptPurchase">
<div class="step-title mb12">部门自行采购</div> <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-col :span="24" class="mb12">
<el-form-item label="采购内容" prop="projectContent"> <el-form-item label="采购内容" prop="projectContent">
<el-input <el-input
@@ -194,7 +199,7 @@
:disabled="isPurchaseTypeDisabled" :disabled="isPurchaseTypeDisabled"
style="width: 100%" style="width: 100%"
> >
<el-option v-for="item in purchaseTypeDeptOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in purchaseTypeDeptOptions" :key="item.value" :label="item.label" :value="item.value" :disabled="item.disabled" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@@ -373,6 +378,21 @@
<!-- 分支二:学校统一采购(三列紧凑) --> <!-- 分支二:学校统一采购(三列紧凑) -->
<div class="mb20 form-section-compact" v-if="showPurchaseDetailBlocks && !isDeptPurchase"> <div class="mb20 form-section-compact" v-if="showPurchaseDetailBlocks && !isDeptPurchase">
<div class="step-title mb12">学校统一采购</div> <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-row :gutter="16">
<el-col :span="8" class="mb12"> <el-col :span="8" class="mb12">
<el-form-item label="组织采购形式" prop="purchaseSchool" :required="isPurchaseSchoolRequired"> <el-form-item label="组织采购形式" prop="purchaseSchool" :required="isPurchaseSchoolRequired">
@@ -842,12 +862,7 @@
<template v-else> <template v-else>
<el-button v-if="!isFlowEmbed" @click="handleCancel">取消</el-button> <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 && !flowSubmitDisabled" type="warning" @click="handleTempStore" :disabled="loading"> 暂存 </el-button>
<!-- <el-button --> <el-button v-if="!isFlowEmbed" type="primary" @click="handleSubmitFlow" :disabled="loading"> 提交 </el-button>
<!-- type="primary" -->
<!-- @click="handleSubmit" -->
<!-- :disabled="loading">-->
<!-- {{ isEditMode ? '保存' : '提交' }}-->
<!-- </el-button>-->
</template> </template>
</div> </div>
</div> </div>
@@ -859,10 +874,10 @@
<script setup lang="ts" name="PurchasingRequisitionAdd"> <script setup lang="ts" name="PurchasingRequisitionAdd">
import { reactive, ref, onMounted, computed, watch, nextTick } from 'vue'; import { reactive, ref, onMounted, computed, watch, nextTick } from 'vue';
import { useRouter, useRoute } from 'vue-router'; import { useRouter, useRoute } from 'vue-router';
import { addObj, tempStore, getObj, editObj, getApplyFiles } from '/@/api/purchase/purchasingrequisition'; import { addObj, tempStore, getObj, editObj, getApplyFiles, submitObj } from '/@/api/purchase/purchasingrequisition';
import { getTree } from '/@/api/purchase/purchasingcategory'; import { getTree } from '/@/api/purchase/purchasingcategory';
import { getDicts } from '/@/api/admin/dict'; import { getDicts } from '/@/api/admin/dict';
import { useMessage } from '/@/hooks/message'; import { useMessage, useMessageBox } from '/@/hooks/message';
import { useUserInfo } from '/@/stores/userInfo'; import { useUserInfo } from '/@/stores/userInfo';
import { usePurchaseRulesSingleton } from '/@/hooks/usePurchaseRules'; import { usePurchaseRulesSingleton } from '/@/hooks/usePurchaseRules';
import UploadFile from '/@/components/Upload/index.vue'; import UploadFile from '/@/components/Upload/index.vue';
@@ -1101,7 +1116,7 @@ const DEPT_PURCHASE_TYPE = {
BUSINESS_NEGOTIATION: '3', BUSINESS_NEGOTIATION: '3',
ENTRUST_CENTER: '4', ENTRUST_CENTER: '4',
INQUIRY: '5', INQUIRY: '5',
/** 公开招标(部门自行采购下委托采购中心采购时可选择) */ DIRECT_PURCHASE: '6',
OPEN_TENDERING: '100', OPEN_TENDERING: '100',
} as const; } as const;
@@ -1441,6 +1456,16 @@ watch(
{ immediate: true } { 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万服务类目特殊服务类目 // 判断是否自动选择网上商城采购方式5万<=金额<30万服务类目特殊服务类目
const isAutoSelectPurchaseType = computed(() => { const isAutoSelectPurchaseType = computed(() => {
if (!dataForm.budget) return false; if (!dataForm.budget) return false;
@@ -2183,12 +2208,16 @@ const getPurchaseTypeDeptDelegationDict = async () => {
} }
}; };
/** 部门采购方式下拉选项:根据采购途径动态切换字典 */ /** 部门采购方式下拉选项:根据采购途径动态切换字典,直接采购仅预算<2000时可选 */
const purchaseTypeDeptOptions = computed(() => { const purchaseTypeDeptOptions = computed(() => {
if (isEntrustCenterChannel.value) { if (isEntrustCenterChannel.value) {
return purchaseTypeDeptDelegationList.value; return purchaseTypeDeptDelegationList.value;
} }
return purchaseTypeDeptList.value; const budget = Number(dataForm.budget) || 0;
return purchaseTypeDeptList.value.map((item: any) => ({
...item,
disabled: item.value === DEPT_PURCHASE_TYPE.DIRECT_PURCHASE && budget >= 2000,
}));
}); });
// 获取学校采购形式字典 // 获取学校采购形式字典
@@ -2519,9 +2548,7 @@ const handleSubmit = async () => {
router.push('/purchase/purchasingrequisition'); router.push('/purchase/purchasingrequisition');
} }
} catch (err: any) { } catch (err: any) {
// 全局拦截器已经显示了错误提示,这里不需要再次显示 if (!err?._messageShown && !err?.msg) {
// 只有当错误没有 msg 时才显示默认错误提示
if (!err?.msg) {
useMessage().error('提交失败'); useMessage().error('提交失败');
} }
} finally { } finally {
@@ -2529,17 +2556,14 @@ const handleSubmit = async () => {
} }
}; };
// 暂存 // 暂存(跳过表单校验,允许部分填写)
const handleTempStore = async () => { const handleTempStore = async () => {
if (loading.value) return; if (loading.value) return;
loading.value = true; loading.value = true;
try { try {
const valid = await formRef.value?.validate().catch(() => {}); // 暂存模式不进行表单校验,允许用户部分填写后保存
if (!valid) { // 提交时才会执行完整的校验规则
loading.value = false;
return false;
}
const submitData: any = { const submitData: any = {
...dataForm, ...dataForm,
@@ -2611,7 +2635,7 @@ const handleTempStore = async () => {
} }
} }
} catch (err: any) { } catch (err: any) {
if (!err?.msg) { if (!err?._messageShown && !err?.msg) {
useMessage().error('暂存失败'); useMessage().error('暂存失败');
} }
} finally { } finally {
@@ -2619,6 +2643,101 @@ const handleTempStore = async () => {
} }
}; };
// 提交(暂存后启动流程)
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 = () => { const setCategoryCodePath = () => {
if (dataForm.categoryCode && categoryTreeData.value.length > 0) { if (dataForm.categoryCode && categoryTreeData.value.length > 0) {

View File

@@ -206,7 +206,7 @@
> >
</el-tag> </el-tag>
<el-tooltip <el-tooltip
v-if="scope.row.purchaseMode === '1' && scope.row.purchaseChannel === '1'" v-if="scope.row.status === '1' && scope.row.purchaseMode === '1' && scope.row.purchaseChannel === '1' && scope.row.purchaseType !=='6'"
:content="getSupplementTooltip(scope.row)" :content="getSupplementTooltip(scope.row)"
placement="top" placement="top"
> >