This commit is contained in:
吴红兵
2026-03-08 15:17:13 +08:00
parent a0218f1c8f
commit 5a53648ee7
10 changed files with 61 additions and 2207 deletions

View File

@@ -1,307 +0,0 @@
/*
* 招标文件处理统一API
* 整合招标代理和审核部门的接口调用
*/
import request from '/@/utils/request';
/**
* 获取列表数据(根据模式调用不同接口)
* @param mode 模式agent-招标代理audit-审核部门
* @param params 分页参数
*/
export function getDocProcessList(mode: string, params?: any) {
const url = mode === 'agent' ? '/purchase/purchasingdoc/agent/list' : '/purchase/purchasingdoc/audit/page';
return request({
url,
method: 'get',
params,
});
}
/**
* 获取采购需求文件列表(招标代理专用)
* @param applyId 采购申请ID
*/
export function getRequirementFiles(applyId: number | string) {
return request({
url: `/purchase/purchasingdoc/agent/requirement/${applyId}`,
method: 'get',
});
}
/**
* 获取采购需求文件列表(审核人员专用)
* @param applyId 采购申请ID
*/
export function getRequirementFilesForAudit(applyId: number | string) {
return request({
url: `/purchase/purchasingdoc/audit/requirement/${applyId}`,
method: 'get',
});
}
/**
* 获取招标文件列表
* @param applyId 采购申请ID
*/
export function getDocList(applyId: number | string) {
return request({
url: `/purchase/purchasingdoc/list/${applyId}`,
method: 'get',
});
}
/**
* 上传招标文件(招标代理)
* @param data 文件数据
*/
export function uploadDoc(data: any) {
return request({
url: '/purchase/purchasingdoc/upload',
method: 'post',
data,
});
}
/**
* 重新上传招标文件
* @param data 文件数据
*/
export function reuploadDoc(data: any) {
return request({
url: '/purchase/purchasingdoc/reupload',
method: 'post',
data,
});
}
/**
* 获取招标文件下载地址
* @param id 招标文件ID
*/
export function getDocDownloadUrl(id: number | string) {
return `/purchase/purchasingdoc/download/${id}`;
}
/**
* 下载招标文件返回blob
* @param id 招标文件ID
*/
export function downloadDocById(id: number | string) {
return request({
url: `/purchase/purchasingdoc/download/${id}`,
method: 'get',
responseType: 'blob',
});
}
/**
* 根据文件ID下载采购附件返回blob
* @param fileId 文件ID
*/
export function downloadFileById(fileId: string | number) {
return request({
url: '/purchase/purchasingfiles/downloadById',
method: 'get',
params: { fileId },
responseType: 'blob',
});
}
/**
* 确认无误
* @param data 审核信息
*/
export function confirmDoc(data: any) {
return request({
url: '/purchase/purchasingdoc/confirm',
method: 'post',
data,
});
}
/**
* 退回修改
* @param data 审核信息
*/
export function returnDoc(data: any) {
return request({
url: '/purchase/purchasingdoc/return',
method: 'post',
data,
});
}
/**
* 确认流程结束
* @param applyId 采购申请ID
*/
export function completeDoc(applyId: number | string) {
return request({
url: '/purchase/purchasingdoc/complete',
method: 'post',
params: { applyId },
});
}
/**
* 获取审核记录
* @param applyId 采购申请ID
*/
export function getAuditRecords(applyId: number | string) {
return request({
url: `/purchase/purchasingdoc/audit-records/${applyId}`,
method: 'get',
});
}
/**
* 获取可执行操作
* @param applyId 采购申请ID
*/
export function getAvailableActions(applyId: number | string) {
return request({
url: `/purchase/purchasingdoc/actions/${applyId}`,
method: 'get',
});
}
/**
* 获取采购申请附件列表
* @param purchaseId 采购申请ID
*/
export function getApplyFiles(purchaseId: string | number) {
return request({
url: '/purchase/purchasingfiles/applyFiles',
method: 'post',
params: { purchaseId },
});
}
/**
* 获取文件上传地址
*/
export function getFileUploadUrl() {
const baseUrl = import.meta.env.VITE_API_URL || '';
return `${baseUrl}/purchase/purchasingfiles/upload`;
}
/**
* 保存草稿(招标代理)
* @param data 文件信息
*/
export function saveDraft(data: any) {
return request({
url: '/purchase/purchasingdoc/save-draft',
method: 'post',
data,
});
}
/**
* 提交草稿(招标代理)
* @param data 文件信息
*/
export function submitDraft(data: any) {
return request({
url: '/purchase/purchasingdoc/submit-draft',
method: 'post',
data,
});
}
/**
* 补充上传(资产管理处)
* @param data 文件信息含fileRemark
*/
export function supplyUpload(data: any) {
return request({
url: '/purchase/purchasingdoc/supply-upload',
method: 'post',
data,
});
}
/**
* 提交至需求部门(资产管理处)
* @param data 审核信息
*/
export function submitToDept(data: any) {
return request({
url: '/purchase/purchasingdoc/submit-to-dept',
method: 'post',
data,
});
}
/**
* 提交至内审部门(资产管理处)
* @param data 审核信息
*/
export function submitToAudit(data: any) {
return request({
url: '/purchase/purchasingdoc/submit-to-audit',
method: 'post',
data,
});
}
/**
* 提交至资产管理处(需求部门/内审部门)
* @param data 审核信息
*/
export function submitToAsset(data: any) {
return request({
url: '/purchase/purchasingdoc/submit-to-asset',
method: 'post',
data,
});
}
/**
* 定稿(资产管理处)
* @param data 审核信息
*/
export function finalizeDoc(data: any) {
return request({
url: '/purchase/purchasingdoc/finalize',
method: 'post',
data,
});
}
/**
* 获取采购代表设置信息
* @param applyId 采购申请ID
*/
export function getReviewerSetting(applyId: string | number) {
return request({
url: `/purchase/purchasingdoc/reviewer/${applyId}`,
method: 'get',
});
}
/**
* 设置采购代表
* @param data 设置信息
*/
export function setReviewerSetting(data: any) {
return request({
url: '/purchase/purchasingdoc/reviewer/set',
method: 'post',
data,
});
}
/**
* 随机抽取采购代表
* @param data 候选人列表
*/
export function randomSelectReviewer(data: any) {
return request({
url: '/purchase/purchasingdoc/reviewer/random',
method: 'post',
data,
});
}

