zcui/saber3/docs/前端开发设计-自行招标流程.md

1464 lines
56 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 前端开发设计文档 — 自行招标流程
**版本**V2.3
**日期**2026-06-30
**对应需求**3.7.4 自行招标流程(完整流程:发起节点至投标方终报)
> **URL 前缀说明**:接口文档 V1.3 中定义的前缀为 `/api`,但当前后端实际服务前缀仍为 `/blade-zhaocai`。因此前端实现统一使用 `/blade-zhaocai/bidding-self/...`,待后端切换后再统一调整。
---
## 1. 需求概述
### 1.1 流程说明
自行招标流程包含15个前端页面覆盖从发起至终报的完整流程
| 页面 | 节点编号 | 节点名称 | 页面类型 | 说明 |
|------|----------|----------|----------|------|
| 发起页面 | 3.7.4.1 | 招采发起人 | 表单填写 | 发起人填写招标信息,自动带出预招标数据 |
| 审批页面 | 3.7.4.2 | 招采负责人初审 | 表单编辑 | 招采负责人补充招标公告信息,上传文件 |
| 终审页面 | 3.7.4.3 | 总裁办负责人审批 | 审批节点 | 总裁办主任审核公告内容,终审通过 |
| 供应商报名页面 | 3.7.4.4 | 招采平台供应商报名入口 | 外部节点 | 供应商查看项目列表并报名 |
| 总裁办负责人审批页面(资质审核视图) | 3.7.4.5 | 供应商资质审核 | 审批节点 | 招采负责人审核供应商资质,由 `final-approval.vue` 根据 `nodeName` 切换展示 |
| 开标页面 | 3.7.4.6 | 开标 | 执行节点 | 招采负责人确认签到数量并开标 |
| 投标页面 | 3.7.4.6 | 投标 | 执行节点 | 供应商签到、上传投标文件、填写报价 |
| 唱标页面 | 3.7.4.7 | 唱标 | 展示节点 | 展示投标方报价信息,生成唱标记录表 |
| 专家签到页面 | 3.7.4.8 | 专家签到 | 交互节点 | 专家线上签到并电子签署文件 |
| 专家评标页面 | 3.7.4.9 | 专家评标 | 评审节点 | 专家对投标方进行评分 |
| 资格认定表审批页面 | 3.7.4.10 | 资格认定表审批 | 审批节点 | 招采负责人审核资格认定表 |
| 供应商报价页面 | 3.7.4.11/15 | 供应商多轮报价 | 报价节点 | 入围供应商进行多轮报价(含终报) |
| 出报价分页面 | 3.7.4.12 | 出报价分 | 计算节点 | 根据评分规则计算报价分 |
| 投票页面 | 3.7.4.13 | 投票页面 | 投票节点 | 业务部门进行投票选定入围供应商 |
| 确定入围供应商页面 | 3.7.4.14 | 确定入围供应商 | 确认节点 | 确认入围名单并通知供应商 |
> **说明**:当前阶段已实现节点 3.7.4.1 和 3.7.4.2后续节点3.7.4.3-3.7.4.14)为待开发内容。
### 1.2 与接口文档的差异说明
> **重要**接口文档中的前端架构假设TypeScript + Pinia + `views/bidding/self/pages/` 目录结构)**与当前项目实际架构不符**。本文档基于项目实际技术栈重新设计,并依据接口文档 V1.3 去除由第三方系统实现的“公众号/招采平台发布公告”模块(原 3.7.4.4),后续节点编号依次前移。
**项目实际技术栈**
- **Vue 3**Options API 为主流Composition API 用于复杂交互页面)
- **Element Plus**UI 组件库)
- **Avue**CRUD 表格封装,但表单页面优先使用原生 Element Plus
- **Vuex**(全局状态管理)
- **Vite**(构建工具)
- **Pure JavaScript**(无 TypeScript
---
## 2. 文件结构设计
按项目现有模块规范,采用 **扁平化目录结构**
```
src/
├── api/
│ └── bidding/
│ └── self.js # 自行招标流程 API 封装全部45个接口
├── views/
│ └── bidding/
│ ├── initiate.vue # 3.7.4.1 发起页面(招采发起人)
│ ├── approval.vue # 3.7.4.2 审批页面(招采负责人初审)
│ ├── final-approval.vue # 3.7.4.3 总裁办负责人审批
│ ├── registration.vue # 3.7.4.4 供应商报名
│ ├── qualification.vue # 3.7.4.5 资质审核
│ ├── bid.vue # 3.7.4.6 开标(招采负责人端)
│ ├── bid-supplier.vue # 3.7.4.6 投标(供应商端)
│ ├── opening.vue # 3.7.4.7 唱标
│ ├── expert-sign.vue # 3.7.4.8 专家签到
│ ├── evaluation.vue # 3.7.4.9 专家评标
│ ├── qualification-cert.vue # 3.7.4.10 资格认定表审批
│ ├── quote.vue # 3.7.4.11/15 供应商报价
│ ├── score.vue # 3.7.4.12 出报价分
│ ├── vote.vue # 3.7.4.13 投票
│ └── finalist.vue # 3.7.4.14 确定入围供应商
```
> **设计理由**
> 1. 项目标准结构为 `src/api/{module}/` + `src/views/{module}/`,无需 `option` 层(非标准 CRUD 列表页)
> 2. 不创建 `self/pages/` 子目录,保持与项目现有模块的目录风格一致
> 3. 无 `PendingBidding.vue` 主容器(当前项目不存在该文件),各页面作为独立路由页面由流程引擎直接挂载
---
## 3. API 层设计(`src/api/bidding/self.js`
### 3.1 设计原则
- 遵循项目现有 API 层模式:导出纯函数,返回 `request({ url, method, params/data })` Promise
- 接口路径统一使用 `/api` 前缀,与后端服务保持一致
- 统一处理请求参数和响应数据结构
### 3.2 接口清单共42个接口
#### 3.2.1 基础接口(已实现)
| 序号 | 接口路径 | 方法 | 接口名称 |
|------|----------|------|----------|
| 1 | `/blade-zhaocai/bidding-self/pre-project/{id}` | GET | 获取预招标项目信息 |
| 2 | `/blade-zhaocai/bidding-self/draft` | POST | 保存招标发起草稿 |
| 3 | `/blade-zhaocai/bidding-self/submit` | POST | 提交招标发起 |
| 4 | `/blade-zhaocai/bidding-self/{id}` | GET | 获取招标发起详情 |
| 5 | `/blade-zhaocai/bidding-self/{id}/update` | PUT | 更新招采负责人数据 |
| 6 | `/blade-zhaocai/bidding-self/evaluation-principles` | GET | 获取评标原则列表 |
| 7 | `/blade-zhaocai/bidding-self/upload` | POST | 文件上传 |
| 8 | `/blade-zhaocai/bidding-self/organization-tree` | GET | 获取组织架构树 |
| 9 | `/blade-zhaocai/bidding-self/user-dept-list` | GET | 获取用户部门列表 |
#### 3.2.2 后续流程接口(待开发)
| 序号 | 接口路径 | 方法 | 接口名称 | 对应节点 |
|------|----------|------|----------|----------|
| 10 | `/blade-zhaocai/bidding-self/{id}/final-approval` | PUT | 更新总裁办负责人数据 | 3.7.4.3 |
| 11 | `/blade-zhaocai/bidding-self/registration/list` | GET | 获取报名项目列表 | 3.7.4.4 |
| 12 | `/blade-zhaocai/bidding-self/registration/submit` | POST | 提交报名 | 3.7.4.4 |
| 13 | `/blade-zhaocai/bidding-self/registration/my` | GET | 获取我的报名 | 3.7.4.4 |
| 14 | `/blade-zhaocai/bidding-self/qualification/list` | GET | 获取待审核供应商 | 3.7.4.5 |
| 15 | `/blade-zhaocai/bidding-self/qualification/audit` | POST | 审核供应商资质 | 3.7.4.5 |
| 16 | `/blade-zhaocai/bidding-self/qualification/batch` | POST | 批量审核 | 3.7.4.5 |
| 17 | `/blade-zhaocai/bidding-self/bid/list` | GET | 获取待投标项目 | 3.7.4.6 |
| 18 | `/blade-zhaocai/bidding-self/bid/sign` | POST | 开标签到 | 3.7.4.6 |
| 19 | `/blade-zhaocai/bidding-self/bid/submit` | POST | 提交投标 | 3.7.4.6 |
| 20 | `/blade-zhaocai/bidding-self/bid/opening` | POST | 开标 | 3.7.4.6 |
| 21 | `/blade-zhaocai/bidding-self/bid/flow` | POST | 流标 | 3.7.4.6 |
| 22 | `/blade-zhaocai/bidding-self/bid/sign-export` | GET | 导出签到表 | 3.7.4.6 |
| 23 | `/blade-zhaocai/bidding-self/opening/list` | GET | 获取唱标列表 | 3.7.4.7 |
| 24 | `/blade-zhaocai/bidding-self/opening/record` | POST | 生成唱标记录 | 3.7.4.7 |
| 25 | `/blade-zhaocai/bidding-self/opening/complete` | POST | 唱标结束 | 3.7.4.7 |
| 26 | `/blade-zhaocai/bidding-self/expert/sign` | POST | 专家签到 | 3.7.4.8 |
| 27 | `/blade-zhaocai/bidding-self/expert/sign-file` | POST | 上传签署文件 | 3.7.4.8 |
| 28 | `/blade-zhaocai/bidding-self/expert/audit` | POST | 审核签署文件 | 3.7.4.8 |
| 29 | `/blade-zhaocai/bidding-self/evaluation/list` | GET | 获取待评标列表 | 3.7.4.9 |
| 30 | `/blade-zhaocai/bidding-self/evaluation/detail` | GET | 获取投标方详情 | 3.7.4.9 |
| 31 | `/blade-zhaocai/bidding-self/evaluation/score` | POST | 提交评分 | 3.7.4.9 |
| 32 | `/blade-zhaocai/bidding-self/evaluation/cert` | POST | 生成资质认定表 | 3.7.4.9 |
| 33 | `/blade-zhaocai/bidding-self/qualification-cert/audit` | POST | 审核资格认定表 | 3.7.4.10 |
| 34 | `/blade-zhaocai/bidding-self/qualification-cert/notify` | POST | 发送淘汰通知 | 3.7.4.10 |
| 35 | `/blade-zhaocai/bidding-self/quote/submit` | POST | 提交报价 | 3.7.4.11/15 |
| 36 | `/blade-zhaocai/bidding-self/quote/history` | GET | 获取报价历史 | 3.7.4.11/15 |
| 37 | `/blade-zhaocai/bidding-self/score/calculate` | POST | 计算报价分 | 3.7.4.12 |
| 38 | `/blade-zhaocai/bidding-self/score/config` | GET | 获取评分配置 | 3.7.4.12 |
| 39 | `/blade-zhaocai/bidding-self/vote/submit` | POST | 提交投票 | 3.7.4.13 |
| 40 | `/blade-zhaocai/bidding-self/vote/result` | GET | 获取投票结果 | 3.7.4.13 |
| 41 | `/blade-zhaocai/bidding-self/finalist/confirm` | POST | 确认入围 | 3.7.4.14 |
| 42 | `/blade-zhaocai/bidding-self/finalist/notify` | POST | 通知入围供应商 | 3.7.4.14 |
### 3.3 API 函数定义
```javascript
import request from '@/axios';
// ==================== 基础接口3.7.4.1 - 3.7.4.2====================
/**
* 获取当前用户的部门列表(含公司信息)
*/
export const getUserDeptList = () => {
return request({ url: '/blade-zhaocai/bidding-self/user-dept-list', method: 'get' });
};
/**
* 获取预招标项目信息
* @param {number} id - 预招标项目ID
*/
export const getPreProject = (id) => {
return request({ url: `/blade-zhaocai/bidding-self/pre-project/${id}`, method: 'get' });
};
/**
* 保存招标发起草稿
* @param {Object} data - BiddingSelfInitiateDTO
*/
export const saveDraft = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/draft', method: 'post', data });
};
/**
* 提交招标发起
* @param {Object} data - BiddingSelfInitiateDTO
*/
export const submitBidding = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/submit', method: 'post', data });
};
/**
* 获取招标发起详情
* @param {number} id - 发起主表ID
*/
export const getBiddingDetail = (id) => {
return request({ url: `/blade-zhaocai/bidding-self/${id}`, method: 'get' });
};
/**
* 更新招采负责人数据
* @param {number} id - 发起主表ID
* @param {Object} data - BiddingSelfUpdateDTO
*/
export const updateApproval = (id, data) => {
return request({ url: `/blade-zhaocai/bidding-self/${id}/update`, method: 'put', data });
};
/**
* 获取评标原则列表
*/
export const getEvaluationPrinciples = () => {
return request({ url: '/blade-zhaocai/bidding-self/evaluation-principles', method: 'get' });
};
/**
* 获取组织架构树
*/
export const getOrganizationTree = () => {
return request({ url: '/blade-zhaocai/bidding-self/organization-tree', method: 'get' });
};
// ==================== 3.7.4.3 总裁办负责人审批 ====================
/**
* 更新总裁办负责人数据
* @param {number} id - 发起主表ID
* @param {Object} data - BiddingSelfFinalApprovalDTO
*/
export const updateFinalApproval = (id, data) => {
return request({ url: `/blade-zhaocai/bidding-self/${id}/final-approval`, method: 'put', data });
};
// ==================== 3.7.4.4 供应商报名 ====================
/**
* 获取报名项目列表(通用)
* 供应商报名页面优先使用 getBiddingDetail(id) 按流程审批ID查询单个招标信息
*/
export const getRegistrationList = () => {
return request({ url: '/blade-zhaocai/bidding-self/registration/list', method: 'get' });
};
/**
* 提交报名
* @param {Object} data - BiddingSelfRegistrationDTO
*/
export const submitRegistration = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/registration/submit', method: 'post', data });
};
/**
* 获取我的报名
*/
export const getMyRegistration = () => {
return request({ url: '/blade-zhaocai/bidding-self/registration/my', method: 'get' });
};
// ==================== 3.7.4.5 资质审核 ====================
/**
* 获取待审核供应商
* @param {number} initiateId - 发起主表ID
*/
export const getQualificationList = (initiateId) => {
return request({ url: '/blade-zhaocai/bidding-self/qualification/list', method: 'get', params: { initiateId } });
};
/**
* 审核供应商资质
* @param {Object} data - BiddingSelfQualificationAuditDTO
*/
export const auditQualification = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/qualification/audit', method: 'post', data });
};
/**
* 批量审核供应商资质
* @param {Object} data - 批量审核数据
*/
export const batchAuditQualification = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/qualification/batch', method: 'post', data });
};
// ==================== 3.7.4.6 开标/投标 ====================
/**
* 获取待投标项目
*/
export const getBidList = () => {
return request({ url: '/blade-zhaocai/bidding-self/bid/list', method: 'get' });
};
/**
* 开标签到
* @param {Object} data - BiddingSelfBidSignDTO
*/
export const bidSign = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/bid/sign', method: 'post', data });
};
/**
* 提交投标
* @param {Object} data - BiddingSelfBidDTO
*/
export const submitBid = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/bid/submit', method: 'post', data });
};
/**
* 开标
* @param {Object} data - 开标数据
*/
export const bidOpening = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/bid/opening', method: 'post', data });
};
/**
* 流标
* @param {Object} data - 流标数据
*/
export const bidFlow = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/bid/flow', method: 'post', data });
};
/**
* 导出签到表
* @param {number} initiateId - 发起主表ID
*/
export const exportSignList = (initiateId) => {
return request({ url: '/blade-zhaocai/bidding-self/bid/sign-export', method: 'get', params: { initiateId } });
};
// ==================== 3.7.4.7 唱标 ====================
/**
* 获取唱标列表
* @param {number} initiateId - 发起主表ID
*/
export const getOpeningList = (initiateId) => {
return request({ url: '/blade-zhaocai/bidding-self/opening/list', method: 'get', params: { initiateId } });
};
/**
* 生成唱标记录
* @param {Object} data - BiddingSelfOpeningRecordDTO
*/
export const generateOpeningRecord = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/opening/record', method: 'post', data });
};
/**
* 唱标结束
* @param {Object} data - 唱标结束数据
*/
export const completeOpening = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/opening/complete', method: 'post', data });
};
// ==================== 3.7.4.8 专家签到 ====================
/**
* 专家签到
* @param {Object} data - 专家签到数据
*/
export const expertSign = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/expert/sign', method: 'post', data });
};
/**
* 上传签署文件
* @param {Object} data - 签署文件数据
*/
export const uploadSignFile = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/expert/sign-file', method: 'post', data });
};
/**
* 审核签署文件
* @param {Object} data - 审核数据
*/
export const auditSignFile = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/expert/audit', method: 'post', data });
};
// ==================== 3.7.4.9 专家评标 ====================
/**
* 获取待评标列表
* @param {number} initiateId - 发起主表ID
*/
export const getEvaluationList = (initiateId) => {
return request({ url: '/blade-zhaocai/bidding-self/evaluation/list', method: 'get', params: { initiateId } });
};
/**
* 获取投标方详情
* @param {number} supplierId - 供应商ID
*/
export const getEvaluationDetail = (supplierId) => {
return request({ url: '/blade-zhaocai/bidding-self/evaluation/detail', method: 'get', params: { supplierId } });
};
/**
* 提交评分
* @param {Object} data - BiddingSelfEvaluationDTO
*/
export const submitScore = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/evaluation/score', method: 'post', data });
};
/**
* 生成资质认定表
* @param {Object} data - 生成数据
*/
export const generateCert = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/evaluation/cert', method: 'post', data });
};
// ==================== 3.7.4.10 资格认定表审批 ====================
/**
* 审核资格认定表
* @param {Object} data - 审核数据
*/
export const auditQualificationCert = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/qualification-cert/audit', method: 'post', data });
};
/**
* 发送淘汰通知
* @param {Object} data - 通知数据
*/
export const sendEliminationNotify = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/qualification-cert/notify', method: 'post', data });
};
// ==================== 3.7.4.11/15 供应商报价 ====================
/**
* 提交报价
* @param {Object} data - BiddingSelfQuoteDTO
*/
export const submitQuote = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/quote/submit', method: 'post', data });
};
/**
* 获取报价历史
* @param {number} initiateId - 发起主表ID
* @param {number} supplierId - 供应商ID
*/
export const getQuoteHistory = (initiateId, supplierId) => {
return request({ url: '/blade-zhaocai/bidding-self/quote/history', method: 'get', params: { initiateId, supplierId } });
};
// ==================== 3.7.4.12 出报价分 ====================
/**
* 计算报价分
* @param {Object} data - BiddingSelfScoreCalculateDTO
*/
export const calculateScore = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/score/calculate', method: 'post', data });
};
/**
* 获取评分配置
* @param {number} initiateId - 发起主表ID
*/
export const getScoreConfig = (initiateId) => {
return request({ url: '/blade-zhaocai/bidding-self/score/config', method: 'get', params: { initiateId } });
};
// ==================== 3.7.4.13 投票 ====================
/**
* 提交投票
* @param {Object} data - BiddingSelfVoteDTO
*/
export const submitVote = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/vote/submit', method: 'post', data });
};
/**
* 获取投票结果
* @param {number} initiateId - 发起主表ID
*/
export const getVoteResult = (initiateId) => {
return request({ url: '/blade-zhaocai/bidding-self/vote/result', method: 'get', params: { initiateId } });
};
// ==================== 3.7.4.14 确定入围供应商 ====================
/**
* 确认入围
* @param {Object} data - BiddingSelfFinalistDTO
*/
export const confirmFinalist = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/finalist/confirm', method: 'post', data });
};
/**
* 通知入围供应商
* @param {Object} data - 通知数据
*/
export const notifyFinalist = (data) => {
return request({ url: '/blade-zhaocai/bidding-self/finalist/notify', method: 'post', data });
};
```
### 3.4 文件上传说明
文件上传 **不单独封装 API 函数**,直接使用 `el-upload` 组件的 `action` 属性指向 `/blade-zhaocai/bidding-self/upload`,由 axios 拦截器自动注入 token 和请求头。
---
## 4. 数据类型定义
### 4.1 基础数据类型(已有)
```javascript
// BladeFile附件对象
// {
// name: string, // 文件名(存储名)
// url: string, // 文件访问URL
// size: number, // 文件大小(字节)
// originalName: string, // 原始文件名
// contentType: string, // 文件类型
// }
// SubjectItem标的物
// {
// seqNo: number, // 序号
// materialName: string, // 物料名称
// materialCode: string, // 物料编码
// specification: string, // 规格型号
// brand: string, // 品牌
// unit: string, // 计量单位
// referencePrice: number, // 参考价(不含税)
// estimatedQuantity: number, // 预计数量
// estimatedUseTime: string, // 预计使用时间
// amount: number, // 金额(元)
// contactPersonId: number, // 对接人ID
// contactPersonName: string, // 对接人姓名
// demandDesc: string, // 需求简介
// remark: string, // 备注
// }
// ExpertItem评标专家
// {
// expertId: number, // 专家ID
// expertName: string, // 专家姓名
// expertType: string, // 专家类型appoint-指定 / random-随机
// }
// UserDeptItem用户部门信息
// {
// deptId: number, // 部门ID
// deptName: string, // 部门名称
// tenantId: number, // 公司ID租户ID
// tenantName: string, // 公司名称
// isDefault: boolean, // 是否默认部门
// }
```
### 4.2 新增数据类型
```javascript
// BiddingSelfRegistration供应商报名
// {
// id: number, // 报名ID
// initiateId: number, // 发起主表ID
// supplierId: number, // 供应商ID
// supplierName: string, // 供应商名称
// contactName: string, // 联系人姓名
// contactPhone: string, // 联系人电话
// contactEmail: string, // 联系人邮箱
// qualificationFiles: string, // 资质文件JSON BladeFile列表
// otherFiles: string, // 其他资料JSON BladeFile列表
// status: string, // 状态PENDING/APPROVED/REJECTED
// rejectReason: string, // 驳回原因
// }
// BiddingSelfQualification资质审核
// {
// id: number, // 审核ID
// initiateId: number, // 发起主表ID
// supplierId: number, // 供应商ID
// supplierName: string, // 供应商名称
// auditResult: string, // 审核结果APPROVED/REJECTED
// rejectReason: string, // 未入围原因
// paymentStatus: string, // 付款状态UNPAID/PAID
// paymentAmount: number, // 标书费用金额
// }
// BiddingSelfBid投标/开标)
// {
// id: number, // 投标ID
// initiateId: number, // 发起主表ID
// supplierId: number, // 供应商ID
// signStatus: number, // 签到状态0-未签到1-已签到
// signTime: string, // 签到时间
// bidFiles: string, // 投标文件JSON BladeFile列表
// authorizationFile: string, // 授权书JSON BladeFile
// otherFiles: string, // 其他资料JSON BladeFile列表
// totalQuote: number, // 总报价(元)
// quoteDetails: string, // 报价明细JSON
// status: string, // 状态PENDING/OPENED/FLOWED
// }
// BiddingSelfBidOpening唱标记录
// {
// id: number, // 唱标ID
// initiateId: number, // 发起主表ID
// supplierId: number, // 供应商ID
// supplierName: string, // 供应商名称
// totalQuote: number, // 投标总报价(元)
// taxRate: number, // 税率
// totalWithTax: number, // 含税总价
// totalWithoutTax: number, // 不含税总价
// taxCategory: string, // 税目
// deliveryPeriod: string, // 工期/交货期/质保期
// remarks: string, // 补充说明
// openingRecordFile: string, // 唱标记录表文件JSON BladeFile
// status: string, // 状态PENDING/COMPLETED
// }
// BiddingSelfExpertSign专家签到
// {
// id: number, // 签到ID
// initiateId: number, // 发起主表ID
// expertId: number, // 专家ID
// expertName: string, // 专家姓名
// expertTitle: string, // 职称
// expertCategory: string, // 专业类别
// expertOrg: string, // 所属单位
// signStatus: number, // 签到状态0-未签到1-已签到
// signTime: string, // 签到时间
// signFiles: string, // 电子签署文件JSON BladeFile列表
// signStatusFile: string, // 签署状态UNSIGNED/SIGNED/REJECTED
// }
// BiddingSelfEvaluation专家评标
// {
// id: number, // 评分ID
// initiateId: number, // 发起主表ID
// expertId: number, // 专家ID
// expertName: string, // 专家姓名
// supplierId: number, // 供应商ID
// supplierName: string, // 供应商名称
// qualificationScore: number, // 资质审查评分
// technicalScore: number, // 技术标准评分
// businessScore: number, // 商务标准评分
// priceScore: number, // 价格评分
// comprehensiveScore: number, // 综合评分
// evaluationOpinion: string, // 评审意见
// status: string, // 状态PENDING/COMPLETED
// }
// BiddingSelfQuote供应商报价
// {
// id: number, // 报价ID
// initiateId: number, // 发起主表ID
// supplierId: number, // 供应商ID
// quoteRound: number, // 报价轮次1-第一轮2-第二轮3-终报
// totalAmount: number, // 报价总金额(元)
// totalAmountUpper: string, // 总金额大写
// deliveryPeriod: string, // 服务/交货周期/质保期
// remarks: string, // 补充说明/优惠承诺
// quoteDetails: string, // 报价明细JSON
// quoteFiles: string, // 报价附件JSON BladeFile列表
// deadline: string, // 报价截止时间
// }
// BiddingSelfScore报价分计算
// {
// id: number, // 计算ID
// initiateId: number, // 发起主表ID
// scoreMethod: string, // 评分方式LOW_PRICE/BASE_PRICE
// scoreWeight: number, // 价格分权重
// quoteRound: number, // 报价轮次
// scoreDetails: string, // 报价分明细JSON
// }
// ScoreResultItem报价分结果项
// {
// supplierName: string, // 供应商名称
// quote: number, // 报价(元)
// priceReduction: number, // 降价幅度
// lowestPrice: number, // 本轮最低价
// priceScore: number, // 价格分
// }
// BiddingSelfVote投票
// {
// id: number, // 投票ID
// initiateId: number, // 发起主表ID
// voterId: number, // 投票人ID
// voterName: string, // 投票人姓名
// voterRole: string, // 投票人角色BUSINESS/PROCUREMENT/AUDIT
// voteRound: number, // 投票轮次
// voteType: string, // 投票类型SINGLE/MULTIPLE
// voteResult: string, // 投票结果JSON供应商ID列表
// personalOpinion: string, // 个人意见备注
// status: string, // 状态PENDING/COMPLETED
// }
// VoteResultItem投票结果项
// {
// supplierId: number, // 供应商ID
// supplierName: string, // 供应商名称
// voteCount: number, // 得票数
// ranking: number, // 排名
// }
// BiddingSelfFinalist入围供应商
// {
// id: number, // 入围ID
// initiateId: number, // 发起主表ID
// supplierId: number, // 供应商ID
// supplierName: string, // 供应商名称
// ranking: number, // 排名
// comprehensiveScore: number, // 综合评分
// voteCount: number, // 得票数
// finalQuote: number, // 最终报价
// status: string, // 状态CONFIRMED/REJECTED
// }
```
---
## 5. 页面设计
### 5.1 通用设计原则
| 项目 | 设计决策 | 理由 |
|------|----------|------|
| API 风格 | Options API | 项目主流风格,与 90% 现有页面保持一致 |
| 容器组件 | `basic-container` | 全局注册组件,所有页面统一使用 |
| 表单组件 | Element Plus 原生表单 | 非标准 CRUD 列表avue-crud 不适用复杂表单布局 |
| 表格组件 | `el-table` + `el-table-column` | 标的物列表需要自定义列和交互 |
| 状态管理 | 组件内部 `data` | 流程表单数据无需全局共享 |
| 时间处理 | `dayjs``this.$dayjs` | 全局注入,统一时区处理 |
| 富文本编辑器 | `avue-ueditor` | 项目已集成,公告内容编辑使用 |
### 5.2 发起页面(`src/views/bidding/initiate.vue`)— 3.7.4.1
**功能**:招标发起人填写自行招标发起表单。页面数据根据预招标流程自动带出。
**接口**
- 加载预招标数据:`GET /blade-zhaocai/bidding-self/pre-project/{preBiddingId}`
- 获取用户部门列表:`GET /blade-zhaocai/bidding-self/user-dept-list`
- 保存草稿:`POST /blade-zhaocai/bidding-self/draft`
- 提交:`POST /blade-zhaocai/bidding-self/submit`
**页面结构**(从上到下):
1. 发起人信息(自动带出,可编辑)
- 申请人:自动带出当前登录用户姓名,只读
- 申请时间:自动带出当前时间,只读
- 联系电话:自动带出当前用户电话,可修改,需验证手机号格式
- 所属部门:下拉框选择,调用 `getUserDeptList` 获取部门列表,默认选中 `isDefault=true` 的项,选择后自动带出公司信息
- 所属公司:根据部门选择自动带出,只读
2. 项目信息(表格形式,只读展示)
- 表格列:计划号、项目编号、项目名称、项目类型、预算金额、需求部门、需求公司、项目对接人
- 预算金额使用金额格式(保留两位小数)
3. 标的物信息(列表形式,部分可编辑)
- 工具栏:添加明细按钮、清空明细按钮
- 表格列:序号、物料名称、物料编码、规格型号、品牌、计量单位、参考价、预计数量、预计使用时间、金额、对接人、需求简介、备注
- 序号自动生成1开始
- 物料名称必填限制100字符
- 预计数量输入时,金额自动计算(参考价 × 数量)
- 对接人:点击选择,弹窗组织架构树
- 需求简介限制1000字符
4. 投标要求(填写区)
- 报名开始/结束时间:时间控件,精确到时分秒
- 投标开始/结束时间:时间控件,精确到时分秒
- 投标方式:下拉选择(线上/线下)
- 开标时间:时间控件,精确到时分秒
- 校验:结束时间 > 开始时间
5. 评标原则(选择区)
- 评标原则:下拉选择(调用 `getEvaluationPrinciples`
- 说明:自动带出,只读展示
6. 评标小组(只读展示)
- 评标小组来源、评标小组人员列表
7. 只读说明区(纯文本展示)
- 定标原则、合同签订说明、其他(没收投标保证金/严禁转包)
**操作按钮**
- 取消:返回待招标库
- 保存草稿:调用 `saveDraft`
- 提交:调用 `submitBidding`,提交成功后返回待招标库
**提交校验**
- 所有必填字段已填写(包含 `deptId` 必填)
- 时间逻辑正确(结束时间 > 开始时间)
- 预计数量 > 0
- 联系电话格式正确
- 物料名称不为空且 <= 100字符
- 需求简介 <= 1000字符
**防抖**:保存草稿和提交按钮防抖 3 秒,提交期间按钮禁用
---
### 5.3 审批页面(`src/views/bidding/approval.vue`)— 3.7.4.2
**功能**:招采负责人对发起人提交的信息进行补充,填写招标公告核心信息并上传文件。
**接口**
- 加载数据:`GET /blade-zhaocai/bidding-self/{id}`
- 保存数据:`PUT /blade-zhaocai/bidding-self/{id}/update`
- 文件上传:`POST /blade-zhaocai/bidding-self/upload`
**页面结构**(从上到下):
1. 上部:发起信息只读展示(默认展示,任何节点都显示)
- 显示发起页面填写的所有信息(发起人、项目、标的物、投标要求、评标原则、评标小组)
- 只读展示,不可编辑
2. 中部:招标公告信息(根据节点名称控制可编辑性)
- 报名开始/结束时间:时间控件,精确到年月日时分,可修改
- 投标开始/结束时间:时间控件,精确到年月日时分,可修改
- 开标时间:时间控件,精确到年月日时分,可修改
- 投标方式:下拉选择(线上/线下),默认线上
- 投标地点:文本输入,选择线下时必填
- 投标联系方式:文本输入,格式:姓名、电话、邮箱
- 招标公告:文件上传,必填,支持 PDF/DOC/DOCX/JPG/PNG
3. 下部:供应商资质审核视图(仅当 `nodeName``供应商资质审核` 时展示)
- 已报名供应商列表
- 审核弹窗
**权限控制**
- 节点名称为"总裁办招采负责人审批"时允许编辑招标公告信息
- 节点名称为"供应商资质审核"时,在发起信息下方展示供应商资质审核视图
- 非目标节点时所有字段只读,仅显示"关闭"按钮
**操作按钮**
- 取消/关闭:返回列表
- 暂存:保存当前编辑内容(不校验必填)
- 保存:校验后调用 `updateApproval`,保存业务数据
- 保存审核:在资质审核弹窗中保存当前审核结果
**保存校验**
- 时间逻辑正确(结束时间 > 开始时间)
- 选择线下时,投标地点必填
- 招标公告文件必须上传
**防抖**:保存和暂存按钮防抖 3 秒,提交期间按钮禁用
**注意**:此页面不处理审批同意/驳回操作(由后续流程统一接口处理),仅保存业务数据(时间、地点、联系方式、附件信息)
---
### 5.4 总裁办负责人审批页面(`src/views/bidding/final-approval.vue`)— 3.7.4.3
**功能**:总裁办主任对招采负责人提交的信息进行审核。审核表单同上一节点页面,若有意见可在表单上填写意见。
**接口**
- 加载数据:`GET /blade-zhaocai/bidding-self/{id}`
- 保存数据:`PUT /blade-zhaocai/bidding-self/{id}/final-approval`
- 文件上传:`POST /blade-zhaocai/bidding-self/upload`
**页面结构**(从上到下):
1. 上部:发起信息只读展示(默认展示,任何节点都显示)
- 显示发起页面和招采负责人填写的所有信息(发起人、项目等)
- 只读展示,不可编辑
2. 中部:总裁办负责人填写区
- 审批意见:文本输入,可填写审核意见
- 附件:可上传补充文件
3. 下部:供应商资质审核视图(仅当 `nodeName``供应商资质审` 时展示)
- 已报名供应商列表
- 审核弹窗
**操作按钮**
- 取消:返回列表
- 暂存:保存当前编辑内容(不校验必填)
- 保存:调用 `updateFinalApproval`,保存业务数据
**节点名称控制**
- 当流程引擎传入的 `nodeName``供应商资质审` 时,在发起信息下方展示供应商资质审核视图,调用 `getQualificationList``auditQualification`
- 其他节点名称时,页面展示总裁办负责人审批视图
**保存校验**
- 审批意见可为空(同意时无需填写)
- 驳回时需填写驳回原因
**防抖**:保存、暂存及审核提交按钮防抖 3 秒,提交期间按钮禁用
**注意**:此页面不处理审批同意/驳回操作(由后续流程统一接口处理),仅保存业务数据(审批意见、附件信息)
**路由规则**
- 同意 → 供应商报名
- 驳回 → 退回总裁办招采负责人审批节点
---
### 5.5 供应商报名页面(`src/views/bidding/registration.vue`)— 3.7.4.4
**功能**:供应商通过招标公告中的报名网址进入系统,查看正在报名中的项目列表,选择项目进行报名。
**路由参数**
- 供应商报名页面为外部入口页面可通过流程引擎挂载或直接通过公告中的报名URL访问。
- 示例URL`/bidding-self-registration/index`
**接口**
- 获取报名项目列表:`GET /blade-zhaocai/bidding-self/registration/list`
- 提交报名:`POST /blade-zhaocai/bidding-self/registration/submit`
- 获取我的报名:`GET /blade-zhaocai/bidding-self/registration/my`
- 文件上传:`POST /blade-zhaocai/bidding-self/upload`
**说明**
- 页面初始化时调用 `getRegistrationList()` 获取正在报名中的项目列表,赋值给 `registrationList`
- 表格字段映射:`projectCode`(项目编号)、`projectName`(项目名称)、`projectType`(项目类型)、`registrationStartTime`(报名开始时间)、`registrationEndTime`(报名结束时间)、`bidOpeningTime`(开标时间)。
**页面结构**(从上到下):
1. 报名项目列表
- 表格列:项目编号、项目名称、项目类型、报名开始时间、报名结束时间、剩余报名时间(倒计时)、开标时间、操作(报名按钮)
- 业务规则:当前时间超过报名结束时间后,操作列不再展示“报名”按钮
2. 报名弹窗
- 供应商名称:自动带出(根据登录账号)
- 联系人输入必填限制50字符
- 联系电话:输入,必填,手机号格式
- 联系邮箱:输入,必填,邮箱格式
- 资质文件文件上传PDF/图片),必填,营业执照、资质证书等
- 其他资料文件上传PDF/图片),选填
3. 我的报名(在"与您相关的项目"窗口查看)
- 表格列:招标方式、项目编号、开始投标时间、剩余投标时间(倒计时)、开标时间、操作(查看详情/修改报名)
**操作按钮**
- 提交报名:提交报名信息
- 取消:关闭报名窗口
> **说明**:弹窗中的联系人、联系电话、联系邮箱当前使用临时默认值(张三 / 13800138000 / supplier@example.com后续将改为根据登录账号或供应商信息自动带出。
---
### 5.6 供应商资质审核页面(`src/views/bidding/final-approval.vue`)— 3.7.4.5
**功能**:在投标方报名完成后,审核投标方的相关资质,决定投标方是否入围。
> **实现说明**:供应商资质审核逻辑已合并至 `src/views/bidding/approval.vue` 与 `src/views/bidding/final-approval.vue`。
> - `approval.vue`:当流程引擎传入的 `nodeName` 为 `供应商资质审核` 时,渲染资质审核视图。
> - `final-approval.vue`:当流程引擎传入的 `nodeName` 为 `供应商资质审` 时,渲染资质审核视图。
> - `src/views/bidding/qualification.vue` 文件保留独立静态路由入口,但后续流程引擎统一使用 `approval.vue` 或 `final-approval.vue`。
**接口**
- 获取待审核供应商:`GET /blade-zhaocai/bidding-self/qualification/list?initiateId={id}`
- 审核供应商资质:`POST /blade-zhaocai/bidding-self/qualification/audit`
- 批量审核:`POST /blade-zhaocai/bidding-self/qualification/batch`
**页面结构**(从上到下):
1. 已报名供应商列表
- 表格列:投标方名称(可点击展开查看详情)、联系人、联系电话、报名状态、操作(审核按钮)
2. 审核弹窗
- 供应商基本信息:自动带出(名称、联系人、联系方式)
- 资质文件:文件预览(支持预览和下载)
- 审核结果:下拉选择(入围/不入围)
- 未入围原因:文本输入,选择"不入围"时必填限制500字符
**操作按钮**
- 保存审核:保存当前审核结果
- 批量通过:批量选中供应商审核通过
- 批量驳回:批量选中供应商审核不通过
- 取消:返回列表
**业务规则**
- 逐个审核供应商资质,可批量操作
- 选择"入围"后,系统自动向供应商发送收款信息(标书费用)
- 选择"不入围"时,必须填写未入围原因,系统通知供应商
- 审核通过后,供应商可进行付款购买标书
---
### 5.7 开标页面(`src/views/bidding/bid.vue`)— 3.7.4.6(招采负责人端)
**功能**:审核入围资质通过后,投标方在规定时间内进行报价投标。投标时间结束后,到达开标时间,进行开标操作。
**接口**
- 获取待投标项目:`GET /blade-zhaocai/bidding-self/bid/list`
- 开标:`POST /blade-zhaocai/bidding-self/bid/opening`
- 流标:`POST /blade-zhaocai/bidding-self/bid/flow`
- 导出签到表:`GET /blade-zhaocai/bidding-self/bid/sign-export?initiateId={id}`
**页面结构**(从上到下):
1. 项目信息展示
- 项目编码、项目名称、投标开始时间、投标截止时间、开标时间、标的物明细
- 应签到/已签到:显示签到比例(如 5/8
2. 签到列表
- 表格列:投标方名称、签到时间、签到状态
- 支持导出签到表Excel/PDF
3. 开标操作
- 到达开标时间后,如已报价投标方达到至少三家,则可以进行开标操作
- 开标时间到,现场宣布开标纪律,检查并先开启商务标,记录各投标人报价
4. 流标操作(投标方不足三家时显示)
- 弹出流标公告通知窗口
- 编辑流标公告内容:公告标题、关键字、查看范围、是否置顶、招标简介
- 公告内容抓取招标内容自动生成,可手动编辑
---
### 5.8 投标页面(`src/views/bidding/bid-supplier.vue`)— 3.7.4.6(供应商端)
**功能**:供应商在开标前进行签到、上传投标文件、填写报价。
**接口**
- 获取待投标项目:`GET /blade-zhaocai/bidding-self/bid/list`
- 开标签到:`POST /blade-zhaocai/bidding-self/bid/sign`
- 提交投标:`POST /blade-zhaocai/bidding-self/bid/submit`
- 文件上传:`POST /blade-zhaocai/bidding-self/upload`
**页面结构**(从上到下):
1. 项目信息展示(只读)
- 项目编码、项目名称、投标截止时间、开标时间、标的物明细
2. 签到区
- 签到按钮:点击后调用 `bidSign` 进行签到
- 签到状态显示
3. 投标信息填写区
- 投标文件文件上传PDF必填
- 授权书文件上传PDF必填
- 其他资料文件上传PDF/图片),选填
- 报价明细:按标的物填写单价、数量,自动计算合价
- 总报价:自动汇总报价明细,只读
**操作按钮**
- 保存草稿:保存投标信息,不提交
- 提交投标:提交投标文件和报价
- 取消:返回列表
---
### 5.9 唱标页面(`src/views/bidding/opening.vue`)— 3.7.4.7
**功能**:展示投标方报价信息,生成唱标记录表。
**接口**
- 获取唱标列表:`GET /blade-zhaocai/bidding-self/opening/list?initiateId={id}`
- 生成唱标记录:`POST /blade-zhaocai/bidding-self/opening/record`
- 唱标结束:`POST /blade-zhaocai/bidding-self/opening/complete`
**页面结构**(从上到下):
1. 项目信息展示(只读)
2. 唱标列表
- 表格列:投标方名称、投标总报价、税率、含税总价、不含税总价、税目、工期/交货期/质保期、补充说明
3. 唱标记录操作
- 生成唱标记录表:调用 `generateOpeningRecord`生成PDF文件
- 唱标结束:确认唱标结束,进入专家评标环节
**操作按钮**
- 生成唱标记录表
- 唱标结束
- 返回列表
---
### 5.10 专家签到页面(`src/views/bidding/expert-sign.vue`)— 3.7.4.8
**功能**:专家线上签到并电子签署文件。
**接口**
- 专家签到:`POST /blade-zhaocai/bidding-self/expert/sign`
- 上传签署文件:`POST /blade-zhaocai/bidding-self/expert/sign-file`
- 审核签署文件:`POST /blade-zhaocai/bidding-self/expert/audit`
**页面结构**(从上到下):
1. 项目信息展示(只读)
2. 专家签到区
- 专家信息:姓名、职称、专业类别、所属单位
- 签到按钮:点击后签到
- 签到状态显示
3. 电子签署区
- 签署文件上传电子签署文件PDF/图片)
- 签署状态UNSIGNED/SIGNED/REJECTED
4. 招采负责人审核区(仅招采负责人可见)
- 审核签署文件:通过/驳回
- 驳回原因:文本输入
**操作按钮**
- 签到
- 上传签署文件
- 审核通过/驳回(招采负责人)
- 返回列表
---
### 5.11 专家评标页面(`src/views/bidding/evaluation.vue`)— 3.7.4.9
**功能**:专家对投标方进行评分。
**接口**
- 获取待评标列表:`GET /blade-zhaocai/bidding-self/evaluation/list?initiateId={id}`
- 获取投标方详情:`GET /blade-zhaocai/bidding-self/evaluation/detail?initiateId={id}&supplierId={id}`
- 提交评分:`POST /blade-zhaocai/bidding-self/evaluation/score`
- 生成资质认定表:`POST /blade-zhaocai/bidding-self/evaluation/cert`
**页面结构**(从上到下):
1. 项目信息展示(只读)
2. 待评标供应商列表
- 表格列:投标方名称、资质审查评分、技术标准评分、商务标准评分、价格评分、综合评分、操作(评分按钮)
3. 评分弹窗
- 供应商基本信息:自动带出
- 投标文件:文件预览
- 资质审查评分数字输入0-100
- 技术标准评分数字输入0-100
- 商务标准评分数字输入0-100
- 价格评分:自动计算(根据报价)
- 综合评分:自动计算(加权汇总)
- 评审意见文本输入限制1000字符
4. 生成资质认定表
- 所有专家评分完成后,生成资质认定表
**操作按钮**
- 提交评分
- 生成资质认定表
- 返回列表
---
### 5.12 资格认定表审批页面(`src/views/bidding/qualification-cert.vue`)— 3.7.4.10
**功能**:招采负责人审核资格认定表,决定供应商是否入围。
**接口**
- 审核资格认定表:`POST /blade-zhaocai/bidding-self/qualification-cert/audit`
- 发送淘汰通知:`POST /blade-zhaocai/bidding-self/qualification-cert/notify`
**页面结构**(从上到下):
1. 资格认定表展示
- 表格列:投标方名称、资质审查评分、技术标准评分、商务标准评分、价格评分、综合评分、排名
2. 审核操作区
- 审核结果:通过/不通过
- 不通过原因:文本输入
3. 淘汰通知
- 选择被淘汰供应商
- 发送淘汰通知
**操作按钮**
- 审核通过
- 审核不通过
- 发送淘汰通知
- 返回列表
---
### 5.13 供应商报价页面(`src/views/bidding/quote.vue`)— 3.7.4.11/15
**功能**:入围供应商进行多轮报价(含终报)。
**接口**
- 提交报价:`POST /blade-zhaocai/bidding-self/quote/submit`
- 获取报价历史:`GET /blade-zhaocai/bidding-self/quote/history?initiateId={id}&supplierId={id}`
**页面结构**(从上到下):
1. 项目信息展示(只读)
2. 报价历史
- 表格列:报价轮次、报价总金额、报价时间、状态
3. 当前报价区
- 报价总金额:金额输入,必填
- 总金额大写:自动转换
- 服务/交货周期/质保期:文本输入
- 补充说明/优惠承诺:文本输入
- 报价明细:按标的物填写单价、数量、小计
- 报价附件文件上传PDF/图片),选填
- 报价截止时间:只读展示
**操作按钮**
- 提交报价
- 保存草稿
- 取消
---
### 5.14 出报价分页面(`src/views/bidding/score.vue`)— 3.7.4.12
**功能**:根据评分规则计算报价分。
**接口**
- 计算报价分:`POST /blade-zhaocai/bidding-self/score/calculate`
- 获取评分配置:`GET /blade-zhaocai/bidding-self/score/config?initiateId={id}`
**页面结构**(从上到下):
1. 评分配置
- 评分方式下拉选择LOW_PRICE-低价优先/BASE_PRICE-基准价)
- 价格分权重数字输入0-100
- 报价轮次:数字输入
2. 报价分明细
- 表格列:供应商名称、报价(元)、降价幅度、本轮最低价、价格分
3. 计算公式展示
- 低价优先法:价格分 = (最低价 / 供应商报价) × 价格分权重
- 基准价法:价格分 = (基准价 / 供应商报价) × 价格分权重
**操作按钮**
- 计算报价分
- 导出报价分表
- 返回列表
---
### 5.15 投票页面(`src/views/bidding/vote.vue`)— 3.7.4.13
**功能**:业务部门进行投票选定入围供应商。
**接口**
- 提交投票:`POST /blade-zhaocai/bidding-self/vote/submit`
- 获取投票结果:`GET /blade-zhaocai/bidding-self/vote/result?initiateId={id}&voteRound={round}`
**页面结构**(从上到下):
1. 项目信息展示(只读)
2. 投票区
- 投票轮次:数字输入
- 投票类型:单选/多选
- 供应商列表:表格展示,每行包含选择框
- 个人意见备注:文本输入
3. 投票结果展示(投票完成后)
- 表格列:供应商名称、得票数、排名
**操作按钮**
- 提交投票
- 查看投票结果
- 返回列表
---
### 5.16 确定入围供应商页面(`src/views/bidding/finalist.vue`)— 3.7.4.14
**功能**:确认入围名单并通知供应商。
**接口**
- 确认入围:`POST /blade-zhaocai/bidding-self/finalist/confirm`
- 通知入围供应商:`POST /blade-zhaocai/bidding-self/finalist/notify`
**页面结构**(从上到下):
1. 入围供应商列表
- 表格列:供应商名称、排名、综合评分、得票数、最终报价
- 支持勾选确认入围
2. 确认操作
- 确认入围:勾选后提交确认
- 发送入围通知:向入围供应商发送通知
**操作按钮**
- 确认入围
- 发送入围通知
- 返回列表
---
## 6. 路由与流程引擎对接
### 6.1 路由设计
自行招标流程页面由 **流程引擎动态挂载**,不配置静态路由(除发起页面外可能需要独立路由)。流程引擎通过节点标识传递参数:
| 页面 | 接收参数 | 参数来源 |
|------|----------|----------|
| 发起页面 | `preBiddingId` | 待招标库跳转时传递 |
| 发起页面 | `id`发起主表ID | 草稿箱编辑时传递 |
| 审批页面 | `id`发起主表ID | 流程引擎 task 数据传递 |
| 审批页面 | `nodeName`(节点名称) | 流程引擎 task 数据传递,`供应商资质审核` 时切换为资质审核视图;`总裁办招采负责人审批` 时允许编辑招标公告信息 |
| 总裁办负责人审批页面 | `id`发起主表ID | 流程引擎 task 数据传递 |
| 总裁办负责人审批页面 | `nodeName`(节点名称) | 流程引擎 task 数据传递,`供应商资质审` 时切换为资质审核视图 |
| 其他页面 | `id`发起主表ID | 流程引擎 task 数据传递 |
### 6.2 页面挂载方式
流程引擎通过 `<component :is="currentPage">` 动态切换,将业务 ID 作为 prop 传入:
```javascript
// 流程引擎容器内(假设已有)
<template>
<component
:is="currentPage"
:node-data="nodeData"
@save="handleSave"
@submit="handleSubmit"
@cancel="handleCancel"
/>
</template>
```
**本页面设计**:所有页面作为独立组件,接收 `nodeData` prop 并 `$emit` 事件通知流程引擎。
### 6.3 静态路由(已配置)
```javascript
// 自行招标发起
path: '/bidding-self-initiate/index'
// 自行招标审批
path: '/bidding-self-approval/index'
```
后续页面如需要独立访问入口,可继续扩展静态路由。
---
## 7. 时区与格式化
### 7.1 时间处理
- 所有时间展示使用 `dayjs` 格式化(`this.$dayjs` 全局注入)
- 时间控件格式:
- 发起页:`YYYY-MM-DD HH:mm:ss`
- 审批页/终审页:`YYYY-MM-DD HH:mm`
- 供应商报名页:`YYYY-MM-DD HH:mm:ss`
- 提交时保持字符串格式,后端统一处理时区
- 倒计时显示:报名截止时间、投标截止时间等使用倒计时组件
```javascript
// 示例:格式化显示
this.$dayjs(timeStr).format('YYYY-MM-DD HH:mm:ss');
// 倒计时
countdown(deadline) {
const now = this.$dayjs();
const end = this.$dayjs(deadline);
const diff = end.diff(now, 'second');
if (diff <= 0) return '已截止';
const days = Math.floor(diff / 86400);
const hours = Math.floor((diff % 86400) / 3600);
const minutes = Math.floor((diff % 3600) / 60);
return `${days}${hours}小时${minutes}分钟`;
}
```
### 7.2 金额格式化
```javascript
// 金额展示:千分位 + 两位小数
formatMoney(value) {
if (!value && value !== 0) return '-';
return '¥ ' + Number(value).toLocaleString('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
}
// 金额大写转换
formatMoneyUpper(value) {
// 使用现有工具或引入 num-to-zh-currency
}
```
---
## 8. 异常与边界处理
| 场景 | 处理策略 |
|------|----------|
| 接口报错 | `try/catch` 捕获,`ElMessage.error` 提示错误信息 |
| 预招标 ID 无效 | 页面加载失败时提示并返回列表 |
| 提交校验失败 | 滚动到第一个错误字段,高亮提示 |
| 文件上传失败 | `el-upload` `on-error` 回调,提示上传失败 |
| 文件类型不符 | `before-upload` 校验文件类型和大小≤50MB |
| 文件数量超限 | `el-upload` `limit` 属性限制 5 个 |
| 网络超时 | axios 默认 30 秒超时,超时后提示重试 |
| 并发提交 | 按钮 `loading` 状态 + 防抖,防止重复提交 |
| 倒计时截止 | 报名/投标截止后,禁用相关操作按钮 |
| 权限不足 | 非目标节点时,表单只读,仅显示"关闭"按钮 |
---
## 9. 与接口文档的修正对照表
| 接口文档描述 | 实际项目修正 | 修正原因 |
|-------------|-------------|----------|
| TypeScript + Pinia | Pure JavaScript + 组件内部状态 | 项目无 TS/Pinia使用 Vuex 但流程表单无需全局状态 |
| `views/bidding/self/pages/` 目录 | `src/views/bidding/` + `src/api/bidding/` | 遵循项目现有模块的扁平目录规范 |
| `PendingBidding.vue` 主容器 | 不存在,由流程引擎直接挂载 | 当前项目中无该文件,无需创建容器 |
| `<component :is="">` 动态切换 | 由流程引擎实现,本页面仅提供子组件 | 保持页面职责单一 |
| 使用 `el-upload` 直接上传 | 保持使用,但利用 axios 拦截器自动注入 token | 无需手动设置 headers |
| 时间精确到秒(发起)+ 分(审批) | 发起页 `YYYY-MM-DD HH:mm:ss`,审批页 `YYYY-MM-DD HH:mm` | 与接口定义保持一致 |
| 接口路径 `/blade-zhaocai/bidding-self/...` | `/blade-zhaocai/bidding-self/...` | 与后端实际服务前缀保持一致 |
| 15个独立页面 | 15个独立 `.vue` 文件 | 每个节点一个完整页面,不复用 |
---
## 10. 开发 checklist
### 10.1 已完成V1.0
- [x] 创建 `src/api/bidding/self.js` — 基础 API 函数9个
- [x] 创建 `src/views/bidding/initiate.vue` — 发起页面
- [x] 创建 `src/views/bidding/approval.vue` — 审批页面
- [x] 发起人页面:表单校验、时间校验、金额自动计算、防抖处理
- [x] 审批页面:文件上传/预览/删除、提交逻辑、时间校验
- [x] 两页面:组织架构树弹窗选择对接人
- [x] 发起人信息优化:所属部门下拉框,选择后自动带出公司信息
- [x] 审批页面权限控制:根据节点名称判断编辑权限
### 10.2 待开发V2.0
- [ ] 扩展 `src/api/bidding/self.js` — 新增33个API函数序号10-42
- [ ] 创建 `src/views/bidding/final-approval.vue` — 3.7.4.3 总裁办负责人审批
- [ ] 创建 `src/views/bidding/registration.vue` — 3.7.4.4 供应商报名
- [ ] 创建 `src/views/bidding/qualification.vue` — 3.7.4.5 资质审核
- [ ] 创建 `src/views/bidding/bid.vue` — 3.7.4.6 开标(招采负责人端)
- [ ] 创建 `src/views/bidding/bid-supplier.vue` — 3.7.4.6 投标(供应商端)
- [ ] 创建 `src/views/bidding/opening.vue` — 3.7.4.7 唱标
- [ ] 创建 `src/views/bidding/expert-sign.vue` — 3.7.4.8 专家签到
- [ ] 创建 `src/views/bidding/evaluation.vue` — 3.7.4.9 专家评标
- [ ] 创建 `src/views/bidding/qualification-cert.vue` — 3.7.4.10 资格认定表审批
- [ ] 创建 `src/views/bidding/quote.vue` — 3.7.4.11/15 供应商报价
- [ ] 创建 `src/views/bidding/score.vue` — 3.7.4.12 出报价分
- [ ] 创建 `src/views/bidding/vote.vue` — 3.7.4.13 投票
- [ ] 创建 `src/views/bidding/finalist.vue` — 3.7.4.14 确定入围供应商
- [ ] 联调与后端42个接口逐一验证
- [ ] 测试:文件上传、表单校验、时间边界、并发提交、倒计时、权限控制
---
## 11. 变更记录
| 版本 | 日期 | 修改内容 | 修改人 |
|------|------|----------|--------|
| V1.0 | 2026-06-17 | 初始版本:发起页面 + 审批页面 | - |
| V1.1 | 2026-06-22 | 发起人信息优化:增加所属部门下拉框,选择后自动带出公司信息 | - |
| V1.2 | 2026-06-23 | 审批页面优化权限控制、标题调整、投标地点位置调整、OSS上传、非必填 | - |
| V2.0 | 2026-06-24 | 完整流程设计从2个节点扩展到15个完整节点新增13个页面设计、36个API接口、12个数据类型 | - |
| V2.1 | 2026-06-30 | 供应商报名页面:超过报名结束时间隐藏“报名”按钮;弹窗联系人字段增加临时默认值;移除“保存草稿”按钮 | - |
| V2.2 | 2026-06-30 | 依据接口文档 V1.3 调整:去除第三方系统实现的“公众号/招采平台发布公告”模块(原 3.7.4.4),后续节点编号依次前移;接口前缀统一为 `/api`接口总数由45调整为42文件结构、API清单、数据类型、页面设计、开发 checklist 同步更新 | - |
---
**文档结束**