This commit is contained in:
吴红兵
2026-03-03 13:59:57 +08:00
parent d0e4071836
commit 168e134e1b
5 changed files with 642 additions and 20 deletions

View File

@@ -0,0 +1,73 @@
import request from "/@/utils/request"
const BASE_URL = '/purchase/purchasingruleconfig'
export function fetchList(query?: Object) {
return request({
url: `${BASE_URL}/page`,
method: 'get',
params: query
})
}
export function getObj(id: string) {
return request({
url: `${BASE_URL}/${id}`,
method: 'get'
})
}
export function addObj(obj: Object) {
return request({
url: BASE_URL,
method: 'post',
data: obj
})
}
export function putObj(obj: Object) {
return request({
url: `${BASE_URL}/edit`,
method: 'post',
data: obj
})
}
export function delObjs(ids: string[]) {
return request({
url: `${BASE_URL}/delete`,
method: 'post',
data: ids
})
}
export function getRuleTypes() {
return request({
url: `${BASE_URL}/rule-types`,
method: 'get'
})
}
export function getEnabledRules() {
return request({
url: `${BASE_URL}/enabled`,
method: 'get'
})
}
export interface RuleEvaluateParams {
budget: number
isCentralized?: string
isSpecial?: string
hasSupplier?: string
projectType?: string
ruleType?: string
}
export function evaluateRules(params: RuleEvaluateParams) {
return request({
url: `${BASE_URL}/evaluate`,
method: 'post',
data: params
})
}

View File

@@ -0,0 +1,110 @@
import { ref, computed, watch } from 'vue';
import { evaluateRules, getEnabledRules } from '/@/api/purchase/purchasingRuleConfig';
const RULE_CACHE_KEY = 'purchase_rule_cache';
const CACHE_EXPIRE_TIME = 5 * 60 * 1000;
interface RuleCache {
data: any[];
timestamp: number;
}
interface RuleResult {
purchaseMode: string;
purchaseSchool: string;
bidTemplate: string;
requiredFiles: { fieldName: string; fileTypeName: string; required: boolean }[];
matchedRules: any[];
}
const ruleCache = ref<RuleCache | null>(null);
export function usePurchaseRules() {
const loading = ref(false);
const rules = ref<any[]>([]);
const loadRules = async (forceRefresh = false) => {
if (!forceRefresh && ruleCache.value) {
const now = Date.now();
if (now - ruleCache.value.timestamp < CACHE_EXPIRE_TIME) {
rules.value = ruleCache.value.data;
return;
}
}
loading.value = true;
try {
const res = await getEnabledRules();
rules.value = res.data || [];
ruleCache.value = {
data: rules.value,
timestamp: Date.now()
};
} catch (e) {
console.error('加载采购规则失败', e);
} finally {
loading.value = false;
}
};
const evaluate = async (params: {
budget: number;
isCentralized?: string;
isSpecial?: string;
hasSupplier?: string;
projectType?: string;
}): Promise<RuleResult | null> => {
try {
const res = await evaluateRules(params);
return res.data;
} catch (e) {
console.error('评估规则失败', e);
return null;
}
};
const getThresholds = () => {
const thresholds: Record<string, number> = {
deptPurchase: 50000,
feasibility: 300000,
publicSelect: 400000,
govPurchase: 1000000
};
rules.value.forEach(rule => {
if (rule.ruleCode === 'DEPT_PURCHASE_THRESHOLD' && rule.amountMax) {
thresholds.deptPurchase = Number(rule.amountMax);
}
if (rule.ruleCode === 'FEASIBILITY_REPORT' && rule.amountMin) {
thresholds.feasibility = Number(rule.amountMin);
}
if (rule.ruleCode === 'PUBLIC_BID_40W_100W' && rule.amountMin) {
thresholds.publicSelect = Number(rule.amountMin);
}
if (rule.ruleCode === 'GOV_PURCHASE_THRESHOLD' && rule.amountMin) {
thresholds.govPurchase = Number(rule.amountMin);
}
});
return thresholds;
};
loadRules();
return {
loading,
rules,
loadRules,
evaluate,
getThresholds
};
}
let globalRulesInstance: ReturnType<typeof usePurchaseRules> | null = null;
export function usePurchaseRulesSingleton() {
if (!globalRulesInstance) {
globalRulesInstance = usePurchaseRules();
}
return globalRulesInstance;
}

View File

