# 前端开发设计文档 — 自行招标流程 **版本**: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 页面挂载方式 流程引擎通过 `` 动态切换,将业务 ID 作为 prop 传入: ```javascript // 流程引擎容器内(假设已有) ``` **本页面设计**:所有页面作为独立组件,接收 `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` 主容器 | 不存在,由流程引擎直接挂载 | 当前项目中无该文件,无需创建容器 | | `` 动态切换 | 由流程引擎实现,本页面仅提供子组件 | 保持页面职责单一 | | 使用 `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 同步更新 | - | --- **文档结束**