采购更新
This commit is contained in:
@@ -90,6 +90,18 @@ export function assignAgent(applyId: number | string, mode: 'random' | 'designat
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送招标代理:将采购项目发送给已分配的招标代理
|
||||||
|
* @param applyId 采购申请ID
|
||||||
|
*/
|
||||||
|
export function sendToAgent(applyId: number | string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingapply/sendToAgent',
|
||||||
|
method: 'post',
|
||||||
|
data: { id: Number(applyId) }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改采购申请
|
* 修改采购申请
|
||||||
* @param obj 对象数据
|
* @param obj 对象数据
|
||||||
@@ -226,3 +238,74 @@ export function getFileApplyTemplateDownloadUrl(id: string | number) {
|
|||||||
return `/purchase/purchasingapply/export-file-apply-template?id=${encodeURIComponent(String(id))}`;
|
return `/purchase/purchasingapply/export-file-apply-template?id=${encodeURIComponent(String(id))}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 招标代理专用接口 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标代理获取待处理列表
|
||||||
|
* @param params 分页参数
|
||||||
|
*/
|
||||||
|
export function getAgentPendingList(params?: any) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingdoc/agent/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标代理获取采购需求文件列表
|
||||||
|
* @param applyId 采购申请ID
|
||||||
|
*/
|
||||||
|
export function getAgentRequirementFiles(applyId: number | string) {
|
||||||
|
return request({
|
||||||
|
url: `/purchase/purchasingdoc/agent/requirement/${applyId}`,
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标代理获取项目详情(仅返回采购编号和项目名称)
|
||||||
|
* @param applyId 采购申请ID
|
||||||
|
*/
|
||||||
|
export function getAgentApplyDetail(applyId: number | string) {
|
||||||
|
return request({
|
||||||
|
url: `/purchase/purchasingdoc/agent/detail/${applyId}`,
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标代理上传采购文件
|
||||||
|
* @param data 文件数据
|
||||||
|
*/
|
||||||
|
export function uploadAgentDoc(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingdoc/upload',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 招标代理重新上传采购文件
|
||||||
|
* @param data 文件数据
|
||||||
|
*/
|
||||||
|
export function reuploadAgentDoc(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingdoc/reupload',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取采购文件列表
|
||||||
|
* @param applyId 采购申请ID
|
||||||
|
*/
|
||||||
|
export function getDocList(applyId: number | string) {
|
||||||
|
return request({
|
||||||
|
url: `/purchase/purchasingdoc/list/${applyId}`,
|
||||||
|
method: 'get'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,16 +93,16 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8" class="mb12" >
|
<el-col :span="8" class="mb12" >
|
||||||
<el-form-item label="采购方式" prop="purchaseType" :required="!isEntrustCenterChannel">
|
<el-form-item label="采购方式" prop="purchaseType" :required="!isEntrustCenterChannel">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="dataForm.purchaseType"
|
v-model="dataForm.purchaseType"
|
||||||
placeholder="请选择采购方式"
|
placeholder="请选择采购方式"
|
||||||
clearable
|
clearable
|
||||||
:disabled="(isFlowEmbed && isPurchaseCenter) ? false : (isDeptSelfMallLocked || isAutoSelectPurchaseType || isEntrustCenterChannel)"
|
:disabled="(isFlowEmbed && isPurchaseCenter) ? false : (isDeptSelfMallLocked || isAutoSelectPurchaseType || isEntrustCenterChannel)"
|
||||||
style="width: 100%">
|
style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in purchaseTypeDeptList"
|
v-for="item in purchaseTypeDeptOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value" />
|
:value="item.value" />
|
||||||
</el-select>
|
</el-select>
|
||||||
<div
|
<div
|
||||||
@@ -215,7 +215,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8" class="mb12" >
|
<el-col :span="8" class="mb12" >
|
||||||
<el-form-item label="采购方式" prop="purchaseType" :required="!isDeptPurchase">
|
<el-form-item label="采购方式" prop="purchaseType" :required="!isDeptPurchase">
|
||||||
<el-select v-model="dataForm.purchaseType" placeholder="请选择采购方式" clearable :disabled="(isFlowEmbed && isPurchaseCenter) ? false : (isAutoSelectPurchaseTypeUnion || flowFieldDisabled('purchaseType'))" style="width: 100%">
|
<el-select v-model="dataForm.purchaseType" placeholder="请选择采购方式" clearable :disabled="(isFlowEmbed && isPurchaseCenter) ? false : (isAutoSelectPurchaseTypeUnion || flowFieldDisabled('purchaseType') || !isPurchaseCenter)" style="width: 100%">
|
||||||
<el-option v-for="item in purchaseTypeUnionList" :key="item.value" :label="item.label" :value="item.value" />
|
<el-option v-for="item in purchaseTypeUnionList" :key="item.value" :label="item.label" :value="item.value" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -640,6 +640,8 @@ const fundSourceList = ref<any[]>([]);
|
|||||||
const isCentralizedList = ref<any[]>([]);
|
const isCentralizedList = ref<any[]>([]);
|
||||||
const isSpecialList = ref<any[]>([]);
|
const isSpecialList = ref<any[]>([]);
|
||||||
const purchaseTypeDeptList = ref<any[]>([]);
|
const purchaseTypeDeptList = ref<any[]>([]);
|
||||||
|
/** 部门采购方式字典(委托采购中心采购时使用) */
|
||||||
|
const purchaseTypeDeptDelegationList = ref<any[]>([]);
|
||||||
const purchaseModeSchoolList = ref<any[]>([]);
|
const purchaseModeSchoolList = ref<any[]>([]);
|
||||||
const purchaseTypeUnionList = ref<any[]>([]);
|
const purchaseTypeUnionList = ref<any[]>([]);
|
||||||
const businessDeptList = ref<any[]>([]);
|
const businessDeptList = ref<any[]>([]);
|
||||||
@@ -974,12 +976,6 @@ const isAutoSelectPurchaseTypeUnion = computed(() => {
|
|||||||
watch(
|
watch(
|
||||||
[() => dataForm.categoryCode, () => dataForm.budget, () => isDeptPurchase.value, () => isFlowEmbed.value, () => dataForm.purchaseChannel, () => isPurchaseCenter.value],
|
[() => dataForm.categoryCode, () => dataForm.budget, () => isDeptPurchase.value, () => isFlowEmbed.value, () => dataForm.purchaseChannel, () => isPurchaseCenter.value],
|
||||||
() => {
|
() => {
|
||||||
// 学校统一采购申请阶段:采购方式隐藏,由审批环节采购中心补充,此处不自动写入且清空已有值
|
|
||||||
if (!isDeptPurchase.value && !isFlowEmbed.value) {
|
|
||||||
dataForm.purchaseType = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 部门自行采购 & 采购途径为”委托采购中心采购”且为新增申请阶段:采购方式隐藏且不设置
|
// 部门自行采购 & 采购途径为”委托采购中心采购”且为新增申请阶段:采购方式隐藏且不设置
|
||||||
// 注意:查看模式和编辑模式不清空已有的采购方式
|
// 注意:查看模式和编辑模式不清空已有的采购方式
|
||||||
if (isDeptPurchase.value && isEntrustCenterChannel.value && !isFlowEmbed.value && !isViewMode.value && !isEditMode.value) {
|
if (isDeptPurchase.value && isEntrustCenterChannel.value && !isFlowEmbed.value && !isViewMode.value && !isEditMode.value) {
|
||||||
@@ -1219,10 +1215,10 @@ async function loadDetail(applyId: string | number) {
|
|||||||
budget: detail.budget != null ? Number(detail.budget) : null,
|
budget: detail.budget != null ? Number(detail.budget) : null,
|
||||||
isCentralized: detail.isCentralized != null ? String(detail.isCentralized) : '',
|
isCentralized: detail.isCentralized != null ? String(detail.isCentralized) : '',
|
||||||
isSpecial: detail.isSpecial != null ? String(detail.isSpecial) : '',
|
isSpecial: detail.isSpecial != null ? String(detail.isSpecial) : '',
|
||||||
purchaseMode: detail.purchaseMode ?? '',
|
purchaseMode: detail.purchaseMode != null ? String(detail.purchaseMode) : '',
|
||||||
purchaseType: detail.purchaseType === DEPT_PURCHASE_TYPE.ENTRUST_CENTER ? '' : (detail.purchaseType ?? ''),
|
purchaseType: detail.purchaseType === DEPT_PURCHASE_TYPE.ENTRUST_CENTER ? '' : (detail.purchaseType != null ? String(detail.purchaseType) : (detail.purchaseTypeUnion != null ? String(detail.purchaseTypeUnion) : '')),
|
||||||
purchaseChannel: (detail as any).purchaseChannel ?? (detail.purchaseType === DEPT_PURCHASE_TYPE.ENTRUST_CENTER ? PURCHASE_CHANNEL.ENTRUST_CENTER : ''),
|
purchaseChannel: (detail as any).purchaseChannel ?? (detail.purchaseType === DEPT_PURCHASE_TYPE.ENTRUST_CENTER ? PURCHASE_CHANNEL.ENTRUST_CENTER : ''),
|
||||||
purchaseTypeUnion: detail.purchaseTypeUnion ?? '',
|
purchaseTypeUnion: detail.purchaseTypeUnion != null ? String(detail.purchaseTypeUnion) : '',
|
||||||
categoryCode: detail.categoryCode ?? '',
|
categoryCode: detail.categoryCode ?? '',
|
||||||
remark: detail.remark ?? '',
|
remark: detail.remark ?? '',
|
||||||
status: detail.status ?? '',
|
status: detail.status ?? '',
|
||||||
@@ -1480,7 +1476,7 @@ const getIsSpecialDict = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取部门采购方式字典(过滤掉“委托采购中心采购”,由采购途径字段控制)
|
// 获取部门采购方式字典(过滤掉”委托采购中心采购”,由采购途径字段控制)
|
||||||
const getPurchaseTypeDeptDict = async () => {
|
const getPurchaseTypeDeptDict = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await getDicts('PURCHASE_TYPE_DEPT');
|
const res = await getDicts('PURCHASE_TYPE_DEPT');
|
||||||
@@ -1498,6 +1494,30 @@ const getPurchaseTypeDeptDict = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取部门采购方式字典(委托采购中心采购时使用)
|
||||||
|
const getPurchaseTypeDeptDelegationDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getDicts('PURCHASE_TYPE_DEPT_DELEGATION');
|
||||||
|
purchaseTypeDeptDelegationList.value = res.data && Array.isArray(res.data)
|
||||||
|
? res.data.map((item: any) => ({
|
||||||
|
id: item.id,
|
||||||
|
label: item.label || item.dictLabel || item.name,
|
||||||
|
value: item.value || item.dictValue || item.code,
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
} catch (err) {
|
||||||
|
purchaseTypeDeptDelegationList.value = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 部门采购方式下拉选项:根据采购途径动态切换字典 */
|
||||||
|
const purchaseTypeDeptOptions = computed(() => {
|
||||||
|
if (isEntrustCenterChannel.value) {
|
||||||
|
return purchaseTypeDeptDelegationList.value;
|
||||||
|
}
|
||||||
|
return purchaseTypeDeptList.value;
|
||||||
|
});
|
||||||
|
|
||||||
// 获取学校采购形式字典
|
// 获取学校采购形式字典
|
||||||
const getPurchaseModeSchoolDict = async () => {
|
const getPurchaseModeSchoolDict = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -1946,6 +1966,7 @@ onMounted(async () => {
|
|||||||
getIsCentralizedDict(),
|
getIsCentralizedDict(),
|
||||||
getIsSpecialDict(),
|
getIsSpecialDict(),
|
||||||
getPurchaseTypeDeptDict(),
|
getPurchaseTypeDeptDict(),
|
||||||
|
getPurchaseTypeDeptDelegationDict(),
|
||||||
getPurchaseModeSchoolDict(),
|
getPurchaseModeSchoolDict(),
|
||||||
getPurchaseTypeUnionDict(),
|
getPurchaseTypeUnionDict(),
|
||||||
getBusinessDeptListData(),
|
getBusinessDeptListData(),
|
||||||
|
|||||||
@@ -0,0 +1,288 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="visible"
|
||||||
|
:title="'处理项目 - ' + (projectInfo.purchaseNo || '')"
|
||||||
|
width="900px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
destroy-on-close
|
||||||
|
class="agent-doc-dialog">
|
||||||
|
<el-tabs v-model="activeTab" type="border-card">
|
||||||
|
<!-- Tab 1: 项目基本信息 -->
|
||||||
|
<el-tab-pane label="项目信息" name="info">
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="采购编号">{{ projectInfo.purchaseNo || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="项目名称">{{ projectInfo.projectName || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="文件状态">
|
||||||
|
<el-tag :type="getStatusType(projectInfo.status)">{{ getStatusLabel(projectInfo.status) }}</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- Tab 2: 采购需求文件 -->
|
||||||
|
<el-tab-pane label="采购需求文件" name="requirement">
|
||||||
|
<el-table :data="requirementFiles" v-loading="requirementLoading" stripe>
|
||||||
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||||
|
<el-table-column prop="fileName" label="文件名称" min-width="200" show-overflow-tooltip />
|
||||||
|
<el-table-column label="操作" width="120" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button type="primary" link icon="Download" @click="handleDownload(scope.row)">下载</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-empty v-if="!requirementLoading && requirementFiles.length === 0" description="暂无采购需求文件" />
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- Tab 3: 上传招标文件 -->
|
||||||
|
<el-tab-pane label="上传招标文件" name="upload">
|
||||||
|
<div class="upload-section">
|
||||||
|
<el-alert type="info" :closable="false" class="mb-4">
|
||||||
|
<template #title>
|
||||||
|
<span>当前状态:<el-tag :type="getStatusType(projectInfo.status)" size="small">{{ getStatusLabel(projectInfo.status) }}</el-tag></span>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
|
||||||
|
<!-- 文件上传 -->
|
||||||
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
|
:action="uploadAction"
|
||||||
|
:headers="uploadHeaders"
|
||||||
|
:on-success="handleUploadSuccess"
|
||||||
|
:on-error="handleUploadError"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
:file-list="fileList"
|
||||||
|
:auto-upload="false"
|
||||||
|
:limit="1"
|
||||||
|
accept=".doc,.docx,.pdf"
|
||||||
|
class="upload-demo">
|
||||||
|
<el-button type="primary">选择文件</el-button>
|
||||||
|
<template #tip>
|
||||||
|
<div class="el-upload__tip">支持 .doc, .docx, .pdf 格式,文件大小不超过 50MB</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
:loading="uploadSubmitting"
|
||||||
|
:disabled="fileList.length === 0 || !canUpload"
|
||||||
|
@click="handleUploadSubmit"
|
||||||
|
class="mt-4">
|
||||||
|
{{ projectInfo.status === 'RETURNED' ? '重新上传招标文件' : '上传招标文件' }}
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-alert v-if="!canUpload" type="warning" :closable="false" class="mt-4">
|
||||||
|
<template #title>
|
||||||
|
<span v-if="projectInfo.status === 'ASSET_REVIEWING'">文件审核中,请等待审核结果</span>
|
||||||
|
<span v-else-if="projectInfo.status === 'DEPT_REVIEWING'">文件审核中,请等待审核结果</span>
|
||||||
|
<span v-else-if="projectInfo.status === 'AUDIT_REVIEWING'">文件审核中,请等待审核结果</span>
|
||||||
|
<span v-else-if="projectInfo.status === 'ASSET_CONFIRMING'">文件审核中,请等待确认</span>
|
||||||
|
<span v-else-if="projectInfo.status === 'COMPLETED'">文件审核已完成</span>
|
||||||
|
<span v-else>当前状态不允许上传</span>
|
||||||
|
</template>
|
||||||
|
</el-alert>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleClose">关闭</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="AgentDocDialog">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { Session } from '/@/utils/storage'
|
||||||
|
import { getAgentRequirementFiles, getAgentApplyDetail, uploadAgentDoc, reuploadAgentDoc, getDocList } from '/@/api/finance/purchasingrequisition'
|
||||||
|
import type { UploadInstance, UploadProps, UploadUserFile } from 'element-plus'
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const activeTab = ref('info')
|
||||||
|
const projectInfo = ref<any>({})
|
||||||
|
const requirementFiles = ref<any[]>([])
|
||||||
|
const requirementLoading = ref(false)
|
||||||
|
const uploadSubmitting = ref(false)
|
||||||
|
const uploadRef = ref<UploadInstance>()
|
||||||
|
const fileList = ref<UploadUserFile[]>([])
|
||||||
|
|
||||||
|
// 上传配置
|
||||||
|
const uploadAction = computed(() => {
|
||||||
|
const baseUrl = import.meta.env.VITE_API_URL || ''
|
||||||
|
return `${baseUrl}/admin/sys-file/upload`
|
||||||
|
})
|
||||||
|
|
||||||
|
const uploadHeaders = computed(() => {
|
||||||
|
const token = Session.getToken()
|
||||||
|
return {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
'TENANT-ID': Session.getTenant() || '1'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 是否可以上传
|
||||||
|
const canUpload = computed(() => {
|
||||||
|
const status = projectInfo.value.status
|
||||||
|
return status === 'PENDING_UPLOAD' || status === 'RETURNED'
|
||||||
|
})
|
||||||
|
|
||||||
|
const open = async (row: any) => {
|
||||||
|
projectInfo.value = { ...row }
|
||||||
|
visible.value = true
|
||||||
|
activeTab.value = 'info'
|
||||||
|
fileList.value = []
|
||||||
|
await loadProjectDetail()
|
||||||
|
await loadRequirementFiles()
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadProjectDetail = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getAgentApplyDetail(projectInfo.value.applyId)
|
||||||
|
if (res?.data) {
|
||||||
|
projectInfo.value = { ...projectInfo.value, ...res.data }
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('加载项目详情失败', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadRequirementFiles = async () => {
|
||||||
|
requirementLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getAgentRequirementFiles(projectInfo.value.applyId)
|
||||||
|
requirementFiles.value = res?.data || []
|
||||||
|
} catch (e: any) {
|
||||||
|
requirementFiles.value = []
|
||||||
|
} finally {
|
||||||
|
requirementLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDownload = (row: any) => {
|
||||||
|
// TODO: 实现下载功能
|
||||||
|
const downloadUrl = `/purchase/purchasingdoc/download/${row.id}`
|
||||||
|
window.open(downloadUrl, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
||||||
|
const allowedTypes = ['application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/pdf']
|
||||||
|
const allowedExtensions = ['.doc', '.docx', '.pdf']
|
||||||
|
const fileExt = rawFile.name.substring(rawFile.name.lastIndexOf('.')).toLowerCase()
|
||||||
|
|
||||||
|
if (!allowedTypes.includes(rawFile.type) && !allowedExtensions.includes(fileExt)) {
|
||||||
|
useMessage().error('只能上传 .doc, .docx, .pdf 格式的文件')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawFile.size / 1024 / 1024 > 50) {
|
||||||
|
useMessage().error('文件大小不能超过 50MB')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUploadSuccess: UploadProps['onSuccess'] = (response: any, uploadFile: any) => {
|
||||||
|
if (response?.code === 0 || response?.code === 200) {
|
||||||
|
const fileUrl = response.data?.url || response.data?.fileUrl || response.data?.filePath
|
||||||
|
const fileName = uploadFile.name
|
||||||
|
submitUpload(fileName, fileUrl)
|
||||||
|
} else {
|
||||||
|
useMessage().error(response?.msg || '上传失败')
|
||||||
|
uploadSubmitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUploadError: UploadProps['onError'] = (error: any) => {
|
||||||
|
useMessage().error('文件上传失败:' + (error?.message || '未知错误'))
|
||||||
|
uploadSubmitting.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitUpload = async (fileName: string, filePath: string) => {
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
applyId: projectInfo.value.applyId,
|
||||||
|
fileName,
|
||||||
|
filePath
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projectInfo.value.status === 'RETURNED') {
|
||||||
|
await reuploadAgentDoc(data)
|
||||||
|
} else {
|
||||||
|
await uploadAgentDoc(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
useMessage().success('招标文件上传成功')
|
||||||
|
emit('refresh')
|
||||||
|
await loadProjectDetail()
|
||||||
|
fileList.value = []
|
||||||
|
} catch (e: any) {
|
||||||
|
useMessage().error(e?.msg || '上传失败')
|
||||||
|
} finally {
|
||||||
|
uploadSubmitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUploadSubmit = async () => {
|
||||||
|
if (fileList.value.length === 0) {
|
||||||
|
useMessage().warning('请先选择文件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadSubmitting.value = true
|
||||||
|
uploadRef.value?.submit()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusType = (status: string) => {
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
'PENDING_UPLOAD': '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': '待上传',
|
||||||
|
'ASSET_REVIEWING': '资产管理处审核中',
|
||||||
|
'DEPT_REVIEWING': '需求部门审核中',
|
||||||
|
'AUDIT_REVIEWING': '内审部门审核中',
|
||||||
|
'ASSET_CONFIRMING': '资产管理处确认中',
|
||||||
|
'COMPLETED': '已完成',
|
||||||
|
'RETURNED': '已退回'
|
||||||
|
}
|
||||||
|
return labelMap[status] || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.agent-doc-dialog {
|
||||||
|
:deep(.el-dialog__body) {
|
||||||
|
padding: 16px 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-section {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-4 {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-4 {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
166
src/views/finance/purchasingrequisition/agentDoc/index.vue
Normal file
166
src/views/finance/purchasingrequisition/agentDoc/index.vue
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
<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>
|
||||||
|
<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"><DocumentCopy /></el-icon>
|
||||||
|
招标代理工作台
|
||||||
|
</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 prop="status" label="文件状态" width="140" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag :type="getStatusType(scope.row.status)">
|
||||||
|
{{ getStatusLabel(scope.row.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</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="handleView(scope.row)">
|
||||||
|
处理
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<!-- 处理弹窗 -->
|
||||||
|
<AgentDocDialog ref="agentDocDialogRef" @refresh="getDataList" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="PurchasingAgentDoc">
|
||||||
|
import { ref, reactive, defineAsyncComponent, onMounted } from 'vue'
|
||||||
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
|
import { useMessage } from "/@/hooks/message";
|
||||||
|
import { getAgentPendingList } from "/@/api/finance/purchasingrequisition";
|
||||||
|
import { Search, DocumentCopy, List } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
// 引入组件
|
||||||
|
const AgentDocDialog = defineAsyncComponent(() => import('./AgentDocDialog.vue'));
|
||||||
|
|
||||||
|
const agentDocDialogRef = ref()
|
||||||
|
const searchFormRef = ref()
|
||||||
|
const showSearch = ref(true)
|
||||||
|
|
||||||
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
pageList: getAgentPendingList,
|
||||||
|
queryForm: {
|
||||||
|
purchaseNo: '',
|
||||||
|
projectName: '',
|
||||||
|
},
|
||||||
|
createdIsNeed: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const { getDataList, tableStyle, sizeChangeHandle, currentChangeHandle } = useTable(state);
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
searchFormRef.value?.resetFields();
|
||||||
|
getDataList();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusType = (status: string) => {
|
||||||
|
const typeMap: Record<string, string> = {
|
||||||
|
'PENDING_UPLOAD': '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': '待上传',
|
||||||
|
'ASSET_REVIEWING': '资产管理处审核中',
|
||||||
|
'DEPT_REVIEWING': '需求部门审核中',
|
||||||
|
'AUDIT_REVIEWING': '内审部门审核中',
|
||||||
|
'ASSET_CONFIRMING': '资产管理处确认中',
|
||||||
|
'COMPLETED': '已完成',
|
||||||
|
'RETURNED': '已退回'
|
||||||
|
};
|
||||||
|
return labelMap[status] || '-';
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleView = (row: any) => {
|
||||||
|
agentDocDialogRef.value?.open(row);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 页面加载时的初始化逻辑
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
@@ -38,6 +38,10 @@
|
|||||||
<el-form-item v-if="agentMode === 'random'">
|
<el-form-item v-if="agentMode === 'random'">
|
||||||
<el-button type="primary" :loading="assignAgentSubmitting" :disabled="!!assignedAgentName" @click="handleAssignAgentRandom">随机分配</el-button>
|
<el-button type="primary" :loading="assignAgentSubmitting" :disabled="!!assignedAgentName" @click="handleAssignAgentRandom">随机分配</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<!-- 发送招标代理按钮 -->
|
||||||
|
<el-form-item v-if="canSendToAgent">
|
||||||
|
<el-button type="success" :loading="sendToAgentSubmitting" @click="handleSendToAgent">发送招标代理</el-button>
|
||||||
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 仅部门审核角色显示:采购代表相关 -->
|
<!-- 仅部门审核角色显示:采购代表相关 -->
|
||||||
@@ -71,7 +75,7 @@
|
|||||||
<script setup lang="ts" name="PurchasingImplement">
|
<script setup lang="ts" name="PurchasingImplement">
|
||||||
import { ref, computed, onMounted, watch, onUnmounted } from 'vue'
|
import { ref, computed, onMounted, watch, onUnmounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { getDeptMembers, getObj, assignAgent } from '/@/api/finance/purchasingrequisition'
|
import { getDeptMembers, getObj, assignAgent, sendToAgent } from '/@/api/finance/purchasingrequisition'
|
||||||
import { getPage as getAgentPage } from '/@/api/finance/purchaseagent'
|
import { getPage as getAgentPage } from '/@/api/finance/purchaseagent'
|
||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { Session } from '/@/utils/storage'
|
import { Session } from '/@/utils/storage'
|
||||||
@@ -129,6 +133,7 @@ const agentListLoading = ref(false)
|
|||||||
const assignAgentSubmitting = ref(false)
|
const assignAgentSubmitting = ref(false)
|
||||||
const rollingAgentName = ref<string>('')
|
const rollingAgentName = ref<string>('')
|
||||||
const assignedAgentName = ref<string>('')
|
const assignedAgentName = ref<string>('')
|
||||||
|
const sendToAgentSubmitting = ref(false)
|
||||||
let rollInterval: ReturnType<typeof setInterval> | null = null
|
let rollInterval: ReturnType<typeof setInterval> | null = null
|
||||||
|
|
||||||
/** 是否可以分配代理:委托代理采购 且 (学校统一采购 或 部门自行采购且委托采购中心采购) */
|
/** 是否可以分配代理:委托代理采购 且 (学校统一采购 或 部门自行采购且委托采购中心采购) */
|
||||||
@@ -140,6 +145,16 @@ const canAssignAgent = computed(() => {
|
|||||||
return row.purchaseMode === '2' || (row.purchaseMode === '0' && row.purchaseType === '4')
|
return row.purchaseMode === '2' || (row.purchaseMode === '0' && row.purchaseType === '4')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/** 是否可以发送招标代理:委托代理采购 且 已分配代理 且 未发送 */
|
||||||
|
const canSendToAgent = computed(() => {
|
||||||
|
// 自行组织采购不需要发送
|
||||||
|
if (implementType.value !== '2') return false
|
||||||
|
const row = applyRow.value
|
||||||
|
if (!row) return false
|
||||||
|
// 已分配代理 且 未发送
|
||||||
|
return !!row.agentId && row.agentSent !== '1'
|
||||||
|
})
|
||||||
|
|
||||||
const loadAgentList = async () => {
|
const loadAgentList = async () => {
|
||||||
if (!canAssignAgent.value) return
|
if (!canAssignAgent.value) return
|
||||||
agentListLoading.value = true
|
agentListLoading.value = true
|
||||||
@@ -233,6 +248,25 @@ const handleAssignAgentDesignated = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 发送招标代理 */
|
||||||
|
const handleSendToAgent = async () => {
|
||||||
|
const id = applyRow.value?.id ?? applyId.value
|
||||||
|
if (!id) {
|
||||||
|
useMessage().warning('无法获取申请单ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sendToAgentSubmitting.value = true
|
||||||
|
try {
|
||||||
|
await sendToAgent(Number(id))
|
||||||
|
useMessage().success('已发送招标代理')
|
||||||
|
await loadData()
|
||||||
|
} catch (e: any) {
|
||||||
|
useMessage().error(e?.msg || '发送招标代理失败')
|
||||||
|
} finally {
|
||||||
|
sendToAgentSubmitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const isInIframe = () => typeof window !== 'undefined' && window.self !== window.top
|
const isInIframe = () => typeof window !== 'undefined' && window.self !== window.top
|
||||||
|
|
||||||
const postMessage = (type: string, payload?: any) => {
|
const postMessage = (type: string, payload?: any) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user