View File

@@ -33,3 +33,35 @@ export function getFileTypes() {
export function getDeptSelfMeetingFiletype() { export function getDeptSelfMeetingFiletype() {
return '140'; return '140';
} }
/**
* 获取 120 采购需求表文件类型
*/
export function getPurchaseRequirementFiletype() {
return '120';
}
/**
* 按文件类型获取采购附件列表
* @param purchaseId 采购申请ID
* @param fileType 文件类型(可选)
*/
export function getFilesByType(purchaseId: string, fileType?: string) {
return request({
url: '/purchase/purchasingfiles/listByType',
method: 'get',
params: { purchaseId, fileType },
});
}
/**
* 获取采购需求文件列表(招标代理可见)
* @param purchaseId 采购申请ID
*/
export function getRequirementFiles(purchaseId: string) {
return request({
url: '/purchase/purchasingfiles/listByType',
method: 'get',
params: { purchaseId, fileType: '120' },
});
}

View File

@@ -31,6 +31,7 @@ const init = () => {
let flowInstId = props.currJob.flowInstId; let flowInstId = props.currJob.flowInstId;
let runJobId = props.currJob.id; let runJobId = props.currJob.id;
src.value = props.currElTab.path + `?token=${token}&tenantId=${tenantId}&flowInstId=${flowInstId}&runJobId=${runJobId}`; src.value = props.currElTab.path + `?token=${token}&tenantId=${tenantId}&flowInstId=${flowInstId}&runJobId=${runJobId}`;
console.log(src.value)
}; };
function handleJob(jobBtn) { function handleJob(jobBtn) {

View File

@@ -21,6 +21,7 @@ const modules: Record<string, () => Promise<unknown>> = import.meta.glob([
'../../views/purchase/*/*.vue', '../../views/purchase/*/*.vue',
'../../views/purchase/purchasingrequisition/add.vue', '../../views/purchase/purchasingrequisition/add.vue',
'../../views/purchase/purchasingrequisition/implement.vue', '../../views/purchase/purchasingrequisition/implement.vue',
'../../views/purchase/purchasingrequisition/bidfile-audit.vue',
]); ]);
/** /**

View File

@@ -107,6 +107,14 @@ export const staticRoutes: Array<RouteRecordRaw> = [
isAuth: true, isAuth: true,
}, },
}, },
{
path: '/purchase/purchasingrequisition/bidfile-audit',
name: 'purchase.purchasingrequisition.bidfile-audit',
component: () => import('/@/views/purchase/purchasingrequisition/bidfile-audit.vue'),
meta: {
isAuth: true,
},
},
...staticRoutesFlow, ...staticRoutesFlow,
]; ];

View File

@@ -1,69 +0,0 @@
<template>
<el-table :data="records" stripe v-loading="loading" max-height="400">
<el-table-column prop="operateTypeDesc" label="操作类型" width="100" align="center">
<template #default="scope">
<el-tag :type="getOperateTypeStyle(scope.row.operateType)">
{{ scope.row.operateTypeDesc }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="operateRoleDesc" label="操作角色" width="100" />
<el-table-column prop="operateByName" label="操作人" width="100" />
<el-table-column prop="currentVersion" label="文件版本" width="80" align="center" />
<el-table-column prop="remark" label="批注意见" min-width="200" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.remark || '-' }}
</template>
</el-table-column>
<el-table-column prop="operateTime" label="操作时间" width="160" />
</el-table>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { getAuditRecords } from '/@/api/purchase/docProcess';
const props = defineProps<{
applyId: number | string;
}>();
const records = ref<any[]>([]);
const loading = ref(false);
const loadRecords = async () => {
if (!props.applyId) return;
loading.value = true;
try {
const res = await getAuditRecords(props.applyId);
records.value = res.data || [];
} catch (e) {
records.value = [];
} finally {
loading.value = false;
}
};
const refresh = () => {
loadRecords();
};
const getOperateTypeStyle = (type: string) => {
const styleMap: Record<string, string> = {
UPLOAD: 'primary',
CONFIRM: 'success',
RETURN: 'warning',
COMPLETE: 'success',
};
return styleMap[type] || 'info';
};
watch(
() => props.applyId,
() => {
loadRecords();
},
{ immediate: true }
);
defineExpose({ refresh });
</script>

View File

@@ -1,397 +0,0 @@
<template>
<div class="reviewer-setting">
<el-form :model="formData" label-width="100px" v-loading="loading">
<!-- 当前采购代表 -->
<el-form-item label="当前代表" v-if="currentRepresentor.teacherName">
<el-tag type="success">{{ currentRepresentor.teacherName }} ({{ currentRepresentor.teacherNo }})</el-tag>
<el-tag v-if="currentRepresentor.representorType" type="info" style="margin-left: 8px">{{
getRepresentorTypeLabel(currentRepresentor.representorType)
}}</el-tag>
</el-form-item>
<!-- 选择方式 -->
<el-form-item label="选择方式">
<el-radio-group v-model="formData.selectMode" @change="handleSelectModeChange">
<el-radio label="DESIGNATED">指定人员</el-radio>
<el-radio label="RANDOM">随机抽取</el-radio>
</el-radio-group>
</el-form-item>
<!-- 指定人员模式 -->
<el-form-item v-if="formData.selectMode === 'DESIGNATED'" label="选择人员" required>
<org-selector v-model:orgList="selectedUserList" type="user" :multiple="false" @update:orgList="handleUserChange" />
</el-form-item>
<!-- 随机抽取模式 -->
<template v-if="formData.selectMode === 'RANDOM'">
<el-form-item label="选择候选人" required>
<org-selector v-model:orgList="candidateUserList" type="user" :multiple="true" @update:orgList="handleCandidateChange" />
</el-form-item>
<!-- 候选人列表 -->
<el-form-item label="候选人列表" v-if="candidates.length > 0">
<el-table :data="candidates" stripe size="small" max-height="200">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="teacherNo" label="工号" width="120" />
<el-table-column prop="teacherName" label="姓名" />
</el-table>
</el-form-item>
<!-- 随机抽取结果 -->
<el-form-item label="抽取结果">
<div class="random-roller">
<span v-if="rollingName" class="rolling">{{ rollingName }}</span>
<span v-else-if="selectedCandidate.teacherName" class="selected">
已抽取{{ selectedCandidate.teacherName }} ({{ selectedCandidate.teacherNo }})
</span>
<span v-else class="placeholder">点击下方按钮进行随机抽取</span>
</div>
</el-form-item>
<!-- 随机抽取按钮 -->
<el-form-item v-if="candidates.length > 1">
<el-button type="primary" :loading="rolling" @click="handleRandomSelect">
{{ rolling ? '抽取中...' : '随机抽取' }}
</el-button>
</el-form-item>
</template>
<!-- 人员类型选择 -->
<el-form-item label="人员类型">
<el-select v-model="formData.representorType" placeholder="请选择人员类型" clearable style="width: 200px">
<el-option v-for="item in representorTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 保存按钮 -->
<el-form-item>
<el-button type="primary" :loading="saving" :disabled="!canSave" @click="handleSave"> 保存设置 </el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup lang="ts" name="ReviewerSetting">
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useMessage } from '/@/hooks/message';
import { useDict } from '/@/hooks/dict';
import { getReviewerSetting, setReviewerSetting, randomSelectReviewer } from '/@/api/purchase/docProcess';
import orgSelector from '/@/components/OrgSelector/index.vue';
const props = defineProps<{
applyId: string | number;
}>();
const emit = defineEmits<{
(e: 'saved'): void;
}>();
// 常量
const SELECT_MODE = {
DESIGNATED: 'DESIGNATED',
RANDOM: 'RANDOM',
} as const;
// 字典数据
const { PURCHASE_REPRESENTOR_TYPE: representorTypeDict } = useDict('PURCHASE_REPRESENTOR_TYPE');
// 人员类型选项
const representorTypeOptions = computed(() => {
return (representorTypeDict.value || []).map((item: any) => ({
value: item.value,
label: item.label,
}));
});
// 表单数据
const formData = ref({
selectMode: 'DESIGNATED' as 'DESIGNATED' | 'RANDOM',
teacherNo: '',
teacherName: '',
candidates: [] as Array<{ teacherNo: string; teacherName: string }>,
representorType: '',
});
// 当前采购代表(已有设置)
const currentRepresentor = ref({
teacherNo: '',
teacherName: '',
representorType: '',
});
// 用户选择相关
const selectedUserList = ref<any[]>([]);
const candidateUserList = ref<any[]>([]);
// 候选人列表
const candidates = ref<Array<{ teacherNo: string; teacherName: string }>>([]);
// 随机抽取相关
const rolling = ref(false);
const rollingName = ref('');
const selectedCandidate = ref<{ teacherNo: string; teacherName: string }>({ teacherNo: '', teacherName: '' });
let rollInterval: ReturnType<typeof setInterval> | null = null;
// 加载状态
const loading = ref(false);
const saving = ref(false);
// 是否可以保存
const canSave = computed(() => {
if (formData.value.selectMode === SELECT_MODE.DESIGNATED) {
return formData.value.teacherNo && formData.value.teacherName;
} else {
return formData.value.teacherNo && formData.value.teacherName && candidates.value.length > 0;
}
});
// 处理选择方式变化
const handleSelectModeChange = () => {
formData.value.teacherNo = '';
formData.value.teacherName = '';
formData.value.candidates = [];
selectedUserList.value = [];
candidateUserList.value = [];
candidates.value = [];
selectedCandidate.value = { teacherNo: '', teacherName: '' };
rollingName.value = '';
};
// 获取人员类型标签
const getRepresentorTypeLabel = (value: string): string => {
const option = representorTypeOptions.value.find((item: any) => item.value === value);
return option ? option.label : value;
};
// 处理指定人员变化
const handleUserChange = (list: any[]) => {
if (list && list.length > 0) {
const user = list[0];
formData.value.teacherNo = user.username || user.userName || '';
formData.value.teacherName = user.name || user.realName || '';
} else {
formData.value.teacherNo = '';
formData.value.teacherName = '';
}
};
// 处理候选人变化
const handleCandidateChange = (list: any[]) => {
candidates.value = (list || []).map((user) => ({
teacherNo: user.username || user.userName || '',
teacherName: user.name || user.realName || '',
}));
// 重置已选结果
if (candidates.value.length > 0) {
selectedCandidate.value = { ...candidates.value[0] };
formData.value.teacherNo = selectedCandidate.value.teacherNo;
formData.value.teacherName = selectedCandidate.value.teacherName;
} else {
selectedCandidate.value = { teacherNo: '', teacherName: '' };
formData.value.teacherNo = '';
formData.value.teacherName = '';
}
};
// 随机抽取动画
const startRollingAnimation = (finalCandidate: { teacherNo: string; teacherName: string }) => {
if (candidates.value.length === 0) return;
if (rollInterval) {
clearInterval(rollInterval);
rollInterval = null;
}
rollingName.value = '';
rolling.value = true;
let currentIndex = 0;
const totalDuration = 2000;
const intervalTime = 80;
rollInterval = setInterval(() => {
rollingName.value = candidates.value[currentIndex].teacherName;
currentIndex = (currentIndex + 1) % candidates.value.length;
}, intervalTime);
setTimeout(() => {
if (rollInterval) {
clearInterval(rollInterval);
rollInterval = null;
}
rolling.value = false;
rollingName.value = '';
selectedCandidate.value = finalCandidate;
formData.value.teacherNo = finalCandidate.teacherNo;
formData.value.teacherName = finalCandidate.teacherName;
}, totalDuration);
};
// 执行随机抽取
const handleRandomSelect = async () => {
if (candidates.value.length < 2) {
useMessage().warning('请至少选择2位候选人');
return;
}
rolling.value = true;
try {
const res = await randomSelectReviewer({
applyId: props.applyId,
selectMode: SELECT_MODE.RANDOM,
candidates: candidates.value,
});
const result = res?.data || res;
if (result?.teacherNo) {
startRollingAnimation({
teacherNo: result.teacherNo,
teacherName: result.teacherName || '',
});
}
} catch (e: any) {
rolling.value = false;
useMessage().error(e?.msg || '随机抽取失败');
}
};
// 保存设置
const handleSave = async () => {
if (!canSave.value) {
useMessage().warning('请完善设置信息');
return;
}
saving.value = true;
try {
const params: any = {
applyId: props.applyId,
selectMode: formData.value.selectMode,
teacherNo: formData.value.teacherNo,
teacherName: formData.value.teacherName,
representorType: formData.value.representorType,
};
if (formData.value.selectMode === SELECT_MODE.RANDOM) {
params.candidates = candidates.value;
}
await setReviewerSetting(params);
useMessage().success('保存成功');
emit('saved');
await loadData();
} catch (e: any) {
useMessage().error(e?.msg || '保存失败');
} finally {
saving.value = false;
}
};
// 加载数据
const loadData = async () => {
if (!props.applyId) return;
loading.value = true;
try {
const res = await getReviewerSetting(props.applyId);
const data = res?.data || res;
if (data) {
currentRepresentor.value = {
teacherNo: data.teacherNo || '',
teacherName: data.teacherName || '',
representorType: data.representorType || '',
};
formData.value.selectMode = data.selectMode || SELECT_MODE.DESIGNATED;
formData.value.teacherNo = data.teacherNo || '';
formData.value.teacherName = data.teacherName || '';
formData.value.representorType = data.representorType || '';
if (data.candidateList && data.candidateList.length > 0) {
candidates.value = data.candidateList;
formData.value.candidates = data.candidateList;
// 回显候选人
candidateUserList.value = data.candidateList.map((c: any) => ({
username: c.teacherNo,
userName: c.teacherNo,
name: c.teacherName,
realName: c.teacherName,
}));
}
if (data.teacherNo && data.selectMode === SELECT_MODE.DESIGNATED) {
selectedUserList.value = [
{
username: data.teacherNo,
userName: data.teacherNo,
name: data.teacherName,
realName: data.teacherName,
},
];
}
if (data.selectMode === SELECT_MODE.RANDOM && data.teacherNo) {
selectedCandidate.value = {
teacherNo: data.teacherNo,
teacherName: data.teacherName || '',
};
}
}
} catch (_) {
// 忽略错误
} finally {
loading.value = false;
}
};
onMounted(() => {
loadData();
});
onUnmounted(() => {
if (rollInterval) {
clearInterval(rollInterval);
rollInterval = null;
}
});
</script>
<style scoped lang="scss">
.reviewer-setting {
padding: 16px;
}
.random-roller {
padding: 12px 16px;
background: var(--el-fill-color-light);
border-radius: 4px;
min-height: 40px;
display: flex;
align-items: center;
.rolling {
font-size: 16px;
font-weight: 500;
color: var(--el-color-primary);
animation: blink 0.1s infinite;
}
.selected {
font-size: 16px;
font-weight: 600;
color: var(--el-color-success);
}
.placeholder {
color: var(--el-text-color-placeholder);
}
}
@keyframes blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
</style>

View File

@@ -1,241 +0,0 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<!-- 搜索表单卡片 -->
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
<el-form-item label="采购编号" prop="purchaseNo">
<el-input v-model="state.queryForm.purchaseNo" placeholder="请输入采购编号" clearable style="width: 200px" />
</el-form-item>
<el-form-item label="项目名称" prop="projectName">
<el-input v-model="state.queryForm.projectName" placeholder="请输入项目名称" clearable style="width: 200px" />
</el-form-item>
<!-- 审核状态筛选 - 仅审核模式显示 -->
<el-form-item v-if="mode === 'audit'" label="审核状态" prop="docAuditStatus">
<el-select v-model="state.queryForm.docAuditStatus" placeholder="请选择审核状态" clearable style="width: 200px">
<el-option label="待上传" value="PENDING_UPLOAD" />
<el-option label="草稿" value="DRAFT" />
<el-option label="资产管理处审核中" value="ASSET_REVIEWING" />
<el-option label="需求部门审核中" value="DEPT_REVIEWING" />
<el-option label="内审部门审核中" value="AUDIT_REVIEWING" />
<el-option label="资产管理处确认中" value="ASSET_CONFIRMING" />
<el-option label="已完成" value="COMPLETED" />
<el-option label="已退回" value="RETURNED" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 内容卡片 -->
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon">
<component :is="titleIcon" />
</el-icon>
{{ pageTitle }}
</span>
<div class="header-actions">
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<!-- 表格 -->
<el-table
ref="tableRef"
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
>
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
</el-table-column>
<el-table-column prop="purchaseNo" label="采购编号" min-width="140" show-overflow-tooltip />
<el-table-column prop="projectName" label="项目名称" min-width="200" show-overflow-tooltip />
<!-- 需求部门 - 仅审核模式显示 -->
<el-table-column v-if="mode === 'audit'" prop="deptName" label="需求部门" min-width="150" show-overflow-tooltip />
<!-- 预算金额 - 仅审核模式显示 -->
<el-table-column v-if="mode === 'audit'" prop="budget" label="预算金额(元)" width="120" align="right">
<template #default="scope">
{{ scope.row.budget ? Number(scope.row.budget).toLocaleString() : '-' }}
</template>
</el-table-column>
<!-- 文件状态 -->
<el-table-column :prop="statusProp" label="文件状态" width="140" align="center">
<template #default="scope">
<el-tag :type="getStatusType(getRowStatus(scope.row))">
{{ getStatusLabel(getRowStatus(scope.row)) }}
</el-tag>
</template>
</el-table-column>
<!-- 当前版本 - 仅审核模式显示 -->
<el-table-column v-if="mode === 'audit'" prop="currentDocVersion" label="当前版本" width="100" align="center">
<template #default="scope">
{{ scope.row.currentDocVersion || '-' }}
</template>
</el-table-column>
<!-- 操作 -->
<el-table-column label="操作" align="center" fixed="right" width="120">
<template #default="scope">
<el-button type="primary" link icon="View" @click="handleProcess(scope.row)">
{{ actionLabel }}
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-if="state.pagination && state.pagination.total && state.pagination.total > 0"
:total="state.pagination.total"
:current="state.pagination.current"
:size="state.pagination.size"
@sizeChange="sizeChangeHandle"
@currentChange="currentChangeHandle"
/>
</el-card>
</div>
<!-- 处理弹窗 -->
<!-- {{mode}}-->
<DocProcessDialog ref="docProcessDialogRef" :mode="mode" @refresh="getDataList" />
</div>
</template>
<script setup lang="ts" name="PurchasingDocProcess">
import { ref, reactive, computed, defineAsyncComponent, onMounted } from 'vue';
import { BasicTableProps, useTable } from '/@/hooks/table';
import { getDocProcessList } from '/@/api/purchase/docProcess';
import { Search, DocumentCopy, DocumentChecked, List } from '@element-plus/icons-vue';
import { useUserInfo } from '/@/stores/userInfo';
// 引入组件
const DocProcessDialog = defineAsyncComponent(() => import('./DocProcessDialog.vue'));
const userInfoStore = useUserInfo();
const docProcessDialogRef = ref();
const searchFormRef = ref();
const showSearch = ref(true);
// 从用户角色自动判断模式
const mode = computed(() => {
const roleCodes = userInfoStore.userInfos.roleCodes || [];
// 有 PURCHASE_AGENT 角色则显示代理模式
if (roleCodes.includes('PURCHASE_AGENT')) {
return 'agent';
}
// 其他情况显示审核模式
return 'audit';
});
// 页面标题
const pageTitle = computed(() => {
return mode.value === 'agent' ? '招标代理工作台' : '招标文件审核';
});
// 标题图标
const titleIcon = computed(() => {
return mode.value === 'agent' ? DocumentCopy : DocumentChecked;
});
// 操作按钮标签
const actionLabel = computed(() => {
return mode.value === 'agent' ? '处理' : '审核';
});
// 状态字段
const statusProp = computed(() => {
return mode.value === 'agent' ? 'status' : 'docAuditStatus';
});
// 查询表单
const getQueryForm = () => {
const base: any = {
purchaseNo: '',
projectName: '',
};
if (mode.value === 'audit') {
base.docAuditStatus = '';
}
return base;
};
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: (params?: any) => getDocProcessList(mode.value, params),
queryForm: getQueryForm(),
createdIsNeed: true,
});
const { getDataList, tableStyle, sizeChangeHandle, currentChangeHandle } = useTable(state);
const handleReset = () => {
searchFormRef.value?.resetFields();
getDataList();
};
// 获取行状态(兼容两种字段名)
const getRowStatus = (row: any) => {
return mode.value === 'agent' ? row.status : row.docAuditStatus;
};
const getStatusType = (status: string) => {
const typeMap: Record<string, string> = {
PENDING_UPLOAD: 'info',
DRAFT: 'info',
ASSET_REVIEWING: 'warning',
DEPT_REVIEWING: 'warning',
AUDIT_REVIEWING: 'warning',
ASSET_CONFIRMING: 'primary',
COMPLETED: 'success',
RETURNED: 'danger',
};
return typeMap[status] || 'info';
};
const getStatusLabel = (status: string) => {
const labelMap: Record<string, string> = {
PENDING_UPLOAD: '待上传',
DRAFT: '草稿',
ASSET_REVIEWING: '资产管理处审核中',
DEPT_REVIEWING: '需求部门审核中',
AUDIT_REVIEWING: '内审部门审核中',
ASSET_CONFIRMING: '资产管理处确认中',
COMPLETED: '已完成',
RETURNED: '已退回',
};
return labelMap[status] || '-';
};
const handleProcess = (row: any) => {
docProcessDialogRef.value?.open(row);
};
// 监听路由参数变化,重新加载数据
onMounted(() => {
// 重置查询表单
state.queryForm = getQueryForm();
});
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -89,6 +89,7 @@
<script setup lang="ts" name="PurchasingImplement"> <script setup lang="ts" name="PurchasingImplement">
import { ref, computed, onMounted, onUnmounted } from 'vue'; import { ref, computed, onMounted, onUnmounted } from 'vue';
import { ElMessageBox } from 'element-plus';
import { getObj, assignAgent, sendToAgent, revokeAgent, saveImplementType } from '/@/api/purchase/purchasingrequisition'; import { getObj, assignAgent, sendToAgent, revokeAgent, saveImplementType } from '/@/api/purchase/purchasingrequisition';
import { getPage as getAgentPage } from '/@/api/purchase/purchaseagent'; import { getPage as getAgentPage } from '/@/api/purchase/purchaseagent';
import { useMessage } from '/@/hooks/message'; import { useMessage } from '/@/hooks/message';
@@ -276,13 +277,30 @@ const handleAssignAgentDesignated = async () => {
} }
}; };
/** 发送招标代理 */ /** 发送招标代理 - 带确认弹窗 */
const handleSendToAgent = async () => { const handleSendToAgent = async () => {
const id = applyRow.value?.id ?? applyId.value; const id = applyRow.value?.id ?? applyId.value;
if (!id) { if (!id) {
useMessage().warning('无法获取申请单ID'); useMessage().warning('无法获取申请单ID');
return; return;
} }
// 确认弹窗
try {
await ElMessageBox.confirm(
'是否确认发送至招标代理启动招标文件审核流程?',
'确认发送',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
);
} catch {
// 用户取消
return;
}
sendToAgentSubmitting.value = true; sendToAgentSubmitting.value = true;
try { try {
await sendToAgent(id); await sendToAgent(id);