@@ -0,0 +1,246 @@
<template>
<el-dialog
v-model="visible"
:title="dialogTitle"
width="700px"
:close-on-click-modal="false"
draggable
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="120px"
v-loading="loading"
>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="规则编码" prop="ruleCode">
<el-input v-model="form.ruleCode" placeholder="请输入规则编码" :disabled="isEdit" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规则名称" prop="ruleName">
<el-input v-model="form.ruleName" placeholder="请输入规则名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规则类型" prop="ruleType">
<el-select v-model="form.ruleType" placeholder="请选择规则类型" style="width: 100%">
<el-option
v-for="item in ruleTypes"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="优先级" prop="sortOrder">
<el-input-number v-model="form.sortOrder" :min="1" :max="999" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="金额区间">
<div style="display: flex; align-items: center; gap: 8px;">
<el-input-number
v-model="form.amountMin"
:min="0"
:precision="2"
:controls="false"
placeholder="金额下限"
style="width: 200px;"
/>
<span></span>
<span style="margin: 0 8px;"></span>
<el-input-number
v-model="form.amountMax"
:min="0"
:precision="2"
:controls="false"
placeholder="金额上限"
style="width: 200px;"
/>
<span></span>
<span style="color: #909399; font-size: 12px;">不填表示不限</span>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="附加条件字段">
<el-select v-model="form.conditionField" placeholder="请选择" clearable style="width: 100%">
<el-option label="是否集采" value="isCentralized" />
<el-option label="是否特殊情况" value="isSpecial" />
<el-option label="是否有推荐供应商" value="hasSupplier" />
<el-option label="项目类别" value="projectType" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="附加条件值">
<el-input v-model="form.conditionValue" placeholder="如0、1、A等" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="动作类型" prop="actionType">
<el-select v-model="form.actionType" placeholder="请选择动作类型" style="width: 100%">
<el-option label="设置字段值" value="SET_FIELD" />
<el-option label="切换模板" value="SWITCH_TEMPLATE" />
<el-option label="要求文件" value="REQUIRE_FILE" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="动作目标" prop="actionTarget">
<el-select v-model="form.actionTarget" placeholder="请选择" allow-create filterable style="width: 100%">
<el-option v-if="form.actionType === 'SET_FIELD'" label="采购形式" value="purchaseMode" />
<el-option v-if="form.actionType === 'SET_FIELD'" label="学校采购方式" value="purchaseSchool" />
<el-option v-if="form.actionType === 'SWITCH_TEMPLATE'" label="比选模板" value="bidTemplate" />
<el-option v-if="form.actionType === 'REQUIRE_FILE'" label="可行性论证报告" value="feasibilityReport" />
<el-option v-if="form.actionType === 'REQUIRE_FILE'" label="会议纪要" value="meetingMinutes" />
<el-option v-if="form.actionType === 'REQUIRE_FILE'" label="政府采购意向表" value="governmentIntention" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="动作值">
<el-input v-model="form.actionValue" placeholder="设置的值" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否启用">
<el-radio-group v-model="form.isEnabled">
<el-radio label="1">启用</el-radio>
<el-radio label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="规则描述">
<el-input
v-model="form.description"
type="textarea"
:rows="2"
placeholder="请输入规则描述"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">确定</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { useMessage } from "/@/hooks/message";
import { getObj, addObj, putObj, getRuleTypes } from "/@/api/purchase/purchasingRuleConfig";
const emit = defineEmits(['refresh']);
const visible = ref(false);
const loading = ref(false);
const submitLoading = ref(false);
const formRef = ref();
const ruleTypes = ref<{ code: string; name: string }[]>([]);
const isEdit = ref(false);
const dialogTitle = computed(() => isEdit.value ? '编辑规则' : '新增规则');
const form = reactive({
id: '',
ruleCode: '',
ruleName: '',
ruleType: '',
amountMin: null as number | null,
amountMax: null as number | null,
conditionField: '',
conditionValue: '',
actionType: '',
actionTarget: '',
actionValue: '',
description: '',
sortOrder: 1,
isEnabled: '1',
remark: ''
});
const rules = {
ruleCode: [{ required: true, message: '请输入规则编码', trigger: 'blur' }],
ruleName: [{ required: true, message: '请输入规则名称', trigger: 'blur' }],
ruleType: [{ required: true, message: '请选择规则类型', trigger: 'change' }],
actionType: [{ required: true, message: '请选择动作类型', trigger: 'change' }],
actionTarget: [{ required: true, message: '请输入动作目标', trigger: 'blur' }]
};
const openDialog = async (id?: string) => {
visible.value = true;
isEdit.value = !!id;
resetForm();
const res = await getRuleTypes();
ruleTypes.value = res.data || [];
if (id) {
loading.value = true;
try {
const response = await getObj(id);
if (response.data) {
Object.assign(form, response.data);
}
} finally {
loading.value = false;
}
}
};
const resetForm = () => {
form.id = '';
form.ruleCode = '';
form.ruleName = '';
form.ruleType = '';
form.amountMin = null;
form.amountMax = null;
form.conditionField = '';
form.conditionValue = '';
form.actionType = '';
form.actionTarget = '';
form.actionValue = '';
form.description = '';
form.sortOrder = 1;
form.isEnabled = '1';
form.remark = '';
formRef.value?.resetFields();
};
const handleSubmit = async () => {
await formRef.value?.validate();
submitLoading.value = true;
try {
if (isEdit.value) {
await putObj(form);
useMessage().success('修改成功');
} else {
await addObj(form);
useMessage().success('新增成功');
}
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg || '操作失败');
} finally {
submitLoading.value = false;
}
};
defineExpose({ openDialog });
</script>

