fix
This commit is contained in:
144
src/api/purchase/bidfile.ts
Normal file
144
src/api/purchase/bidfile.ts
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import request from '/@/utils/request';
|
||||||
|
|
||||||
|
export function startBidFileFlow(purchaseId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/startFlow',
|
||||||
|
method: 'post',
|
||||||
|
params: { purchaseId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBidFileTodoList(pageNum: number, pageSize: number) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/todoList',
|
||||||
|
method: 'get',
|
||||||
|
params: { pageNum, pageSize },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBidFileTaskDetail(runJobId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/taskDetail',
|
||||||
|
method: 'get',
|
||||||
|
params: { runJobId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function agentUploadBidFile(data: {
|
||||||
|
purchaseId: string;
|
||||||
|
fileId: string;
|
||||||
|
fileName: string;
|
||||||
|
fileUrl: string;
|
||||||
|
comment?: string;
|
||||||
|
runJobId: string;
|
||||||
|
}) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/agentUpload',
|
||||||
|
method: 'post',
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assetUploadBidFile(data: {
|
||||||
|
purchaseId: string;
|
||||||
|
fileId: string;
|
||||||
|
fileName: string;
|
||||||
|
fileUrl: string;
|
||||||
|
comment?: string;
|
||||||
|
runJobId: string;
|
||||||
|
}) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/assetUpload',
|
||||||
|
method: 'post',
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function uploadBidFileNewVersion(data: {
|
||||||
|
purchaseId: string;
|
||||||
|
fileId: string;
|
||||||
|
fileName: string;
|
||||||
|
fileUrl: string;
|
||||||
|
comment?: string;
|
||||||
|
runJobId: string;
|
||||||
|
}) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/uploadNewVersion',
|
||||||
|
method: 'post',
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submitBidFileTask(data: {
|
||||||
|
runJobId: string;
|
||||||
|
to?: number;
|
||||||
|
comment?: string;
|
||||||
|
}) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/submit',
|
||||||
|
method: 'post',
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function confirmBidFileFinalize(runJobId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/confirmFinalize',
|
||||||
|
method: 'post',
|
||||||
|
params: { runJobId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBidFileVersionHistory(purchaseId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/versionHistory',
|
||||||
|
method: 'get',
|
||||||
|
params: { purchaseId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBidFileCurrentVersion(purchaseId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/currentVersion',
|
||||||
|
method: 'get',
|
||||||
|
params: { purchaseId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAgentPurchaseDetail(purchaseId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/agentPurchaseDetail',
|
||||||
|
method: 'get',
|
||||||
|
params: { purchaseId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFlowPurchaseDetail(purchaseId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/flowPurchaseDetail',
|
||||||
|
method: 'get',
|
||||||
|
params: { purchaseId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tempStoreBidFile(data: {
|
||||||
|
purchaseId: string;
|
||||||
|
fileId?: string;
|
||||||
|
fileName?: string;
|
||||||
|
fileUrl?: string;
|
||||||
|
comment?: string;
|
||||||
|
}) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/tempStore',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submitBidFile(purchaseId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingbidfile/submitFile',
|
||||||
|
method: 'post',
|
||||||
|
params: { purchaseId },
|
||||||
|
});
|
||||||
|
}
|
||||||
744
src/views/purchase/purchasingrequisition/bidfile-audit.vue
Normal file
744
src/views/purchase/purchasingrequisition/bidfile-audit.vue
Normal file
@@ -0,0 +1,744 @@
|
|||||||
|
<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">仅支持 doc、docx 格式,单文件不超过 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>
|
||||||
|
</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 } 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';
|
||||||
|
|
||||||
|
// ==================== 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_FILE_AUDIT: '内审部门',
|
||||||
|
PURCHASE_CENTER: '采购中心',
|
||||||
|
};
|
||||||
|
|
||||||
|
const PURCHASE_TYPE_MAP: Record<string, string> = {
|
||||||
|
'1': '网上商城',
|
||||||
|
'2': '市场采购',
|
||||||
|
'3': '商务洽谈',
|
||||||
|
'5': '询价',
|
||||||
|
'6': '单一来源',
|
||||||
|
'7': '竞争性谈判',
|
||||||
|
'8': '竞争性磋商',
|
||||||
|
'9': '公开招标',
|
||||||
|
'10': '邀请招标',
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 响应式数据 ====================
|
||||||
|
|
||||||
|
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 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 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 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 handleTempStoreBidFile = async () => {
|
||||||
|
if (!uploadForm.fileId) {
|
||||||
|
ElMessage.warning('请先上传招标文件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tempStoreLoading.value = true;
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
purchaseId: effectivePurchaseId.value,
|
||||||
|
fileId: uploadForm.fileId,
|
||||||
|
fileName: uploadForm.fileName,
|
||||||
|
fileUrl: uploadForm.fileUrl,
|
||||||
|
comment: uploadForm.comment,
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await tempStoreBidFile(params);
|
||||||
|
|
||||||
|
if (res.code === 0) {
|
||||||
|
await loadBidFiles();
|
||||||
|
isTempStored.value = true;
|
||||||
|
ElMessage.success('暂存成功,可继续上传或提交');
|
||||||
|
// 清空表单和上传组件,允许继续上传新文件
|
||||||
|
uploadForm.fileId = '';
|
||||||
|
uploadForm.fileUrl = '';
|
||||||
|
uploadForm.fileName = '';
|
||||||
|
uploadForm.comment = '';
|
||||||
|
fileList.value = [];
|
||||||
|
uploadRef.value?.clearFiles();
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || '暂存失败');
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.msg || '暂存招标文件失败');
|
||||||
|
} finally {
|
||||||
|
tempStoreLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交招标文件(启动流程)
|
||||||
|
const handleSubmitBidFile = async () => {
|
||||||
|
submitLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await submitBidFile(effectivePurchaseId.value);
|
||||||
|
|
||||||
|
if (res.code === 0) {
|
||||||
|
await loadApplyData();
|
||||||
|
await loadBidFiles();
|
||||||
|
isTempStored.value = false;
|
||||||
|
ElMessage.success('提交成功,流程已启动');
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.msg || '提交失败');
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.msg || '提交招标文件失败');
|
||||||
|
} finally {
|
||||||
|
submitLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 流程集成 ====================
|
||||||
|
|
||||||
|
// 流程保存回调 - 审核时自动调用暂存接口
|
||||||
|
const handleFlowSave = async () => {
|
||||||
|
// 如果有待保存的文件,先保存文件
|
||||||
|
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 || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 生命周期 ====================
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await loadApplyData();
|
||||||
|
await loadBidFiles();
|
||||||
|
if (isAgent.value) {
|
||||||
|
await loadRequirementFiles();
|
||||||
|
}
|
||||||
|
registerFlowCallbacks();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.currJob,
|
||||||
|
async (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
await loadApplyData();
|
||||||
|
await loadBidFiles();
|
||||||
|
if (isAgent.value) {
|
||||||
|
await loadRequirementFiles();
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user