采购更新
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 对象数据
|
||||
@@ -226,3 +238,74 @@ export function getFileApplyTemplateDownloadUrl(id: string | number) {
|
||||
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 :span="8" class="mb12" >
|
||||
<el-form-item label="采购方式" prop="purchaseType" :required="!isEntrustCenterChannel">
|
||||
<el-select
|
||||
v-model="dataForm.purchaseType"
|
||||
placeholder="请选择采购方式"
|
||||
<el-select
|
||||
v-model="dataForm.purchaseType"
|
||||
placeholder="请选择采购方式"
|
||||
clearable
|
||||
:disabled="(isFlowEmbed && isPurchaseCenter) ? false : (isDeptSelfMallLocked || isAutoSelectPurchaseType || isEntrustCenterChannel)"
|
||||
style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in purchaseTypeDeptList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
<el-option
|
||||
v-for="item in purchaseTypeDeptOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
<div
|
||||
@@ -215,7 +215,7 @@
|
||||
</el-col>
|
||||
<el-col :span="8" class="mb12" >
|
||||
<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-select>
|
||||
</el-form-item>
|
||||
@@ -640,6 +640,8 @@ const fundSourceList = ref<any[]>([]);
|
||||
const isCentralizedList = ref<any[]>([]);
|
||||
const isSpecialList = ref<any[]>([]);
|
||||
const purchaseTypeDeptList = ref<any[]>([]);
|
||||
/** 部门采购方式字典(委托采购中心采购时使用) */
|
||||
const purchaseTypeDeptDelegationList = ref<any[]>([]);
|
||||
const purchaseModeSchoolList = ref<any[]>([]);
|
||||
const purchaseTypeUnionList = ref<any[]>([]);
|
||||
const businessDeptList = ref<any[]>([]);
|
||||
@@ -974,12 +976,6 @@ const isAutoSelectPurchaseTypeUnion = computed(() => {
|
||||
watch(
|
||||
[() => 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) {
|
||||
@@ -1219,10 +1215,10 @@ async function loadDetail(applyId: string | number) {
|
||||
budget: detail.budget != null ? Number(detail.budget) : null,
|
||||
isCentralized: detail.isCentralized != null ? String(detail.isCentralized) : '',
|
||||
isSpecial: detail.isSpecial != null ? String(detail.isSpecial) : '',
|
||||
purchaseMode: detail.purchaseMode ?? '',
|
||||
purchaseType: detail.purchaseType === DEPT_PURCHASE_TYPE.ENTRUST_CENTER ? '' : (detail.purchaseType ?? ''),
|
||||
purchaseMode: detail.purchaseMode != null ? String(detail.purchaseMode) : '',
|
||||
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 : ''),
|
||||
purchaseTypeUnion: detail.purchaseTypeUnion ?? '',
|
||||
purchaseTypeUnion: detail.purchaseTypeUnion != null ? String(detail.purchaseTypeUnion) : '',
|
||||
categoryCode: detail.categoryCode ?? '',
|
||||
remark: detail.remark ?? '',
|
||||
status: detail.status ?? '',
|
||||
@@ -1480,7 +1476,7 @@ const getIsSpecialDict = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取部门采购方式字典(过滤掉“委托采购中心采购”,由采购途径字段控制)
|
||||
// 获取部门采购方式字典(过滤掉”委托采购中心采购”,由采购途径字段控制)
|
||||
const getPurchaseTypeDeptDict = async () => {
|
||||
try {
|
||||
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 () => {
|
||||
try {
|
||||
@@ -1946,6 +1966,7 @@ onMounted(async () => {
|
||||
getIsCentralizedDict(),
|
||||
getIsSpecialDict(),
|
||||
getPurchaseTypeDeptDict(),
|
||||
getPurchaseTypeDeptDelegationDict(),
|
||||
getPurchaseModeSchoolDict(),
|
||||
getPurchaseTypeUnionDict(),
|
||||
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-button type="primary" :loading="assignAgentSubmitting" :disabled="!!assignedAgentName" @click="handleAssignAgentRandom">随机分配</el-button>
|
||||
</el-form-item>
|
||||
<!-- 发送招标代理按钮 -->
|
||||
<el-form-item v-if="canSendToAgent">
|
||||
<el-button type="success" :loading="sendToAgentSubmitting" @click="handleSendToAgent">发送招标代理</el-button>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 仅部门审核角色显示:采购代表相关 -->
|
||||
@@ -71,7 +75,7 @@
|
||||
<script setup lang="ts" name="PurchasingImplement">
|
||||
import { ref, computed, onMounted, watch, onUnmounted } from 'vue'
|
||||
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 { useMessage } from '/@/hooks/message'
|
||||
import { Session } from '/@/utils/storage'
|
||||
@@ -129,6 +133,7 @@ const agentListLoading = ref(false)
|
||||
const assignAgentSubmitting = ref(false)
|
||||
const rollingAgentName = ref<string>('')
|
||||
const assignedAgentName = ref<string>('')
|
||||
const sendToAgentSubmitting = ref(false)
|
||||
let rollInterval: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
/** 是否可以分配代理:委托代理采购 且 (学校统一采购 或 部门自行采购且委托采购中心采购) */
|
||||
@@ -140,6 +145,16 @@ const canAssignAgent = computed(() => {
|
||||
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 () => {
|
||||
if (!canAssignAgent.value) return
|
||||
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 postMessage = (type: string, payload?: any) => {
|
||||
|
||||
Reference in New Issue
Block a user