View File

@@ -0,0 +1,169 @@
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
<el-row>
<div class="mb8" style="width: 100%">
<el-button
icon="folder-add"
type="primary"
class="ml10"
@click="formDialogRef.openDialog()"
v-auth="'purchase_purchasingruleconfig_add'"
>
新增规则
</el-button>
<el-button
plain
:disabled="multiple"
icon="Delete"
type="primary"
v-auth="'purchase_purchasingruleconfig_del'"
@click="handleDelete(selectObjs)"
>
删除
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10 mr20"
style="float: right;"
@queryTable="getDataList"
/>
</div>
</el-row>
<el-table
:data="state.dataList"
v-loading="state.loading"
border
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
@selection-change="selectionChangHandle"
>
<el-table-column type="selection" width="40" align="center" />
<el-table-column type="index" label="#" width="50" />
<el-table-column prop="ruleCode" label="规则编码" width="180" show-overflow-tooltip />
<el-table-column prop="ruleName" label="规则名称" min-width="200" show-overflow-tooltip />
<el-table-column prop="ruleTypeName" label="规则类型" width="120" />
<el-table-column label="金额区间" width="180">
<template #default="scope">
<span v-if="scope.row.amountMin || scope.row.amountMax">
{{ formatAmount(scope.row.amountMin) }} ~ {{ formatAmount(scope.row.amountMax) }}
</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="附加条件" width="150">
<template #default="scope">
<span v-if="scope.row.conditionField">
{{ getConditionLabel(scope.row.conditionField) }} = {{ scope.row.conditionValue }}
</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="actionTypeName" label="动作类型" width="100" />
<el-table-column prop="description" label="规则描述" min-width="250" show-overflow-tooltip />
<el-table-column prop="sortOrder" label="优先级" width="80" align="center" />
<el-table-column label="状态" width="80" align="center">
<template #default="scope">
<el-tag :type="scope.row.isEnabled === '1' ? 'success' : 'danger'" size="small">
{{ scope.row.isEnabled === '1' ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<el-button
icon="edit-pen"
text
type="primary"
v-auth="'purchase_purchasingruleconfig_edit'"
@click="formDialogRef.openDialog(scope.row.id)"
>
编辑
</el-button>
<el-button
icon="delete"
text
type="primary"
v-auth="'purchase_purchasingruleconfig_del'"
@click="handleDelete([scope.row.id])"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination"
/>
</div>
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
</div>
</template>
<script setup lang="ts" name="purchasingRuleConfig">
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObjs } from "/@/api/purchase/purchasingRuleConfig";
import { useMessage, useMessageBox } from "/@/hooks/message";
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
const formDialogRef = ref();
const queryRef = ref();
const showSearch = ref(true);
const selectObjs = ref([]) as any;
const multiple = ref(true);
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {},
pageList: fetchList
});
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
tableStyle
} = useTable(state);
const selectionChangHandle = (objs: { id: string }[]) => {
selectObjs.value = objs.map(({ id }) => id);
multiple.value = !objs.length;
};
const handleDelete = async (ids: string[]) => {
try {
await useMessageBox().confirm('此操作将永久删除该规则,是否继续?');
} catch {
return;
}
try {
await delObjs(ids);
getDataList();
useMessage().success('删除成功');
} catch (err: any) {
useMessage().error(err.msg || '删除失败');
}
};
const formatAmount = (amount: number | null) => {
if (amount === null || amount === undefined) return '不限';
return (amount / 10000).toFixed(0) + '万';
};
const getConditionLabel = (field: string) => {
const map: Record<string, string> = {
isCentralized: '是否集采',
isSpecial: '是否特殊',
hasSupplier: '是否有推荐供应商',
projectType: '项目类别'
};
return map[field] || field;
};
</script>

