rebuild all
This commit is contained in:
@@ -127,6 +127,119 @@
|
||||
<el-tab-pane label="审核记录" name="audit">
|
||||
<AuditRecordList :apply-id="applyId" ref="auditRecordListRef" />
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 采购代表设置(仅需求部门审核时显示) -->
|
||||
<el-tab-pane v-if="canSetReviewer" label="采购代表设置" name="reviewer">
|
||||
<ReviewerSetting :apply-id="applyId" @saved="handleReviewerSaved" />
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 开标通知 - 仅招标代理模式且状态为已完成时显示 -->
|
||||
<el-tab-pane v-if="showBidOpeningNotice" label="开标通知" name="bidOpening">
|
||||
<div class="bid-opening-form" v-loading="bidOpeningLoading">
|
||||
<el-form :model="bidOpeningForm" :rules="bidOpeningRules" ref="bidOpeningFormRef" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目名称">
|
||||
<el-input v-model="rowData.projectName" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开标时间" prop="openTime">
|
||||
<el-date-picker
|
||||
v-model="bidOpeningForm.openTime"
|
||||
type="datetime"
|
||||
placeholder="请选择开标时间"
|
||||
style="width: 100%"
|
||||
value-format="YYYY-MM-DD HH:mm:ss" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开标地点" prop="openLocation">
|
||||
<el-input v-model="bidOpeningForm.openLocation" placeholder="请输入开标地点" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="公示网址">
|
||||
<el-input v-model="bidOpeningForm.publicUrl" placeholder="请输入公示网址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="代理联系人" prop="agentContactName">
|
||||
<el-input v-model="bidOpeningForm.agentContactName" placeholder="请输入代理联系人姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="agentContactPhone">
|
||||
<el-input v-model="bidOpeningForm.agentContactPhone" placeholder="请输入代理联系人电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="招标确认函">
|
||||
<el-upload
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:data="{ fileType: '131', purchaseId: applyId }"
|
||||
:on-success="(res: any, file: any) => handleBidFileSuccess(res, file, 'bidConfirmationLetter')"
|
||||
:before-upload="beforeUpload"
|
||||
:show-file-list="false"
|
||||
accept=".doc,.docx,.pdf,.jpg,.jpeg,.png">
|
||||
<el-button type="primary" icon="Upload">上传文件</el-button>
|
||||
</el-upload>
|
||||
<div v-if="bidOpeningForm.bidConfirmationLetter" class="file-info">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span>已上传</span>
|
||||
<el-button type="primary" link @click="viewFile(bidOpeningForm.bidConfirmationLetter)">查看</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="授权书">
|
||||
<el-upload
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:data="{ fileType: '132', purchaseId: applyId }"
|
||||
:on-success="(res: any, file: any) => handleBidFileSuccess(res, file, 'authorizationLetter')"
|
||||
:before-upload="beforeUpload"
|
||||
:show-file-list="false"
|
||||
accept=".doc,.docx,.pdf,.jpg,.jpeg,.png">
|
||||
<el-button type="primary" icon="Upload">上传文件</el-button>
|
||||
</el-upload>
|
||||
<div v-if="bidOpeningForm.authorizationLetter" class="file-info">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span>已上传</span>
|
||||
<el-button type="primary" link @click="viewFile(bidOpeningForm.authorizationLetter)">查看</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="bidOpeningForm.remarks" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24" class="form-actions">
|
||||
<el-button type="info" :loading="bidOpeningSaving" @click="handleSaveBidOpeningDraft">保存草稿</el-button>
|
||||
<el-button type="success" :loading="bidOpeningPublishing" @click="handlePublishBidOpening">发布通知</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<!-- 已发布提示 -->
|
||||
<el-alert v-if="bidOpeningNoticeStatus === 'PUBLISHED'" type="success" :closable="false" class="mt-4">
|
||||
<template #title>
|
||||
<span>开标通知已于 {{ bidOpeningForm.publishTime }} 发布</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 操作区域 -->
|
||||
@@ -244,9 +357,17 @@ import {
|
||||
submitToAsset as submitToAssetApi,
|
||||
finalizeDoc as finalizeDocApi
|
||||
} from '/@/api/purchase/docProcess'
|
||||
import {
|
||||
getNoticeByApplyId,
|
||||
saveNotice,
|
||||
publishNotice,
|
||||
canSendNotice
|
||||
} from '/@/api/purchase/bidOpeningNotice'
|
||||
import type { UploadInstance, UploadProps, UploadUserFile } from 'element-plus'
|
||||
import { Document } from '@element-plus/icons-vue'
|
||||
|
||||
const AuditRecordList = defineAsyncComponent(() => import('./AuditRecordList.vue'))
|
||||
const ReviewerSetting = defineAsyncComponent(() => import('./ReviewerSetting.vue'))
|
||||
|
||||
const props = defineProps<{
|
||||
mode: 'agent' | 'audit'
|
||||
@@ -297,6 +418,33 @@ const supplyUploadForm = ref({
|
||||
fileRemark: ''
|
||||
})
|
||||
|
||||
// 开标通知相关
|
||||
const bidOpeningFormRef = ref()
|
||||
const bidOpeningLoading = ref(false)
|
||||
const bidOpeningSaving = ref(false)
|
||||
const bidOpeningPublishing = ref(false)
|
||||
const bidOpeningNoticeStatus = ref('')
|
||||
const bidOpeningForm = ref({
|
||||
id: '',
|
||||
applyId: '',
|
||||
projectName: '',
|
||||
openTime: '',
|
||||
openLocation: '',
|
||||
agentContactName: '',
|
||||
agentContactPhone: '',
|
||||
publicUrl: '',
|
||||
bidConfirmationLetter: '',
|
||||
authorizationLetter: '',
|
||||
remarks: '',
|
||||
publishTime: ''
|
||||
})
|
||||
const bidOpeningRules = {
|
||||
openTime: [{ required: true, message: '请选择开标时间', trigger: 'change' }],
|
||||
openLocation: [{ required: true, message: '请输入开标地点', trigger: 'blur' }],
|
||||
agentContactName: [{ required: true, message: '请输入代理联系人姓名', trigger: 'blur' }],
|
||||
agentContactPhone: [{ required: true, message: '请输入代理联系人电话', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// 弹窗标题
|
||||
const dialogTitle = computed(() => {
|
||||
return props.mode === 'agent' ? `处理项目 - ${rowData.value.purchaseNo || ''}` : '招标文件审核'
|
||||
@@ -336,6 +484,12 @@ const isReviewing = computed(() => ['ASSET_REVIEWING', 'DEPT_REVIEWING', 'AUDIT_
|
||||
const isConfirming = computed(() => statusField.value === 'ASSET_CONFIRMING')
|
||||
const isCompleted = computed(() => statusField.value === 'COMPLETED')
|
||||
|
||||
// 是否显示开标通知Tab(招标代理模式且状态为已完成)
|
||||
const showBidOpeningNotice = computed(() => props.mode === 'agent' && isCompleted.value)
|
||||
|
||||
// 是否显示采购代表设置Tab(需求部门审核中且有提交权限)
|
||||
const canSetReviewer = computed(() => statusField.value === 'DEPT_REVIEWING' && availableActions.value.includes('submitToAsset'))
|
||||
|
||||
// 上传配置
|
||||
const uploadAction = computed(() => {
|
||||
const baseUrl = import.meta.env.VITE_API_URL || ''
|
||||
@@ -365,6 +519,9 @@ const open = async (row: any) => {
|
||||
fileList.value = []
|
||||
uploadedFileData.value = null
|
||||
|
||||
// 重置开标通知表单
|
||||
resetBidOpeningForm()
|
||||
|
||||
// 获取申请ID(兼容 id 和 applyId 两种字段名)
|
||||
applyId.value = row.applyId || row.id
|
||||
|
||||
@@ -380,6 +537,10 @@ const open = async (row: any) => {
|
||||
loadRequirementFiles()
|
||||
// 加载招标文件
|
||||
loadDocList()
|
||||
// 加载开标通知(如果是招标代理模式且状态为已完成)
|
||||
if (props.mode === 'agent') {
|
||||
loadBidOpeningNotice()
|
||||
}
|
||||
}
|
||||
|
||||
const loadRequirementFiles = async () => {
|
||||
@@ -801,6 +962,154 @@ const submitFinalize = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 开标通知相关方法 ====================
|
||||
|
||||
/**
|
||||
* 重置开标通知表单
|
||||
*/
|
||||
const resetBidOpeningForm = () => {
|
||||
bidOpeningForm.value = {
|
||||
id: '',
|
||||
applyId: '',
|
||||
projectName: '',
|
||||
openTime: '',
|
||||
openLocation: '',
|
||||
agentContactName: '',
|
||||
agentContactPhone: '',
|
||||
publicUrl: '',
|
||||
bidConfirmationLetter: '',
|
||||
authorizationLetter: '',
|
||||
remarks: '',
|
||||
publishTime: ''
|
||||
}
|
||||
bidOpeningNoticeStatus.value = ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载开标通知
|
||||
*/
|
||||
const loadBidOpeningNotice = async () => {
|
||||
if (!applyId.value) return
|
||||
bidOpeningLoading.value = true
|
||||
try {
|
||||
const res = await getNoticeByApplyId(applyId.value)
|
||||
if (res?.code === 0 || res?.code === 200) {
|
||||
const data = res.data
|
||||
if (data) {
|
||||
bidOpeningForm.value = {
|
||||
id: data.id || '',
|
||||
applyId: data.applyId || applyId.value,
|
||||
projectName: data.projectName || rowData.value.projectName || '',
|
||||
openTime: data.openTime || '',
|
||||
openLocation: data.openLocation || '',
|
||||
agentContactName: data.agentContactName || '',
|
||||
agentContactPhone: data.agentContactPhone || '',
|
||||
publicUrl: data.publicUrl || '',
|
||||
bidConfirmationLetter: data.bidConfirmationLetter || '',
|
||||
authorizationLetter: data.authorizationLetter || '',
|
||||
remarks: data.remarks || '',
|
||||
publishTime: data.publishTime || ''
|
||||
}
|
||||
bidOpeningNoticeStatus.value = data.status || ''
|
||||
} else {
|
||||
bidOpeningForm.value.applyId = applyId.value
|
||||
bidOpeningForm.value.projectName = rowData.value.projectName || ''
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略错误,可能是第一次创建
|
||||
} finally {
|
||||
bidOpeningLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开标通知文件上传成功回调
|
||||
*/
|
||||
const handleBidFileSuccess = (response: any, file: any, field: string) => {
|
||||
if (response?.code === 0 || response?.code === 200) {
|
||||
bidOpeningForm.value[field] = response.data.remark || response.data.filePath
|
||||
useMessage().success('文件上传成功')
|
||||
} else {
|
||||
useMessage().error(response?.msg || '文件上传失败')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看文件
|
||||
*/
|
||||
const viewFile = (url: string) => {
|
||||
if (url) {
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存开标通知草稿
|
||||
*/
|
||||
const handleSaveBidOpeningDraft = async () => {
|
||||
try {
|
||||
await bidOpeningFormRef.value?.validate()
|
||||
} catch {
|
||||
useMessage().warning('请填写必填项')
|
||||
return
|
||||
}
|
||||
|
||||
bidOpeningSaving.value = true
|
||||
try {
|
||||
const res = await saveNotice({
|
||||
...bidOpeningForm.value,
|
||||
applyId: applyId.value
|
||||
})
|
||||
if (res?.code === 0 || res?.code === 200) {
|
||||
useMessage().success('保存成功')
|
||||
await loadBidOpeningNotice()
|
||||
} else {
|
||||
useMessage().error(res?.msg || '保存失败')
|
||||
}
|
||||
} catch (e: any) {
|
||||
useMessage().error(e?.msg || '保存失败')
|
||||
} finally {
|
||||
bidOpeningSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布开标通知
|
||||
*/
|
||||
const handlePublishBidOpening = async () => {
|
||||
try {
|
||||
await bidOpeningFormRef.value?.validate()
|
||||
} catch {
|
||||
useMessage().warning('请填写必填项')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await useMessageBox().confirm('确定要发布开标通知吗?发布后将无法修改。')
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
bidOpeningPublishing.value = true
|
||||
try {
|
||||
const res = await publishNotice({
|
||||
...bidOpeningForm.value,
|
||||
applyId: applyId.value
|
||||
})
|
||||
if (res?.code === 0 || res?.code === 200) {
|
||||
useMessage().success('发布成功')
|
||||
await loadBidOpeningNotice()
|
||||
} else {
|
||||
useMessage().error(res?.msg || '发布失败')
|
||||
}
|
||||
} catch (e: any) {
|
||||
useMessage().error(e?.msg || '发布失败')
|
||||
} finally {
|
||||
bidOpeningPublishing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
}
|
||||
@@ -841,6 +1150,11 @@ const getFileTypeLabel = (type: string) => {
|
||||
return labelMap[type] || type
|
||||
}
|
||||
|
||||
// 采购代表设置保存成功回调
|
||||
const handleReviewerSaved = () => {
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
@@ -868,4 +1182,29 @@ defineExpose({ open })
|
||||
.mt-4 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.bid-opening-form {
|
||||
padding: 20px;
|
||||
|
||||
.file-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
color: #606266;
|
||||
|
||||
.el-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user