Files
school-developer/src/views/purchase/purchasingrequisition/bidfile-audit.vue
吴红兵 4b33dc9aab 更新
2026-03-08 23:23:02 +08:00

962 lines
28 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="bidfile-audit-container">
<!-- 基本信息 -->
<el-card shadow="never" class="info-card">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon><Document /></el-icon>
采购申请信息
</span>
<el-tag v-if="flowStatus === '0'" type="warning">运行中</el-tag>
<el-tag v-else-if="flowStatus === '1'" type="success">已完成</el-tag>
<el-tag v-else-if="flowStatus === '2'" type="info">已作废</el-tag>
</div>
</template>
<!-- 招标代理只能看到有限信息 -->
<el-descriptions v-if="isAgent" :column="2" border>
<el-descriptions-item label="采购编号">{{ applyData.purchaseNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="项目名称">{{ applyData.projectName || '-' }}</el-descriptions-item>
<el-descriptions-item label="招标代理">{{ applyData.agentName || '-' }}</el-descriptions-item>
</el-descriptions>
<!-- 其他角色可以看到完整信息 -->
<el-descriptions v-else :column="3" border>
<el-descriptions-item label="采购编号">{{ applyData.purchaseNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="项目名称">{{ applyData.projectName || '-' }}</el-descriptions-item>
<el-descriptions-item label="预算金额">{{ applyData.budget ? Number(applyData.budget).toLocaleString() + ' 元' : '-' }}</el-descriptions-item>
<el-descriptions-item label="采购方式">{{ purchaseTypeLabel || '-' }}</el-descriptions-item>
<el-descriptions-item label="招标代理">{{ applyData.agentName || '-' }}</el-descriptions-item>
<el-descriptions-item label="申请人">{{ applyData.createName || applyData.createBy || '-' }}</el-descriptions-item>
<el-descriptions-item label="申请部门" :span="3">{{ applyData.deptName || '-' }}</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 需求文件招标代理可见 -->
<el-card v-if="isAgent" shadow="never" class="file-card">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon><FolderOpened /></el-icon>
需求文件
</span>
</div>
</template>
<el-table v-loading="requirementLoading" :data="requirementFiles" stripe border>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="fileTitle" label="文件名" min-width="200" show-overflow-tooltip />
<el-table-column prop="createTime" label="上传时间" width="160" />
<el-table-column label="操作" width="100" align="center" fixed="right">
<template #default="{ row }">
<el-button type="primary" link icon="Download" @click="handleDownloadRequirement(row)">下载</el-button>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!requirementLoading && requirementFiles.length === 0" description="暂无需求文件" />
</el-card>
<!-- 招标文件信息 -->
<el-card shadow="never" class="file-card">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon><FolderOpened /></el-icon>
招标文件
</span>
<el-tag v-if="currentVersion" type="success">{{ currentVersion.version }}</el-tag>
</div>
</template>
<el-tabs v-model="activeTab">
<!-- 当前版本 -->
<el-tab-pane label="当前版本" name="current">
<div v-if="currentVersion" class="version-detail">
<el-descriptions :column="2" border>
<el-descriptions-item label="版本号">
<el-tag type="primary">{{ currentVersion.version }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="上传人">
{{ currentVersion.uploadUserName || '-' }}
<el-tag size="small" type="info" style="margin-left: 8px">{{ getRoleLabel(currentVersion.uploadRoleCode) }}</el-tag>
</el-descriptions-item>
<el-descriptions-item label="文件名" :span="2">
<el-link type="primary" :href="currentVersion.fileUrl" target="_blank">
<el-icon><Link /></el-icon>
{{ currentVersion.fileName }}
</el-link>
</el-descriptions-item>
<el-descriptions-item label="文件修改意见" :span="2">
{{ currentVersion.comment || '无' }}
</el-descriptions-item>
<el-descriptions-item label="上传时间" :span="2">
{{ currentVersion.createTime || '-' }}
</el-descriptions-item>
</el-descriptions>
<div class="file-actions">
<el-button type="primary" icon="Download" @click="handleDownload(currentVersion)">下载文件</el-button>
</div>
</div>
<el-empty v-else description="暂无招标文件,请上传" />
</el-tab-pane>
<!-- 版本历史 -->
<el-tab-pane label="版本历史" name="history">
<el-table :data="versionHistory" stripe border v-if="versionHistory.length > 0">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="version" label="版本" width="80" align="center">
<template #default="{ row }">
<el-tag :type="row.isCurrent === '1' ? 'success' : 'info'">{{ row.version }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="fileName" label="文件名" min-width="200" show-overflow-tooltip />
<el-table-column prop="uploadUserName" label="上传人" width="100" />
<el-table-column prop="uploadRoleCode" label="角色" width="120">
<template #default="{ row }">
{{ getRoleLabel(row.uploadRoleCode) }}
</template>
</el-table-column>
<el-table-column prop="comment" label="文件修改意见" min-width="150" show-overflow-tooltip>
<template #default="{ row }">
{{ row.comment || '-' }}
</template>
</el-table-column>
<el-table-column prop="createTime" label="上传时间" width="160" />
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="{ row }">
<el-button type="primary" link icon="Download" @click="handleDownload(row)">下载</el-button>
</template>
</el-table-column>
</el-table>
<el-empty v-else description="暂无版本历史" />
</el-tab-pane>
</el-tabs>
</el-card>
<!-- 上传招标文件流程中可编辑时显示 -->
<el-card v-if="showUploadSection" shadow="never" class="upload-card">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon><Upload /></el-icon>
上传招标文件
</span>
</div>
</template>
<el-form ref="uploadFormRef" :model="uploadForm" :rules="uploadRules" label-width="120px">
<el-form-item label="选择文件" prop="fileId">
<el-upload
ref="uploadRef"
:action="uploadUrl"
:headers="uploadHeaders"
:data="uploadData"
:before-upload="beforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:on-remove="handleUploadRemove"
:file-list="fileList"
accept=".doc,.docx"
:disabled="isViewMode"
>
<el-button type="primary" :disabled="isViewMode">选择文件</el-button>
<template #tip>
<div class="el-upload__tip">仅支持 docdocx 格式单文件不超过 50MB上传后自动保存</div>
</template>
</el-upload>
</el-form-item>
<el-form-item v-if="uploadForm.fileId" label="文件修改意见">
<el-input
v-model="uploadForm.comment"
type="textarea"
:rows="3"
placeholder="请输入文件修改意见或备注(可选)"
:disabled="isViewMode"
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item v-if="uploadForm.fileId" label="文件修改意见">
<el-input
v-model="uploadForm.comment"
type="textarea"
:rows="3"
placeholder="请输入文件修改意见或备注(可选)"
:disabled="isViewMode"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
</el-card>
<!-- 流转去向选择资产管理处审核时显示 -->
<el-card v-if="showFlowTargetSection" shadow="never" class="flow-target-card">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon><Guide /></el-icon>
流转去向
</span>
</div>
</template>
<el-form label-width="100px">
<el-form-item label="选择去向" required>
<el-radio-group v-model="flowTarget" :disabled="isViewMode">
<el-radio v-for="item in FLOW_TARGET_OPTIONS" :key="item.value" :label="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-card>
<!-- 部门参与人选择部门负责人审核时显示 -->
<el-card v-if="showRepresentorSection" shadow="never" class="representor-card">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon><User /></el-icon>
部门参与人
</span>
<el-tag v-if="applyData.representorTeacherNo" type="success">已设置</el-tag>
<el-tag v-else type="warning">待设置</el-tag>
</div>
</template>
<el-form label-width="120px">
<el-form-item label="选择方式">
<el-radio-group v-model="representorSelectMode" :disabled="isViewMode">
<el-radio label="designate">指定一人</el-radio>
<el-radio label="random">随机抽取</el-radio>
</el-radio-group>
</el-form-item>
<!-- 指定一人 -->
<el-form-item v-if="representorSelectMode === 'designate'" label="选择参与人">
<el-select
v-model="selectedRepresentor"
placeholder="请选择参与人"
filterable
:disabled="isViewMode"
style="width: 300px"
>
<el-option
v-for="member in deptMembers"
:key="member.teacherNo"
:label="`${member.realName} (${member.teacherNo})`"
:value="member.teacherNo"
/>
</el-select>
</el-form-item>
<!-- 随机抽取 -->
<el-form-item v-if="representorSelectMode === 'random'" label="选择候选人">
<el-select
v-model="randomCandidates"
placeholder="请选择参与随机抽取的人员(可多选)"
multiple
filterable
:disabled="isViewMode"
style="width: 400px"
>
<el-option
v-for="member in deptMembers"
:key="member.teacherNo"
:label="`${member.realName} (${member.teacherNo})`"
:value="member.teacherNo"
/>
</el-select>
<el-button
type="primary"
:loading="randomSelectLoading"
:disabled="randomCandidates.length < 2 || isViewMode"
style="margin-left: 12px"
@click="handleRandomSelect"
>
随机抽取
</el-button>
</el-form-item>
<!-- 已选中的参与人 -->
<el-form-item v-if="currentRepresentor" label="已选中参与人">
<el-tag type="success" size="large">
{{ currentRepresentor.realName }} ({{ currentRepresentor.teacherNo }})
</el-tag>
</el-form-item>
<!-- 参与人身份 -->
<el-form-item label="参与人身份" required>
<el-radio-group v-model="representorType" :disabled="isViewMode">
<el-radio label="purchase_rep">采购代表</el-radio>
<el-radio label="judge">评委</el-radio>
</el-radio-group>
</el-form-item>
<!-- 保存按钮 -->
<el-form-item>
<el-button
type="primary"
:loading="saveRepresentorLoading"
:disabled="!canSaveRepresentor || isViewMode"
@click="handleSaveRepresentor"
>
保存参与人信息
</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup lang="ts" name="BidFileAudit">
import { ref, reactive, computed, onMounted, watch } from 'vue';
import { useRoute } from 'vue-router';
import { Document, FolderOpened, Upload, Link, Guide, User } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { Session } from '/@/utils/storage';
import { useUserInfo } from '/@/stores/userInfo';
import other from '/@/utils/other';
import {
getBidFileVersionHistory,
getBidFileCurrentVersion,
agentUploadBidFile,
assetUploadBidFile,
uploadBidFileNewVersion,
getAgentPurchaseDetail,
getFlowPurchaseDetail,
tempStoreBidFile,
submitBidFile,
} from '/@/api/purchase/bidfile';
import { getRequirementFiles } from '/@/api/purchase/purchasingfiles';
import { currElTabIsSave } from '/@/api/order/order-key-vue';
import { getDeptMembers, saveRepresentor, randomSelectRepresentor } from '/@/api/purchase/purchasingrequisition';
// ==================== Props & Emits ====================
const props = defineProps({
currJob: { type: Object, default: null },
currElTab: { type: Object, default: null },
});
const emit = defineEmits(['handleJob']);
// ==================== 用户角色判断 ====================
const userStore = useUserInfo();
const currentUserRoleCodes = computed(() => userStore.userInfos.roleCodes || []);
// ==================== 常量定义 ====================
const ROLE_LABEL_MAP: Record<string, string> = {
PURCHASE_AGENT: '招标代理',
PURCHASE_ASSET: '资产管理处',
PURCHASE_DEPT_APPLY: '部门经办人',
PURCHASE_DEPT_AUDIT: '部门负责人',
PURCHASE_FILE_AUDIT: '内审部门',
PURCHASE_CENTER: '采购中心',
};
const PURCHASE_TYPE_MAP: Record<string, string> = {
'1': '网上商城',
'2': '市场采购',
'3': '商务洽谈',
'5': '询价',
'6': '单一来源',
'7': '竞争性谈判',
'8': '竞争性磋商',
'9': '公开招标',
'10': '邀请招标',
};
// 流转去向选项(资产管理处审核时使用)
const FLOW_TARGET_OPTIONS = [
{ label: '需求部门负责人', value: '4' },
{ label: '文件内审部门', value: '2' },
{ label: '招标代理', value: '1' },
{ label: '确认定稿', value: '3' },
] as const;
// ==================== 响应式数据 ====================
const route = useRoute();
const loading = ref(false);
// 流程相关
const isFlowEmbed = computed(() => !!props.currJob);
const isViewMode = computed(() => {
if (isFlowEmbed.value && props.currJob) {
return !!props.currJob.hiJob || props.currElTab?.isFormEdit === '0';
}
return route.query.mode === 'view';
});
// 当前用户角色判断从用户store获取
const isAgent = computed(() => currentUserRoleCodes.value.includes('PURCHASE_AGENT'));
const isAsset = computed(() => currentUserRoleCodes.value.includes('PURCHASE_ASSET'));
const isDeptApply = computed(() => currentUserRoleCodes.value.includes('PURCHASE_DEPT_APPLY'));
const isDeptAudit = computed(() => currentUserRoleCodes.value.includes('PURCHASE_DEPT_AUDIT'));
const isFileAudit = computed(() => currentUserRoleCodes.value.includes('PURCHASE_FILE_AUDIT'));
// 是否显示上传区域
const showUploadSection = computed(() => {
if (isViewMode.value) return false;
if (isAgent.value ) return true;
if (isAsset.value || isDeptApply.value || isFileAudit.value) return true;
return false;
});
// 是否显示流转去向选择区域(仅资产管理处在审核时显示)
const showFlowTargetSection = computed(() => {
if (isViewMode.value) return false;
return isAsset.value && isFlowEmbed.value;
});
// 是否显示部门参与人选择区域(仅部门负责人在审核时显示)
const showRepresentorSection = computed(() => {
if (isViewMode.value) return false;
return isDeptAudit.value && isFlowEmbed.value;
});
// 采购申请数据
const applyData = ref<any>({});
const purchaseTypeLabel = computed(() => {
const type = applyData.value.purchaseType;
return PURCHASE_TYPE_MAP[type] || type || '-';
});
// 流程状态
const flowStatus = computed(() => applyData.value.fileFlowStatus || '0');
// 招标文件
const activeTab = ref('current');
const currentVersion = ref<any>(null);
const versionHistory = ref<any[]>([]);
// 需求文件(招标代理可见)
const requirementFiles = ref<any[]>([]);
const requirementLoading = ref(false);
// 上传相关
const uploadFormRef = ref();
const uploadRef = ref();
const fileList = ref<any[]>([]);
const saveLoading = ref(false);
const tempStoreLoading = ref(false);
const submitLoading = ref(false);
const isTempStored = ref(false);
const uploadForm = reactive({
fileId: '',
fileName: '',
fileUrl: '',
comment: '',
});
const uploadRules = {
// 招标文件上传为可选,后续可补充上传
};
// 流转去向(资产管理处审核时使用)
const flowTarget = ref<string>('');
// 部门参与人选择相关(部门负责人审核时使用)
const representorSelectMode = ref<'designate' | 'random'>('designate');
const deptMembers = ref<any[]>([]);
const deptMembersLoading = ref(false);
const selectedRepresentor = ref<string>('');
const randomCandidates = ref<string[]>([]);
const currentRepresentor = ref<any>(null);
const representorType = ref<string>('purchase_rep');
const saveRepresentorLoading = ref(false);
const randomSelectLoading = ref(false);
const canSaveRepresentor = computed(() => {
if (representorSelectMode.value === 'designate') {
return !!selectedRepresentor.value;
}
return !!currentRepresentor.value;
});
// ==================== 计算属性 ====================
const BID_FILE_TYPE = '130';
const uploadUrl = computed(() => import.meta.env.VITE_API_URL + '/purchase/purchasingfiles/upload');
const uploadHeaders = computed(() => ({
Authorization: 'Bearer ' + Session.getToken(),
'TENANT-ID': Session.getTenant(),
}));
const uploadData = computed(() => ({
purchaseId: effectivePurchaseId.value,
fileType: BID_FILE_TYPE,
}));
// 当前采购申请ID
const effectivePurchaseId = computed(() => {
if (props.currJob?.orderId) {
return String(props.currJob.orderId);
}
return route.query.id as string || route.query.purchaseId as string || '';
});
// 是否可以提交(非查看模式即可提交,不强制要求上传文件)
const canSubmitFile = computed(() => {
return !isViewMode.value;
});
// ==================== 方法定义 ====================
// 获取角色标签
const getRoleLabel = (roleCode: string) => {
return ROLE_LABEL_MAP[roleCode] || roleCode || '-';
};
// 加载采购申请详情(根据角色调用不同接口)
const loadApplyData = async () => {
if (!effectivePurchaseId.value) return;
try {
loading.value = true;
let res: any;
// 招标代理只能看到有限信息
if (isAgent.value) {
res = await getAgentPurchaseDetail(effectivePurchaseId.value);
} else {
// 其他角色可以看到完整信息
res = await getFlowPurchaseDetail(effectivePurchaseId.value);
}
if (res.code === 0 && res.data) {
applyData.value = res.data;
isTempStored.value = res.data.fileFlowStatus === '-1';
}
} catch (e: any) {
ElMessage.error(e?.msg || '加载采购申请失败');
} finally {
loading.value = false;
}
};
// 加载招标文件
const loadBidFiles = async () => {
if (!effectivePurchaseId.value) return;
try {
const [currentRes, historyRes] = await Promise.all([
getBidFileCurrentVersion(effectivePurchaseId.value),
getBidFileVersionHistory(effectivePurchaseId.value),
]);
if (currentRes.code === 0) {
currentVersion.value = currentRes.data;
}
if (historyRes.code === 0) {
versionHistory.value = historyRes.data || [];
}
} catch (e: any) {
ElMessage.error(e?.msg || '加载招标文件失败');
}
};
// 加载需求文件(招标代理可见)
const loadRequirementFiles = async () => {
if (!isAgent.value || !effectivePurchaseId.value) return;
try {
requirementLoading.value = true;
const res = await getRequirementFiles(effectivePurchaseId.value);
if (res.code === 0) {
requirementFiles.value = res.data || [];
}
} catch (e: any) {
ElMessage.error(e?.msg || '加载需求文件失败');
} finally {
requirementLoading.value = false;
}
};
// 下载需求文件使用downBlobFile携带token
const handleDownloadRequirement = (row: any) => {
if (row.id) {
const downloadUrl = '/purchase/purchasingfiles/downloadById?fileId=' + row.id;
other.downBlobFile(downloadUrl, {}, row.fileTitle || '需求文件');
}
};
// 文件上传前校验
const beforeUpload = (file: any) => {
const isLt50M = file.size / 1024 / 1024 < 50;
if (!isLt50M) {
ElMessage.error('文件大小不能超过 50MB');
return false;
}
return true;
};
// 上传成功后自动保存
const handleUploadSuccess = async (response: any, file: any) => {
if (response.code === 0) {
uploadForm.fileId = response.data.id || response.data.fileName;
uploadForm.fileUrl = response.data.url || response.data.remark;
uploadForm.fileName = file.name;
// 自动保存招标文件
const saved = await saveUploadedFile();
if (saved) {
ElMessage.success('招标文件上传并保存成功');
}
} else {
ElMessage.error(response.msg || '文件上传失败');
fileList.value = [];
}
};
// 上传失败
const handleUploadError = () => {
ElMessage.error('文件上传失败');
fileList.value = [];
};
// 移除文件
const handleUploadRemove = () => {
uploadForm.fileId = '';
uploadForm.fileUrl = '';
uploadForm.fileName = '';
};
// 下载招标文件使用downBlobFile携带token
const handleDownload = (row: any) => {
if (row.fileId) {
const downloadUrl = '/purchase/purchasingfiles/downloadById?fileId=' + row.fileId;
other.downBlobFile(downloadUrl, {}, row.fileName || '招标文件');
}
};
// 手动保存上传的招标文件
const handleSaveBidFile = async () => {
if (!uploadForm.fileId) {
ElMessage.warning('请先上传招标文件');
return;
}
saveLoading.value = true;
try {
const saved = await saveUploadedFile();
if (saved) {
ElMessage.success('招标文件保存成功,版本已更新');
}
} finally {
saveLoading.value = false;
}
};
// 保存上传的招标文件
const saveUploadedFile = async () => {
if (!uploadForm.fileId) {
ElMessage.warning('请先上传招标文件');
return false;
}
const runJobId = props.currJob?.id || '';
const params = {
purchaseId: effectivePurchaseId.value,
fileId: uploadForm.fileId,
fileName: uploadForm.fileName,
fileUrl: uploadForm.fileUrl,
comment: uploadForm.comment,
runJobId,
};
try {
let res: any;
if (isAgent.value) {
res = await agentUploadBidFile(params);
} else if (isAsset.value) {
res = await assetUploadBidFile(params);
} else {
res = await uploadBidFileNewVersion(params);
}
if (res.code === 0) {
await loadBidFiles();
// 标记为已保存
if (props.currJob && props.currElTab?.id) {
currElTabIsSave(props.currJob, props.currElTab.id, true, emit);
}
// 清空表单和上传组件,允许继续上传新文件
uploadForm.fileId = '';
uploadForm.fileUrl = '';
uploadForm.fileName = '';
uploadForm.comment = '';
fileList.value = [];
uploadRef.value?.clearFiles();
return true;
} else {
ElMessage.error(res.msg || '保存失败');
return false;
}
} catch (e: any) {
ElMessage.error(e?.msg || '保存招标文件失败');
return false;
}
};
// ==================== 流程集成 ====================
// 流程保存回调 - 审核时自动调用暂存接口
const handleFlowSave = async () => {
// 资产管理处审核时,必须选择流转去向
if (showFlowTargetSection.value && !flowTarget.value) {
ElMessage.warning('请选择流转去向');
return false;
}
// 如果有待保存的文件,先保存文件
if (uploadForm.fileId) {
const saved = await saveUploadedFile();
if (!saved) return false;
}
// 自动调用暂存接口(可以没有文件)
try {
const params = {
purchaseId: effectivePurchaseId.value,
fileId: uploadForm.fileId || '',
fileName: uploadForm.fileName || '',
fileUrl: uploadForm.fileUrl || '',
comment: uploadForm.comment || '',
flowTarget: flowTarget.value || ''
};
const res = await tempStoreBidFile(params);
if (res.code === 0) {
await loadBidFiles();
// 标记为已保存
if (props.currJob && props.currElTab?.id) {
currElTabIsSave(props.currJob, props.currElTab.id, true, emit);
}
return true;
} else {
ElMessage.error(res.msg || '暂存失败');
return false;
}
} catch (e: any) {
ElMessage.error(e?.msg || '暂存失败');
return false;
}
};
// 注册流程回调
const registerFlowCallbacks = () => {
if (!isFlowEmbed.value) return;
if (props.currJob?.resolveSaves) {
props.currJob.resolveSaves.push(handleFlowSave);
}
};
// ==================== 部门参与人选择 ====================
const loadDeptMembers = async () => {
if (!showRepresentorSection.value) return;
try {
deptMembersLoading.value = true;
const res = await getDeptMembers();
if (res.code === 0 && res.data) {
deptMembers.value = res.data;
}
} catch (e: any) {
ElMessage.error(e?.msg || '加载部门人员失败');
} finally {
deptMembersLoading.value = false;
}
};
const handleRandomSelect = async () => {
if (randomCandidates.value.length < 2) {
ElMessage.warning('请至少选择2名候选人');
return;
}
try {
randomSelectLoading.value = true;
const res = await randomSelectRepresentor(
effectivePurchaseId.value,
randomCandidates.value.join(',')
);
if (res.code === 0 && res.data) {
currentRepresentor.value = res.data;
ElMessage.success(`随机抽取成功:${res.data.realName}`);
} else {
ElMessage.error(res.msg || '随机抽取失败');
}
} catch (e: any) {
ElMessage.error(e?.msg || '随机抽取失败');
} finally {
randomSelectLoading.value = false;
}
};
const handleSaveRepresentor = async () => {
if (!canSaveRepresentor.value) {
ElMessage.warning('请先选择参与人');
return;
}
if (!representorType.value) {
ElMessage.warning('请选择参与人身份');
return;
}
try {
saveRepresentorLoading.value = true;
let res: any;
if (representorSelectMode.value === 'designate') {
res = await saveRepresentor(
Number(effectivePurchaseId.value),
selectedRepresentor.value,
undefined,
representorType.value
);
} else {
res = await saveRepresentor(
Number(effectivePurchaseId.value),
currentRepresentor.value?.teacherNo,
randomCandidates.value.join(','),
representorType.value
);
}
if (res.code === 0) {
ElMessage.success('保存参与人信息成功');
await loadApplyData();
} else {
ElMessage.error(res.msg || '保存失败');
}
} catch (e: any) {
ElMessage.error(e?.msg || '保存失败');
} finally {
saveRepresentorLoading.value = false;
}
};
// ==================== 生命周期 ====================
onMounted(async () => {
await loadApplyData();
await loadBidFiles();
if (isAgent.value) {
await loadRequirementFiles();
}
if (showRepresentorSection.value) {
await loadDeptMembers();
}
registerFlowCallbacks();
});
watch(
() => props.currJob,
async (newVal) => {
if (newVal) {
await loadApplyData();
await loadBidFiles();
if (isAgent.value) {
await loadRequirementFiles();
}
if (showRepresentorSection.value) {
await loadDeptMembers();
}
registerFlowCallbacks();
}
},
{ immediate: false }
);
watch(
() => route.query.id,
async (newId) => {
if (newId && !isFlowEmbed.value) {
await loadApplyData();
await loadBidFiles();
if (isAgent.value) {
await loadRequirementFiles();
}
}
},
{ immediate: false }
);
</script>
<style scoped lang="scss">
.bidfile-audit-container {
padding: 16px;
min-height: 100%;
background: var(--el-fill-color-lighter);
}
.info-card,
.file-card,
.upload-card {
margin-bottom: 16px;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.card-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 16px;
font-weight: 600;
color: var(--el-text-color-primary);
}
.version-detail {
padding: 16px 0;
}
.file-actions {
margin-top: 16px;
display: flex;
gap: 12px;
}
:deep(.el-descriptions__label) {
font-weight: 500;
}
:deep(.el-upload__tip) {
color: var(--el-text-color-secondary);
font-size: 12px;
margin-top: 8px;
}
.save-tip {
margin-left: 12px;
color: var(--el-color-warning);
font-size: 13px;
}
.flow-target-card {
margin-bottom: 16px;
}
.flow-target-card :deep(.el-radio-group) {
display: flex;
flex-direction: column;
gap: 12px;
}
.flow-target-card :deep(.el-radio) {
height: auto;
line-height: 1.5;
}
</style>