View File

@@ -121,12 +121,17 @@
</div>
</el-form-item>
</el-col>
<el-col :span="24" class="mb12">
<el-form-item label="采购内容" prop="projectContent">
<el-input v-model="dataForm.projectContent" type="textarea" :rows="3" :maxlength="1000" show-word-limit placeholder="请输入采购内容限制1000字" clearable :disabled="flowFieldDisabled('projectContent')" />
</el-form-item>
</el-col>
<el-col :span="8" class="mb12" v-if="isPurchaseType(DEPT_PURCHASE_TYPE.BUSINESS_NEGOTIATION)">
<el-col :span="24" class="mb12">
<el-form-item label="采购内容" prop="projectContent">
<el-input v-model="dataForm.projectContent" type="textarea" :rows="3" :maxlength="1000" show-word-limit placeholder="请输入采购内容限制1000字" clearable :disabled="flowFieldDisabled('projectContent')" />
</el-form-item>
</el-col>
<el-col :span="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="isPurchaseType(DEPT_PURCHASE_TYPE.BUSINESS_NEGOTIATION)">
<el-form-item label="商务洽谈表" prop="businessNegotiationTable" required>
<upload-file v-model="dataForm.businessNegotiationTable" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.businessNegotiationTable }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('businessNegotiationTable')" />
<el-button type="primary" link icon="Download" size="small" @click="downloadTemplate('business_negotiation')" style="margin-top: 8px; display: inline-block">下载商务洽谈表模版</el-button>
@@ -517,6 +522,7 @@ import { addObj, tempStore, getObj, editObj, getApplyFiles } from '/@/api/purcha
import { getTree } from '/@/api/purchase/purchasingcategory';
import { getDicts } from '/@/api/admin/dict';
import { useMessage } from '/@/hooks/message';
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';
@@ -656,7 +662,8 @@ const dataForm = reactive({
agentId: '',
agentName: '',
representorName:'',
representorType: ''
representorType: '',
negotiationReason: ''
});
/** 查看时展示的招标文件列表(实施采购上传的 type=130 */
const viewImplementPurchaseFiles = ref<{ id: string; fileTitle?: string; createTime?: string; remark?: string }[]>([]);
@@ -714,11 +721,13 @@ Object.entries(FILE_TYPE_MAP).forEach(([field, type]) => {
FILE_TYPE_TO_FIELDS[type].push(field);
});
// 金额阈值常量(与后端 PurchaseConstants 保持一致)
const BUDGET_DEPT_PURCHASE_THRESHOLD = 50000; // 部门自行采购上限(< 5 万)
const BUDGET_FEASIBILITY_THRESHOLD = 300000; // 可行性论证/会议纪要起点(≥ 30 万)
const BUDGET_PUBLIC_SELECT_THRESHOLD = 400000; // 公开比选起点(≥ 40 万)
const BUDGET_GOV_PURCHASE_THRESHOLD = 1000000; // 政府采购起点(≥ 100 万)
// 金额阈值(从规则配置动态获取,默认值与后端 PurchaseConstants 保持一致)
const { rules: purchaseRules, getThresholds, evaluate: evaluatePurchaseRules } = usePurchaseRulesSingleton();
const BUDGET_DEPT_PURCHASE_THRESHOLD = computed(() => getThresholds().deptPurchase || 50000);
const BUDGET_FEASIBILITY_THRESHOLD = computed(() => getThresholds().feasibility || 300000);
const BUDGET_PUBLIC_SELECT_THRESHOLD = computed(() => getThresholds().publicSelect || 400000);
const BUDGET_GOV_PURCHASE_THRESHOLD = computed(() => getThresholds().govPurchase || 1000000);
// 部门采购方式字典 value与 DeptPurchaseTypeEnum 一致)
const DEPT_PURCHASE_TYPE = {
@@ -775,7 +784,7 @@ const isDeptPurchase = computed(() => {
return !!(isSpecialNoValue && isCentralizedNoValue &&
dataForm.isSpecial === isSpecialNoValue &&
dataForm.isCentralized === isCentralizedNoValue &&
dataForm.budget && dataForm.budget < 50000);
dataForm.budget && dataForm.budget < BUDGET_DEPT_PURCHASE_THRESHOLD.value);
});
// 是否为“委托采购中心采购”途径
@@ -795,8 +804,8 @@ const showPurchaseDetailBlocks = computed(() => {
const schoolUnifiedPurchaseFormDefault = computed(() => {
if (isDeptPurchase.value || dataForm.budget == null) return null;
const budget = Number(dataForm.budget);
if (budget >= BUDGET_GOV_PURCHASE_THRESHOLD) return '1'; // 政府采购
if (budget >= BUDGET_DEPT_PURCHASE_THRESHOLD && budget < BUDGET_GOV_PURCHASE_THRESHOLD) {
if (budget >= BUDGET_GOV_PURCHASE_THRESHOLD.value) return '1'; // 政府采购
if (budget >= BUDGET_DEPT_PURCHASE_THRESHOLD.value && budget < BUDGET_GOV_PURCHASE_THRESHOLD.value) {
if (dataForm.isCentralized === '0') return '2'; // 集采=否 → 学校自主采购
if (dataForm.isCentralized === '1') return '1'; // 政府集中采购 → 政府采购
if (dataForm.isCentralized === '2') return '2'; // 学校集中采购 → 学校自主采购
@@ -959,7 +968,7 @@ watch(
const isAutoSelectPurchaseType = computed(() => {
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD && budget < BUDGET_PUBLIC_SELECT_THRESHOLD && isServiceCategory.value && isSpecialServiceCategory.value;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD.value && budget < BUDGET_PUBLIC_SELECT_THRESHOLD.value && isServiceCategory.value && isSpecialServiceCategory.value;
});
// 判断是否显示自动邀请比选模版5万<=金额<40万服务类目特殊服务类目
@@ -967,7 +976,7 @@ const showAutoInviteSelect = computed(() => {
if (!isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD && budget < BUDGET_PUBLIC_SELECT_THRESHOLD && isServiceCategory.value && isSpecialServiceCategory.value;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD.value && budget < BUDGET_PUBLIC_SELECT_THRESHOLD.value && isServiceCategory.value && isSpecialServiceCategory.value;
});
// 判断是否显示学校统一采购的自动邀请比选模版5万<=金额<40万服务类目特殊服务类目
@@ -975,7 +984,7 @@ const showAutoInviteSelectSchool = computed(() => {
if (isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD && budget < BUDGET_PUBLIC_SELECT_THRESHOLD && isSpecialServiceCategory.value;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD.value && budget < BUDGET_PUBLIC_SELECT_THRESHOLD.value && isSpecialServiceCategory.value;
});
// 判断是否显示自动公开比选模版40万<=金额<100万特殊服务类目isMallService=1、isProjectService=1
@@ -983,7 +992,7 @@ const showAutoPublicSelect = computed(() => {
if (isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= BUDGET_PUBLIC_SELECT_THRESHOLD && budget < BUDGET_GOV_PURCHASE_THRESHOLD && isSpecialServiceCategory.value;
return budget >= BUDGET_PUBLIC_SELECT_THRESHOLD.value && budget < BUDGET_GOV_PURCHASE_THRESHOLD.value && isSpecialServiceCategory.value;
});
// 获取需求文件的 prop 名称(用于表单验证)
@@ -1005,7 +1014,7 @@ const isAutoSelectPurchaseTypeUnion = computed(() => {
if (isDeptPurchase.value) return false;
if (!dataForm.budget) return false;
const budget = dataForm.budget;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD && budget < BUDGET_PUBLIC_SELECT_THRESHOLD && isSpecialServiceCategory.value;
return budget >= BUDGET_DEPT_PURCHASE_THRESHOLD.value && budget < BUDGET_PUBLIC_SELECT_THRESHOLD.value && isSpecialServiceCategory.value;
});
// 监听品目编码、预算金额、采购类型及采购途径变化,自动设置/清空采购方式
@@ -1217,6 +1226,20 @@ const dataRules = reactive({
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'
}
],
});
// 取消
@@ -1298,6 +1321,7 @@ async function loadDetail(applyId: string | number) {
agentName: detail.agentName ?? '',
representorName: detail.representorName ?? '',
representorType: detail.representorType ?? '',
negotiationReason: detail.negotiationReason ?? '',
});
setCategoryCodePath();
try {