恢复修改的数据

This commit is contained in:
wangzzpeng
2026-09-28 10:47:49 +08:00
parent f1e64ef864
commit 43c2dc0d8d
57 changed files with 4557 additions and 1642 deletions

View File

@@ -1,9 +1,52 @@
// 案例实体(PRD 4.1,home/cases/profile.favorites 共享)
// 案例库类型(案例实体 + 后端 VO,与 api/cases.ts 对应)
//
// 后端:vermis-maker com.ruoyi.vermins.controller.pc.web.ProjectCaseUserController(前缀 /web/projectCase)
// 说明:案例实体类型统一定义在本文件(含首页「案例库精选」消费的 ProjectCaseVo)。
import type { BusinessType } from '@/types/api'
/** 项目规模区间 */
export type ScaleRange = 'lt50w' | '50to100w' | '100to300w' | '300to500w' | 'gt500w'
/**
* 业务类型筛选项。
*
* <p>选项不是前端写死的,而是取自 GET /web/projectCase/categories 返回的
* {@link ProjectCaseStatisticsVo#projectCaseCounts}——后端按项目提报表统计后下发。</p>
*/
export interface CaseTypeOption {
/**
* 下发值 = **提报主键 id**(如 "2099753433048526850")。
*
* <p>必须用 id 而非类型名:案例表 business_type 列实测存的就是提报主键 id,
* 传类型名(如「硬件电路」)后端按原值精确匹配不到 → 实测 total=0。</p>
*/
value: string
/** 展示名(后端下发的 businessType 名称,如「硬件电路」) */
label: string
/** 该业务类型的案例数(后端统计口径,可能为 0) */
count: number
}
/** 案例附件(页面消费,由后端 ProjectCaseAttachmentVo 映射而来) */
export interface CaseAttachment {
/** 原始文件名 */
name: string
/** 文件大小(后端已格式化的文本形式,可能为空) */
size: string
/** 附件访问地址 */
url: string
}
/**
* 案例正文区块(后端「自定义目录」一项 = 页面一张白卡)。
*
* <p>标题由后端下发,前端不写死:增删区块(项目背景 / 客户痛点 / 需求描述 / 交付物 …)
* 完全由 vm_project_case_catalog 数据决定。</p>
*/
export interface CaseSection {
/** 区块标题(vm_project_case_catalog.title) */
title: string
/** 区块内容:富文本 HTML(可能内嵌 img / video) */
content: string
}
/** 案例 */
export interface Case {
@@ -18,21 +61,219 @@ export interface Case {
industry: string
duration: string
clientType: string
background: string
painPoints: string
requirements: string
deliverables: string
/**
* 正文区块列表(标题 + 富文本)。
*
* <p>来自后端 catalogs(vm_project_case_catalog,按 sort_order 升序);
* 列表 / 精选接口不下发,故那些场景为空数组。</p>
*/
sections: CaseSection[]
/** 附件列表(仅案例详情接口返回,列表页为空) */
attachments?: CaseAttachment[]
video: string
isFavorite: boolean
/** 收藏量(详情/列表接口下发,本地收藏翻转时同步 ±1) */
favoriteCount: number
}
/** 案例筛选条件 */
/**
* 案例筛选条件。
*
* <p>三类选项全部由后端下发,前端不写死,且都来自 GET /categories({@link CaseSelectionModel}):
* 业务类型({@link CaseTypeOption})、所属行业、项目规模;仅完成日期区间由前端生成。</p>
*/
export interface CaseQuery {
category: BusinessType | 'all'
/** 业务类型**提报主键 id**(多选,空数组 = 全部;值域见 CaseTypeOption.value) */
categories: string[]
/** 所属行业名称(多选,空数组 = 全部;值域见 BusinessIndustryOption.name) */
industries: string[]
scales: ScaleRange[]
/** 项目规模档位(多选,空数组 = 全部;整档对象直接下发,值域见 /categories) */
scales: ScaleLimit[]
dateRange: [string, string] | null
/** 只看收藏(对应后端 isFavorite=1) */
favoriteOnly: boolean
keyword: string
page: number
pageSize: number
}
/* ============================ 后端 VO / BO ============================ */
/**
* GET /web/projectCase/list 的查询条件(后端 ProjectCaseUserBo)。
*
* <p>businessTypes 按案例表 business_type 列的**原值**精确匹配(in),而该列实测存的是
* **提报主键 id**(非类型名),所以前端下发的是 CaseTypeOption.value(id)。</p>
*/
export interface ProjectCaseUserBo {
/** 业务类型提报主键 id 列表 */
businessTypes?: string[]
/** 业务/行业名称列表 */
businessIndustries?: string[]
/** 规模区间列表(区间之间取并集) */
scales?: ScaleLimit[]
/** 只看收藏:1 = 是;不传 = 全部 */
isFavorite?: number
/** 关键字(案例名 / 客户类型 / 项目周期 / 团队人数) */
key?: string
/** 开始时间 */
beginTime?: string
/** 结束时间 */
endTime?: string
}
/** 规模区间(后端 UpperAndLowerLimitsModel):任一端为空表示该方向不设限 */
export interface ScaleLimit {
lowerLimit?: number | null
upperLimit?: number | null
/** 展示样式:以下 / 区间 / 以上 */
model?: string | null
/** 档位文字(后端拼好,如「28–46万」) */
label?: string | null
sort?: number | null
}
/** 所属行业选项(后端 BusinessIndustryModel) */
export interface BusinessIndustryOption {
name: string | null
sort: number | null
}
/**
* GET /web/projectCase/categories —— 筛选面板选项。
*
* <p>左侧筛选**除完成时间外**的选项全部由本接口一次下发:
* 业务类型({@link CaseSelectionModel.projectCaseStatisticsVo})、
* 所属行业({@link CaseSelectionModel.businessIndustry})、
* 项目规模({@link CaseSelectionModel.upperAndLowerLimits})。</p>
*/
export interface CaseSelectionModel {
businessIndustry: BusinessIndustryOption[] | null
upperAndLowerLimits: ScaleLimit[] | null
/** 业务类型统计(承载业务类型筛选项与案例总数) */
projectCaseStatisticsVo: ProjectCaseStatisticsVo | null
}
/** 案例总数 + 各业务类型案例数 */
export interface ProjectCaseStatisticsVo {
/** 案例总数(口径:未删除且在架) */
total: number | null
/** 各业务类型的案例数(按案例数倒序) */
projectCaseCounts: ProjectCaseCountVo[] | null
}
/** 单个业务类型的案例数 */
export interface ProjectCaseCountVo {
/** 业务类型原值(提报主键 id 的字符串形式) */
id: string | null
/** 业务类型名称 */
businessType: string | null
caseCount: number | null
}
/**
* GET /web/projectCase/list —— 案例分页列表 + 业务数据统计。
*
* <p>统计口径是「本次查询结果」,即跟随筛选条件变化,不是全表统计。</p>
*/
export interface ProjectCaseAndCaseCountVo {
projectCasePage: ProjectCasePageVo | null
projectCaseStatisticsVo: ProjectCaseStatisticsVo | null
}
/** 分页体(后端 PageResult:rows + total) */
export interface ProjectCasePageVo {
total: number
rows: ProjectCaseVo[]
}
/** 案例库视图对象(列表 / 详情 / mostViewed 共用;后端 Long 主键序列化为字符串) */
export interface ProjectCaseVo {
id: string
/** 案例编号 */
caseNo: string | null
/** 案例名称 */
caseName: string | null
/** 业务类型原值(案例表该列存提报主键 id 或类型名称) */
businessType: string | null
/** 业务类型名称(后端 JOIN 提报表翻译后的展示名) */
projectTypeName: string | null
/** 业务/行业 */
businessIndustry: string | null
/** 上下架:1 上架,0 下架 */
enabled: number | null
/** 规模数值(单位:万) */
scale: number | null
/** 预览量(详情接口每次调用自增 1) */
previewCount: number | null
/** 浏览量(已废弃,勿用于排序) */
viewCount: number | null
/** 收藏量 */
favoriteCount: number | null
/** 封面(后端下发的是 OSS 文件标识,未必是可直接访问的 URL) */
coverUrl: string | null
/** 客户类型 */
customerType: string | null
/** 所属行业 */
industry: string | null
/** 项目周期 */
projectCycle: string | null
/** 服务团队人数 */
serviceTeam: number | null
/** 完成时间 yyyy-MM-dd */
completedAt: string | null
/** 是否收藏:1 是,0 否(详情与列表接口均会回填) */
isFavorite: number | null
/**
* 项目背景富文本段列表(仅详情返回)。
*
* @deprecated 后端已改用 {@link ProjectCaseVo.catalogs}(带标题的自定义目录),该字段恒为 null。
*/
backgrounds: ProjectCaseBackgroundVo[] | null
/** 正文区块列表(仅详情返回;后端按 sort_order、id 升序下发) */
catalogs: ProjectCaseCatalogVo[] | null
/** 附件列表(仅详情返回) */
attachments: ProjectCaseAttachmentVo[] | null
/** 更新时间 yyyy-MM-dd HH:mm:ss */
updateTime: string | null
}
/**
* 案例项目背景富文本段(vm_project_case_detail)。
*
* @deprecated 已被 {@link ProjectCaseCatalogVo} 取代(新结构带 title),后端恒下发 null。
*/
export interface ProjectCaseBackgroundVo {
id: string | null
projectId: string | null
/** 富文本 HTML */
content: string | null
sortOrder: number | null
}
/** 案例自定义目录 / 正文区块(vm_project_case_catalog) */
export interface ProjectCaseCatalogVo {
id: string | null
projectId: string | null
/** 目录标题(如「项目背景」「客户痛点」) */
title: string | null
/** 目录内容富文本 HTML */
content: string | null
/** 排序值,升序 */
sortOrder: number | null
}
/** 案例附件(vm_project_case_attachment) */
export interface ProjectCaseAttachmentVo {
id: string | null
projectId: string | null
/** OSS 标识 */
ossId: string | null
/** 原始文件名 */
originalName: string | null
/** 文件大小(文本形式) */
fileSize: string | null
/** 附件访问地址 */
url: string | null
sortOrder: number | null
}

View File

@@ -1,20 +1,181 @@
// 需求提报实体(M3,page-architecture)
// 需求提报(对接真实后端)
// 草稿:POST /web/workOrderDraft/{getDraft,add,upload,reset} → vm_work_order_draft(WorkOrderDraftBo / Vo)
// 提交:POST /web/requirementPool/create → vm_project_requirement(入参 WorkOrderDraftBo,与草稿同 BO,附件随请求体)
// (同控制器的 /submit 入参是 RdProjectBo、会额外落 vm_rd_project,前端当前未使用)
// 业务类型:GET /web/projectSubmission/list(vm_project_submission,@SaIgnore 免登录)
// 与首页 /web/home-page/service-type-list 是同一个 service 方法,数据完全一致
// 预算范围:GET /system/dict/data/type/vm_budget_range(字典)
// 注意:草稿 / 提交 / 字典 需登录态(匿名实测 401);业务类型列表免登录
import type { BusinessType, FileInfo } from '@/types/api'
/** 业务类型图标标识(对应前端内联 SVG,见 views/demand/cpns/BizTypeIcon.vue) */
export type BizIconKey = 'code' | 'chip' | 'iot' | 'factory' | 'other'
/** 预算范围 */
export type BudgetRange = 'lt1w' | '1to5w' | '5to10w' | 'gt10w' | ''
/** 需求提报表单 */
export interface Demand {
businessType: BusinessType
projectName: string
attachments: FileInfo[]
coreDescription: string
expectedDuration: number | ''
budgetRange: 'lt1w' | '1to5w' | '5to10w' | 'gt10w' | ''
userName: string
userPhone: string
remark: string
status: 'draft' | 'submitted'
/** 字典项(GET /system/dict/data/type/{dictType}) */
export interface DictOption {
dictLabel: string
dictValue: string
dictSort: number
}
/** 项目提报列表项(GET /web/projectSubmission/list,只取业务类型卡片用得到的字段) */
export interface SubmissionBizTypeVo {
/** 提报主键(业务详情接口 /web/projectSubmission/{id} 用它) */
id: string | number
/** 业务类型(既作展示名,也作下发给后端的值) */
businessType: string | null
/** 标题 */
title: string | null
/** 副标题(卡片一句话描述的数据源) */
subtitle: string | null
/** 封面(⚠️ 实测存的是 OSS 文件标识,非完整地址) */
coverUrl: string | null
/** Logo(⚠️ 同上,需经 /resource/oss/listByIds 换取真实地址) */
logoUrl: string | null
}
/**
* 业务类型卡片选项。
*
* <p>值域**完全由接口决定**:提报列表返回几条就渲染几张卡,前端不追加任何兜底项
* (「其他」也不例外——接口有才显示,没有就不显示)。字段映射:`businessType` 作下发值、
* `title` 作展示名、`subtitle` 作描述、`logoUrl` 作图标。但后端这两个字段目前是**半成品**——
* subtitle 常与类型名重复、logo 存的是 OSS 文件标识——故 desc / icon 保留前端映射表兜底
* (见 `utils/bizTypeMeta.ts`,只按类型名补描述/图标,不产生新选项)。</p>
*/
export interface BizTypeOption {
/** 下发给后端的值(= 提报 businessType,如「互联网软件」) */
value: string
/** 展示名 */
label: string
/** 一句话描述(后端 subtitle 可用时优先,否则用前端映射) */
desc: string
/** 图标标识(前端内联 SVG;`iconUrl` 为空时渲染它) */
icon: BizIconKey
/** 后端 Logo 图片地址(已换链可直接访问时优先渲染,否则回退 `icon` 内联 SVG) */
iconUrl?: string
}
/** 草稿附件(vm_work_order_draft_attachment) */
export interface DemandAttachment {
id?: string | number | null
draftId?: string | number | null
/** OSS 文件主键 */
ossId?: string | number | null
/** 原始文件名 */
originalName?: string | null
/** 文件访问地址 */
url?: string | null
/** 文件大小(字节) */
fileSize?: number | null
/** 文件格式后缀(pdf / docx / zip …) */
type?: string | null
/** 上传时间(BaseEntity.createTime;本地上传时由前端补当前时间) */
createTime?: string | null
}
/** 需求提报表单(字段与后端 WorkOrderDraftBo 对应,另有 1 个纯前端状态字段:budgetManual) */
export interface Demand {
/** 草稿主键(编辑已有草稿时回填) */
id?: string | number | null
/** 业务类型(= 项目提报的 businessType,如「互联网软件」;后端落库存的也是这个名称,不是提报 id) */
bizType: string
/** 项目名称 */
projectName: string
/** 核心需求描述 */
requirementDesc: string
/**
* 预算范围:选预设档位时为字典 `vm_budget_range` 的 dictValue('0'~'3');
* 在第 5 个「手填」输入框里写字时为用户自己填的金额文本(如「80万」)。
*
* <p>⚠️ 手填内容只能放本字段:后端 `WorkOrderDraftBo.budgetCustom` 是被注释掉的、
* `vm_work_order_draft` 表也没有 `budget_custom` 列(VO 上那个 `budgetCustom` 恒为 null),
* 所以没有第二个位置可以承载手填文本。提交落 `vm_project_requirement.budget_range`,
* 该列为 **varchar(30)**,故手填文本限长见 `utils/budgetMeta.ts#MANUAL_BUDGET_MAX`。</p>
*/
budgetRange: string
/** 是否由「手填」输入框提供预算(**纯前端状态**:后端 BO/VO 与两张表都没有该字段,下发前需剔除) */
budgetManual: boolean
/** 预计开发周期(工作日) */
developmentCycle: number | ''
/** 补充备注 */
supplementaryRemarks: string
/** 用户名称 */
userName: string
/** 用户电话 */
userPhone: string
/** 附件列表(后端按此逐条转存为需求附件) */
attachmentInfo: DemandAttachment[]
}
/**
* 直接提报请求体(对应后端 `RdProjectBo` 的可写子集,POST /web/requirementPool/submit)。
*
* <p>⚠️ **当前前端未使用**:提交走的是 `/web/requirementPool/create`(入参 `WorkOrderDraftBo`,
* 与草稿同一个 BO,附件随请求体下发)。本类型对应的是后端 `/submit` 链路,保留以备切回。</p>
*
* <p>⚠️ 与草稿 BO 的字段名**不同**,别混用:业务类型叫 `businessType`(草稿叫 `bizType`)、
* 补充备注叫 `extraRemark`(草稿叫 `supplementaryRemarks`)。</p>
*
* <p>⚠️ 两个字段**不在**此请求体里:</p>
* <ul>
* <li>`developmentCycle`(预计开发周期)—— `vm_project_requirement` 与 `vm_rd_project`
* 两张表都**没有**该列,所以提报链路无处存放(草稿表 `vm_work_order_draft.development_cycle`
* 里有,提交后即丢失,草稿路径同样如此);</li>
* <li>`attachmentInfo` —— `RdProjectBo` 无附件字段,后端按登录用户从**已落库草稿**里
* 按 `ossId` 搬运到 `vm_project_requirement_attachment`(故走该链路时调用方需先落一次草稿,
* 这也正是前端改回 `/create` 的原因之一)。</li>
* </ul>
*
* <p>`projectNo` / `sourceType` / `priority` / `status` / `progress` 在 BO 上虽标了 `@NotBlank`,
* 但注解都挂 `AddGroup` / `EditGroup`,而控制器是裸 `@Validated`(未指定分组)⇒ 实际**不生效**;
* 这些值由后端统一生成/兜默认(`project_no` 复用需求编号、`source_type=EXTERNAL`、
* `priority=UNASSESSED`、`status=IN_PROGRESS`、`progress=0`),前端不下发。</p>
*/
export interface DemandSubmitBo {
/** 业务类型名称(如「互联网软件」;后端落库存的是名称,不是提报 id) */
businessType: string
/** 项目名称 */
projectName: string
/** 核心需求描述 */
requirementDesc: string
/** 联系人姓名(缺失时后端回退取登录用户资料) */
customerName: string
/** 联系人电话(同上) */
customerPhone: string
/** 预算范围:预设档位 = 字典码值;手填 = 用户输入的金额文本 */
budgetRange: string | null
/** 补充备注(对应草稿的 supplementaryRemarks) */
extraRemark: string | null
}
/** 草稿详情(后端 WorkOrderDraftVo,取前端用得到的子集) */
export interface DemandDraftVo {
id: string | number | null
bizType: string | null
projectName: string | null
requirementDesc: string | null
/** 预算范围:预设档位=字典 dictValue;手填=用户在第 5 个输入框里写的文本(前端据此反推 budgetManual) */
budgetRange: string | null
/** ⚠️ 后端 VO 声明了该字段但**恒为 null**(Bo 里被注释、表无该列)→ 不要依赖它回填手填内容 */
budgetCustom: string | null
developmentCycle: number | null
supplementaryRemarks: string | null
userName: string | null
userPhone: string | null
attachmentInfo: DemandAttachment[] | null
}
/** 附件上传返回(后端 SysOssVo) */
export interface OssUploadVo {
ossId: string | number
url: string
originalName: string
fileSuffix: string
}
/** OSS 换链返回项(GET /resource/oss/listByIds/{ids} 的子集) */
export interface OssUrlVo {
ossId: string | number
/** 可直接访问的地址(阿里云 OSS 公网 URL) */
url: string
}

View File

@@ -1,7 +1,14 @@
// 首页实体(PRD 3.1/3.2,page-architecture M1)
// 首页类型(VIEW 类型 + 后端真实 VO 类型 + 门户入口类型,统一归位于 types/business/home.d.ts)
//
// - VIEW 类型(BusinessLine / HomeStats / ServiceType / ServiceContentBlock / QuickLink):页面消费层
// - 后端 VO 类型(Project* / BannerVo / AgreementFormVo / ProjectCaseVo):vermis-maker
// com.ruoyi.vermins.controller.pc.web.UserSideWebHomePageController 与 ProjectCaseUserController 的真实返回
// - 门户入口类型(PortalEntry* / EntryTone / PortalEntryCard):IPortalEntryUserController,属首页快捷入口功能
import type { BusinessType } from '@/types/api'
/* ============================ VIEW 类型(页面消费层) ============================ */
/** 数据看板 */
export interface HomeStats {
pending: number
@@ -9,7 +16,7 @@ export interface HomeStats {
total: number
}
/** 快捷入口 */
/** 快捷入口(VIEW 层占位,实际入口由门户接口驱动) */
export interface QuickLink {
label: string
path: string
@@ -19,6 +26,19 @@ export interface QuickLink {
export interface ServiceType {
label: string
desc: string
/** 图标(后端 icon_url 原样透传:图片地址时渲染为图,否则用名称首字兜底) */
icon?: string
}
/**
* 服务内容区块(对应后端富文本目录 `vm_project_submission_catalog` 的一条:
* 一条目录 = 详情页的一个「服务内容」区块,content 为富文本 HTML,可能内嵌图片/视频)
*/
export interface ServiceContentBlock {
/** 区块标题(后端 catalog.title,可为空) */
title: string
/** 区块内容(富文本 HTML 原文,直接渲染) */
content: string
}
/** 业务线(首页业务卡片 + 业务详情页共用) */
@@ -29,6 +49,179 @@ export interface BusinessLine {
slogan: string
icon: string
serviceContent: string
/** 服务内容区块列表(一条富文本目录一项,详情页逐块渲染,保留 HTML) */
contents: ServiceContentBlock[]
serviceTypes: ServiceType[]
video: string
}
/* ============================ 后端真实 VO 类型 ============================ */
/** GET /web/home-page/project-status-count —— 项目状态统计 */
export interface ProjectStatusCount {
/** 项目数量 */
projectCount: number
/** 进行中的项目数目 */
ongoingProjectCount: number
/** 待处理数目 */
pendingProjectCount: number
}
/** 服务类型子项(service-type-list / service-type-detail 内含) */
export interface ProjectServiceTypeVo {
id: number | null
projectId: number | null
serviceName: string | null
iconUrl: string | null
sortOrder: number | null
}
/** 项目背景(富文本) */
export interface ProjectBackgroundVo {
id: number | null
projectId: number | null
content: string | null
sortOrder: number | null
}
/** 项目提报视图对象:service-type-list / service-type-detail/{id} 的返回 */
export interface ProjectSubmissionVo {
id: number
businessType: string | null
title: string | null
subtitle: string | null
/** 是否启用:1 启用,0 停用 */
enabled: number | null
coverUrl: string | null
logoUrl: string | null
serviceTypes: ProjectServiceTypeVo[]
backgrounds: ProjectBackgroundVo[]
/** yyyy-MM-dd HH:mm:ss */
updateTime: string | null
}
/** GET /web/home-page/service-type-detail/{id} —— 项目提报详情(聚合 VO:主表单对象,三个子表均为一对多 List) */
export interface ProjectSubmissionInfo {
projectSubmission: ProjectSubmissionFlat | null
/** 一对多:服务类型列表 */
projectServiceType: ProjectServiceTypeInfo[] | null
/** 一对多:附件列表 */
projectSubmissionAttachment: ProjectSubmissionAttachmentInfo[] | null
/** 一对多:富文本目录列表 */
projectSubmissionCatalog: ProjectSubmissionCatalogInfo[] | null
}
/** 主表 vm_project_submission(详情接口下发) */
export interface ProjectSubmissionFlat {
id: number | null
businessType: string | null
title: string | null
subtitle: string | null
enabled: number | null
coverUrl: string | null
logoUrl: string | null
}
/** 服务类型 vm_project_service_type(详情接口各取一条) */
export interface ProjectServiceTypeInfo {
id: number | null
projectId: number | null
serviceName: string | null
iconUrl: string | null
sortOrder: number | null
}
/** 附件 vm_project_submission_attachment(详情接口各取一条) */
export interface ProjectSubmissionAttachmentInfo {
id: number | null
projectId: number | null
originalName: string | null
fileSize: string | null
url: string | null
sortOrder: number | null
}
/** 目录(富文本)vm_project_submission_catalog(详情接口各取一条) */
export interface ProjectSubmissionCatalogInfo {
id: number | null
projectId: number | null
title: string | null
content: string | null
sortOrder: number | null
}
/** GET /web/home-page/carousel-list —— 轮播图 */
export interface BannerVo {
id: number | null
imageUrl: string | null
sort: number | null
/** 是否启用(true 启用,false 停用) */
enabled: boolean | null
}
/** GET /web/home-page/platform-intro、contact-info —— 协议/静态文章富文本 */
export interface AgreementFormVo {
id: number | null
/** 协议类型(目前只有「关于我们」一条) */
type: string | null
/** 文章内容(富文本 HTML) */
content: string | null
}
// 案例库的实体类型与 VO(ProjectCaseVo / ProjectCaseUserBo / CaseSelectionModel 等)
// 已按「一页一套」归位到 `@/types/business/cases`,本文件不再重复定义。
/* ============================ 门户入口(首页快捷入口功能) ============================ */
/**
* 后端 IPortalEntryVo(入口目录 + 用户个性化分组)
*
* <p>注意字段来源分两处:</p>
* - 登录分支(selectByUserId):name / id / icon / route / groupType / sortOrder / status,
* 其中 id 取的是 vm_portal_entry.id(入口目录主键);
* - 匿名分支(selectVoList,MyBatis-Plus 自动 SQL):多带 entryId 等目录字段,groupType 为目录默认值。
*/
export interface PortalEntryVo {
/** 入口主键 */
id: number | null
/** 入口标识 */
entryId: string | null
/** 显示名 */
name: string | null
/** 图标名 / URL */
icon: string | null
/** 跳转路由 */
route: string | null
/** 权限标识(空 = 无需权限) */
permission: string | null
/** 1 = 快捷、0 = 其他 */
groupType: string | null
/** 组内顺序 */
sortOrder: number | null
/** 1 = 启用、0 = 停用 */
status: boolean | null
}
/** 入口分组:1 = 快捷入口、0 = 其他入口 */
export type PortalEntryGroup = '1' | '0'
/** 页面消费的入口卡片(视图层类型,与后端字段解耦) */
export interface PortalEntryCard {
/** 入口 id(切换分组时回传后端) */
id: number
/** 显示名 */
label: string
/** 跳转路由(可能为空,为空时不可点击跳转) */
route: string
/** 当前分组:1 = 快捷、0 = 其他 */
groupType: PortalEntryGroup
/** 图标:emoji 字符或图片地址 */
icon: string
/** icon 是否为图片地址 */
isImage: boolean
/** 图标底色主题 */
tone: EntryTone
}
/** 图标底色主题(设计稿为五色循环) */
export type EntryTone = 'purple' | 'green' | 'blue' | 'amber' | 'red' | 'cyan'

View File

@@ -2,8 +2,108 @@
import type { BusinessType, FileInfo } from '@/types/api'
/** 项目状态 */
export type ProjectStatus = '待立项' | '进行中' | '待验收' | '已完成' | '售后中' | '已取消'
/**
* 项目状态(详情页展示口径,中文)。
*
* <p>取值 = 两张表状态码值的并集经 {@link PROJECT_STATUS_LABEL} 映射后的中文:
* 需求侧 `INITIATING`(待立项)/ `CANCELLED`(已取消),项目侧 `IN_PROGRESS` /
* `PENDING_ACCEPTANCE` / `PENDING_INTERNAL_ACCEPTANCE` / `COMPLETED` / `AFTER_SALE`。
* 「待内部验收」数量少但确实存在,故保留。</p>
*/
export type ProjectStatus =
| '待立项'
| '进行中'
| '待验收'
| '待内部验收'
| '已完成'
| '售后中'
| '已取消'
/**
* 列表行状态码值 —— 后端 `COALESCE(rd_project.status, project_requirement.status)`。
*
* <p>列表接口已改为「需求表为主 + LEFT JOIN 项目表」:已立项取项目状态,未立项回落到需求状态。
* 因此这里 = 两张表状态的<b>并集</b>:<br>
* · 项目表 `vm_rd_project.status`:IN_PROGRESS / PENDING_ACCEPTANCE /
* PENDING_INTERNAL_ACCEPTANCE / COMPLETED / AFTER_SALE / CANCELLED<br>
* · 需求表 `vm_project_requirement.status` 额外提供:<b>INITIATING(待立项)</b></p>
*
* <p>所以设计稿画的「待立项」现在是真实码值(= 需求已提报、尚未立项)。</p>
*/
export type RdProjectStatus =
| 'INITIATING'
| 'IN_PROGRESS'
| 'PENDING_ACCEPTANCE'
| 'PENDING_INTERNAL_ACCEPTANCE'
| 'COMPLETED'
| 'AFTER_SALE'
| 'CANCELLED'
/**
* 项目列表行 —— 后端 `RdProjectVo`(`GET /web/requirementPool/list` 的 `requirementPage.rows`)。
*
* <p>`id` 后端是 Long,但全局 Jackson 配置序列化成字符串(已实测),故按 string 声明。
* `progress` 是 Integer,可能为 null(库里大量为空)⇒ 展示层要兜底成「—」。</p>
*/
export interface RdProjectVo {
id: string
/** 外部项目关联的需求主键,内部项目为 null */
requirementId?: string | null
projectNo: string
/** EXTERNAL / INTERNAL */
sourceType?: string | null
/** 业务类型快照:存的是**提报表主键 id**(如 '1'),不是类型名 ⇒ 展示要经 label 映射 */
businessType?: string | null
projectName: string
requirementDesc?: string | null
customerName?: string | null
customerPhone?: string | null
submitUserId?: string | null
submitTime?: string | null
/** HIGH / MEDIUM / LOW */
priority?: string | null
status?: string | null
progress?: number | null
planStartTime?: string | null
planEndTime?: string | null
actualStartTime?: string | null
actualEndTime?: string | null
customerServiceName?: string | null
technicalOwnerName?: string | null
createTime?: string | null
updateTime?: string | null
}
/** 业务类型统计单项(后端 `ProjectRequirementCountVo`) */
export interface ProjectBusinessTypeCount {
/** 提报表主键 id(与列表筛选参数 `businessType` 同口径) */
businessTypeId: string
/** 业务类型名称,如「互联网软件」 */
businessTypeLabel: string
count: number
}
/**
* 业务数据统计(后端 `ProjectRequirementStatisticsVo`)。
*
* <p>来源是独立端点 `GET /web/requirementPool/getFilterConditions`(**全量口径**,不随筛选 / 分页变化)。</p>
*
* <p>⚠️ 列表接口 `/list` 响应里的同名字段后端仍是注释状态、实测恒为 `null`,**不要**拿它当统计源。
* 这里仍按「可能为 null」处理:拿不到时筛选栏只剩「全部」一行。</p>
*/
export interface ProjectStatistics {
total?: number | null
businessTypeCounts?: ProjectBusinessTypeCount[] | null
}
/** 列表响应(后端 `ProjectRequirementAndCountVo`:分页数据 + 统计平级) */
export interface ProjectListResult {
requirementPage?: {
total: number
rows: RdProjectVo[]
} | null
requirementStatistics?: ProjectStatistics | null
}
/** 里程碑节点 */
export interface Milestone {
@@ -48,7 +148,11 @@ export interface AfterSalesPayload {
attachments: FileInfo[]
}
/** 项目 */
/**
* 项目详情(**详情页仍是前端 mock 形态**,尚未接后端)。
*
* <p>列表已改走真实接口(见 {@link RdProjectVo}),本类型只服务详情页那几个组件。</p>
*/
export interface Project {
id: string
projectNo: string
@@ -68,12 +172,201 @@ export interface Project {
afterSales?: AfterSales
}
/** 项目列表查询条件 */
/** 业务类型筛选项 + 件数(左侧筛选栏「业务类型」列表;「全部」的 value 为 'all') */
export interface ProjectTypeCount {
/** 'all' 表示「全部」;否则是后端下发的 `businessTypeId` */
value: string
/** 展示文案(后端 `businessTypeLabel`;「全部」由前端给) */
label: string
/** 该类型下的项目件数(后端全量口径;统计缺失时为 0) */
count: number
}
/**
* 项目列表查询条件(**已是后端口径**)。
*
* <p>与 `RdProjectBo` 的字段对应关系:`keyword → key`、`businessType → businessType`(提报表 id)、
* `status → status`(码值)、`startDate/endDate 或 timeShortcut → beginTime/endTime`(两者都落在
* `create_time` 上)、`pageNum/pageSize → PageQuery`。</p>
*
* <p>用空串表示「不限」而不是 'all'——空串直接不出现在 querystring 里,与后端 `eqIfText` 的语义一致。</p>
*/
export interface ProjectQuery {
keyword: string
businessType: BusinessType | 'all'
status: ProjectStatus | 'all'
/** '' = 全部;否则为后端统计下发的 businessTypeId */
businessType: string
/** '' = 全部;否则为 {@link RdProjectStatus} 码值 */
status: string
/** 提交时间快捷区间;与 startDate/endDate 互斥(选其一会把另一组清空) */
timeShortcut: 'all' | '3m' | '6m' | '1y'
page: number
/** 提交时间自选区间起(YYYY-MM-DD,空串 = 不限) */
startDate: string
/** 提交时间自选区间止(YYYY-MM-DD,空串 = 不限) */
endDate: string
pageNum: number
pageSize: number
}
// ─────────────────────────────────────────────────────────────────────────────
// 详情契约:后端 `ProjectRequirementUserVo`(GET /web/requirementPool/get/{id})
//
// 与列表不同,详情是「需求表为主 + 各子表按需填充」:需求未立项时 `vm_rd_project`
// 没有对应行,项目侧字段(projectStage / projectMilestones / members / billPlans /
// invoices / contracts)一律为空,需求侧字段(基本信息 / requirementAttachments /
// afterSale)照常返回。
// ─────────────────────────────────────────────────────────────────────────────
/** 开发进度(后端 `ProjectStage`,表 `vm_project_stage`) */
export interface RequirementProjectStage {
id: string
projectId: string | null
requirementId: string | null
stageName: string | null
status: string | null
startTime: string | null
planTime: string | null
finishTime: string | null
/** 开发进度百分比(列表页那个 progress 的详情侧来源) */
developmentProgress: number | null
sort: number | null
}
/** 里程碑(后端 `RdProjectMilestone`,表 `vm_rd_project_milestone`) */
export interface RequirementMilestone {
id: string
projectId: string | null
milestoneName: string | null
sortOrder: number | null
status: string | null
startTime: string | null
endTime: string | null
parallelFlag: boolean | null
}
/** 需求附件(后端 `ProjectRequirementAttachment`,表 `vm_project_requirement_attachment`) */
export interface RequirementAttachment {
id: string
requirementId: string | null
/** 附件所属阶段,实测有 `REQUIREMENT` */
phase: string | null
ossId: string | null
originalName: string | null
url: string | null
fileSize: number | null
sortOrder: number | null
/** 上传时间(继承 BaseEntity,后端会下发) */
createTime?: string | null
}
/** 项目团队(后端 `RdProjectMember`,表 `vm_rd_project_member`) */
export interface RequirementMember {
id: string
projectId: string | null
userId: string | null
userName: string | null
roleKey: string | null
roleName: string | null
isOwner: boolean | null
}
/** 付款计划(后端 `BillPlan`,表 `vm_bill_plan`) */
export interface RequirementBillPlan {
id: string
projectId: string | null
projectNo: string | null
projectName: string | null
totalAmount: number | null
periods: number | null
currentPeriod: number | null
periodRule: string | null
nextDate: string | null
status: string | null
}
/** 发票(后端 `Invoice`,表 `vm_invoice`) */
export interface RequirementInvoice {
id: string
billId: string | null
invoiceTitle: string | null
taxNo: string | null
invoiceType: string | null
amount: number | null
status: string | null
invoiceNo: string | null
/** 电子发票文件地址 */
invoiceFile: string | null
applyTime: string | null
invoiceDate: string | null
/** 申请时间(继承 BaseEntity,后端会下发) */
createTime?: string | null
}
/** 合同(后端 `Contract`,表 `vm_contract`) */
export interface RequirementContract {
id: string
contractNo: string | null
projectId: string | null
title: string | null
amount: number | null
signStatus: string | null
status: string | null
signDate: string | null
/** 合同文件地址 */
file: string | null
/** 签署时间(继承 BaseEntity,后端会下发) */
createTime?: string | null
}
/** 售后工单(后端 `ProjectRequirementAfterSale`,表 `vm_project_requirement_after_sale`) */
export interface RequirementAfterSale {
id: string
requirementId: string | null
/** 售后原因 */
reason: string | null
status: string | null
requestUserName: string | null
requestTime: string | null
processUserName: string | null
processNote: string | null
processTime: string | null
withdrawReason: string | null
}
/**
* 需求详情响应(后端 `ProjectRequirementUserVo`)。
*
* <p>注意两个「名字骗人」的字段:`attachments` / `afterSales`(复数)后端**从未赋值**,
* 真正有数据的是 `requirementAttachments` 与 `afterSale`(单数)。前端只取后者。</p>
*/
export interface RequirementDetailVo {
id: string
projectNo: string | null
submitUserId: string | null
submitUserName: string | null
submitPhone: string | null
/** 业务类型**中文名**(如「硬件电路」),非英文 code */
businessType: string | null
projectName: string | null
requirementDesc: string | null
priority: string | null
/** 状态码值:需求侧 INITIATING/CANCELLED,项目侧 IN_PROGRESS/… */
status: string | null
customerServiceName: string | null
customerServicePhone: string | null
technicalOwnerName: string | null
internalRemark: string | null
attachmentCount: number | null
createTime: string | null
updateTime: string | null
launchNote: string | null
cancelReason: string | null
/** 未立项时为 null */
projectStage: RequirementProjectStage | null
projectMilestones: RequirementMilestone[]
requirementAttachments: RequirementAttachment[]
members: RequirementMember[]
billPlans: RequirementBillPlan[]
invoices: RequirementInvoice[]
contracts: RequirementContract[]
afterSale: RequirementAfterSale | null
}

View File

@@ -10,6 +10,21 @@ export interface User {
passwordModifiedAt: string
}
/**
* 登录用户资料(GET /system/user/profile,后端 SysUserVo 的子集)。
*
* <p>字段名与后端一致:`nickName` / `phoneNumber`(不是 nickname / phone)。</p>
*/
export interface UserProfileVo {
userId: string
userName: string
nickName: string
phoneNumber: string
email: string
avatarUrl: string
deptName: string
}
/** 账号密码登录参数 */
export interface LoginByAccountParams {
account: string

View File

@@ -7,5 +7,12 @@ declare module 'vue-router' {
title?: string
/** 是否需要登录 */
requiresAuth?: boolean
/**
* 是否展示公共底栏(AppFooter)。
*
* <p>默认不展示:底栏信息量大、只在门户首页作为站点信息区出现,
* 其余页面(列表/详情/登录区)不渲染,避免干扰正文。</p>
*/
footer?: boolean
}
}