40 多个文件没必要从头扫。本文按一条真实调用链串起来看:发起列表 → 创建实例 → 项目处理器 → 任务中心,顺带把目录职责、状态枚举和四个常见二次开发落点讲清楚。
阅读顺序建议
先不要按目录把 40 多个文件从头扫到尾。按这条调用链看,会快很多:
GL_PrjStart.ts
→ WF_CCPrj.CreatePrj()
→ PrjDev2Interface.Prj_CreateBlankPrjID()
→ MyPrj.vue
→ TaskCenter / TaskGener / TaskFlow / TaskBill
记住两个 Handler:
| 场景 | 类名 |
|---|---|
| 运行时 | BP.CCPrj.HttpHandler.WF_CCPrj |
| 管理端 | BP.CCPrj.HttpHandler.WF_Admin_CCPrj |
前端封装在 Comm/PrjComm.ts:
PrjComm.GetHandler() // WF_CCPrj
PrjComm.GetTaskHanlder() // WF_Admin_CCPrj
业务不要写在 Vue 里拼 SQL,也不要写在 Handler 里堆逻辑。Handler 只接参数、转 JSON;真正干活的是 PrjDev2Interface。
一、目录地图
前端 Vue3/src/CCFast/CCPrj
| 文件 | 先看什么 |
|---|---|
Port.vue | 模块入口,按 PageName 分发 |
GenerList/GL_PrjStart.ts | 点模板 → 创建实例 |
GenerList/GL_PrjTodolist.ts 等 | 待办/在途/已完成,复用 GenerList.vue |
TaskCenter.vue | 个人任务工作台 |
MyPrj.vue | 项目处理器,工程模块的 MyFlow |
PrjToolBar.vue | 按状态和角色出按钮 |
TaskGener.vue / TaskFlow.vue / TaskBill.vue | 三类任务页 |
Comm/taskHandler.ts | 任务类型、待办加载、打开哪个组件 |
SearchPrj.vue | 按模板查实例 |
Admin/TemplateList.vue | 模板库 |
Admin/Template/TemplatePrj.ts | 模板实体 + 设计表单/文件工作区 |
Admin/Template/PG_TemplateTaskGroup2Task.ts | 步骤设计(左组右任务) |
FileWorkspace.vue | 文件工作区 |
后端 CCFlow/Components/BP.WF/CCPrj
| 文件 | 先看什么 |
|---|---|
PrjDev2Interface.cs | 全部业务接口 |
WF_CCPrj.cs | 运行时 HTTP 入口 |
WF_Admin_CCPrj.cs | 模板库、步骤、文件工作区配置 |
Template/*.cs | 设计态实体 |
GenerPrj.cs / GenerTask.cs / GenerTaskGroup.cs | 运行态实体 |
GenerWorker.cs | 待办人员 |
PrjTrack.cs / PrjWorkerLog.cs | 轨迹 / 工时日志 |
Template/PrjEnum.cs | PrjSta / TaskSta |
实体写法与流程模块相同:XxxAttr + Entity + Entities + EnMap。改字段先改 EnMap,再改接口,不要只改前端。
二、入口:点一个模板,发生了什么
GL_PrjStart 继承 PageBaseGenerList,页面本身复用流程的 GenerList.vue。它只做三件事:列怎么显示、数据从哪来、点行去哪。
async Init() {
this.PageSize = 0;
this.LinkField = 'Name';
this.GroupFields = 'SortName';
this.HisGLShowModel = GenerListPageShowModel.Windows;
// ...
const handler = new HttpHandler('BP.CCPrj.HttpHandler.WF_CCPrj');
const data = await handler.DoMethodReturnJson('TaskStart_Init');
this.Data = data;
}
async LinkFieldClick(object: Record<string, any>) {
const handler = new HttpHandler('BP.CCPrj.HttpHandler.WF_CCPrj');
handler.AddPara('TemplateNo', object.No);
const PrjID = await handler.DoMethodReturnString('CreatePrj');
const url = `/src/CCFast/CCPrj/MyPrj.vue?PrjNo=${PrjID}`;
return new GPNReturnObj(GPNReturnType.OpenUrlByDrawer90, url, '项目处理');
}
对应后端非常薄:
public string CreatePrj()
{
string templateNo = this.GetRequestVal("TemplateNo");
Int64 oid = PrjDev2Interface.Prj_CreateBlankPrjID(templateNo);
return oid.ToString();
}
待办列表是同一模式。GL_PrjTodolist 调 PrjTodolist_Init,点行同样打开 MyPrj.vue?PrjNo=...。二次开发如果要加“我延期的工程”,优先抄这两个 GL 文件,不要新写一张列表页。
三、创建实例:模板复制到运行时
核心在 Prj_CreateBlankPrjID。建议按注释里的步骤打断点:
- 读
TemplatePrj OID = DBAccess.GenerOID("PrjID"),不要用数据库自增- 状态先写
PrjSta.Draft - 用模板
BillNoFormat调流程现成的WorkFlowBuessRole.GenerBillNo - 复制
TemplateTaskGroup→GenerTaskGroup - 复制
TemplateTask→GenerTask SyncTaskWorkers写待办人EnsurePrjForm保证项目表单和物理表存在PrjTrack.WriteLog记一条“创建工程”
任务主键约定必须守住:
任务组 No = 工程OID + "_" + 模板任务组No
任务 No = 工程OID + "_" + 模板任务No
前端从实例号反推模板任务号时,用的就是这个规则,见 taskHandler.deriveTemplateTaskNo()。乱改编号,流程任务/单据任务会找不到绑定关系。
复制任务时会带上 TaskType 和 RefTemplateNo。通用任务、流程任务、单据任务在实例里只是不同类型的 GenerTask,不是三张表。
草稿规则和流程对齐:
Draft = 1(保存到待办):给发起人在 Idx 最小的任务上插一条GenerWorkerDraft = 2(保存到草稿箱):只建实例,不进待办Draft = 0:不走草稿策略
所以“为什么创建后有的人立刻看到待办,有的没有”,先查模板的 Draft 字段。
四、状态值不要靠猜
Template/PrjEnum.cs 里工程状态不是 0/1/2 递增那么直觉:
Draft = 0; // 草稿
Complete = 1; // 已完成
Runing = 2; // 进行中
Pause = 3;
Invalid = 4;
任务状态:
None = 0; // 未开始
Complete = 1;
Delay = 2;
Pause = 3;
人员状态在 GenerWorker.cs:
UnRead = 0;
Complete = 1;
Read = 2;
Refuse = 3;
前端 PrjComm.PrjStaText() / NodeStaText() / WorkerStaText() 必须和这里一致。工具栏判断也是按这些数字,见 PrjToolBar.vue。
发起校验在 CheckPrjStartData:没任务、没名称、没负责人、日期范围缺日期,都会返回 err@...。前端只要原样提示,不要自己再写一套校验。
完成任务走 Node_SetOver:任务置完成 → 相关 Worker 置完成 → 若全部任务完成则 Prj_SetOver。自动完成工程的开关就在这里,改产品规则优先改这一段。
五、项目处理器:MyPrj 怎么把数据摊开
MyPrj_Init 直接把甘特数据集吐给前端:
public string MyPrj_Init()
{
DataSet ds = PrjDev2Interface.Prj_GetGanttData(this.PrjID);
return BP.Tools.Json.ToJson(ds);
}
MyPrj.vue 自己拼任务组和任务的树,三个 Tab:
gantt:节点进度form:项目表单(Fool 表单,FrmID默认Prj+ 模板编号)log:轨迹
按钮不写死在页面里,而由 PrjToolBar.vue 按 PrjSta + 角色计算。草稿出发起/删除,进行中出暂停/完成/移交/作废,已完成出回滚。表单 Tab 只出保存和打印。
权限字段在 GenerPrj 上:
BtnSaveRoleBtnCompleteRoleBtnTransferRoleBtnPrintRole
二次开发加按钮时:
PrjToolBar加一个 actionMyPrj.vue的handleToolbarAction接住WF_CCPrj加一个薄方法PrjDev2Interface写业务并打 Track
不要只改 Vue。
打开具体任务前会走 Node_CheckAuth / MyPrjFrm_CheckAuth。无权限就停,不要假定点得开就能改。
六、任务中心:按类型打开不同组件
TaskCenter.vue 三个 scope:todo / running / complete,数据来自:
PrjTaskTodolist_InitPrjTaskParticipatingList_InitPrjTaskDoneList_Init
真正决定打开哪一页的是 Comm/taskHandler.ts:
export function getTaskTypeLabel(taskType: number) {
switch (taskType) {
case 1:
return '流程任务';
case 2:
return '单据任务';
default:
return '通用任务';
}
}
export function getTaskHandlerCompUrl(taskType: number) {
if (taskType === 1) return '/@/CCFast/CCPrj/TaskFlow.vue';
if (taskType === 2) return '/@/CCFast/CCPrj/TaskBill.vue';
return '/@/CCFast/CCPrj/TaskGener.vue';
}
| TaskType | 页面 | 背后依赖 |
|---|---|---|
| 0 | TaskGener.vue | PrjWorkerLog 工时日志 + 附件 |
| 1 | TaskFlow.vue | 模板任务绑定的流程 RefTemplateNo / refFlowNo |
| 2 | TaskBill.vue | Fool 表单,绑定单据 |
如果要加第四种任务(例如“绑定报表”):
TemplateTask.TaskType枚举加值- 模板设计里能选
- 创建实例时把类型复制到
GenerTask getTaskHandlerCompUrl增加映射- 新建一个
TaskXxx.vue,继续用loadTaskRuntimeData做鉴权和基本信息
不要为新类型新建一套待办表。待办始终是 Prj_GenerWorker。
七、管理端:模板库和步骤设计
运行时看 WF_CCPrj,设计态看 WF_Admin_CCPrj:
| 方法 | 用途 |
|---|---|
Prj_CreateTemplate | 新建模板 |
TemplateList_Init | 模板库列表和统计 |
TemplateList_Instances | 某模板下的实例 |
TemplateList_DeleteTemplate | 删模板 |
Task_CreateTaskFlow / Task_CreateTaskBill | 给任务绑定流程或单据 |
FileWorkspace_Init / Save | 文件工作区配置 |
前端模板实体 TemplatePrj.ts 上两个入口最常用:
fStep():打开表单设计器,FrmID =Prj+ 模板编号DoFileWorkspaceConfig():没有配置就先 Save 一份,再打开配置实体
步骤设计不是流程图,是 PG_TemplateTaskGroup2Task:左边任务组,右边任务,支持组内排序、拖动。RefKey = GroupNo。新增任务时会带上当前 TemplateNo。
模板主表关键字段:
PTable/ 表单编号BillNoFormatTitleRoleDraftIsCanEditTemplate
改“发起后能不能改任务结构”,看的是 IsCanEditTemplate,不是前端按钮显隐。
八、文件工作区从哪接进去
配置实体:Admin/Template/TemplateFileWorkspaceConfig.ts
表:Prj_FileWorkspaceConfig
字段要点:
ReviewFlowNo:文件送审流程BucketName/ObjectPrefix:对象存储ParticipantsNo:工作区参与人
运行时接口在 WF_CCPrj 后半段:Workspace_Init、File_StartReview、File_BindReviewWork。MyPrj 工具栏的「文件工作区」和任务页里的 TaskFilePanel 都走这套,不是另一套附件表。
改送审规则时,先看文件是如何绑定 WorkID 的,再改流程,不要在 Vue 里写死流程编号。
九、二次开发最常见的四个落点
1. 加一张用户列表
抄 GL_PrjTodolist.ts + WF_CCPrj 一个 xxx_Init + PrjDev2Interface.DB_xxx。列表 UI 继续用 GenerList.vue。
2. 加一个工程按钮
PrjToolBar.vue → MyPrj.vue → WF_CCPrj.MyPrj_Xxx → PrjDev2Interface.Prj_Xxx,最后 PrjTrack.WriteLog。
3. 加业务字段
不要改 GenerPrj 内核。去模板的表单设计器加字段,数据落在 PTable。EnsurePrjForm 已经会创建默认字段和物理表。
4. 改待办口径
待办不是按工程算,是按 Prj_GenerWorker + GenerTask 算。改“谁能看见这条待办”,改 DB_TaskList,不要改 MyPrj.vue 的过滤。
十、调试时一眼能定位的问题
| 现象 | 先看 |
|---|---|
| 发起列表是空的 | DB_Starter 的模板表/类别表是否与当前实体表名一致 |
| 点模板报错 | CreatePrj 是否收到 TemplateNo;模板是否存在 |
| 创建成功但没有待办 | 模板 Draft 是否为 1;GenerWorker 有没有行 |
| 打不开任务 | MyPrjFrm_CheckAuth / Node_CheckAuth |
| 流程任务是空白 | 模板任务 TaskType=1 且绑定了流程号 |
| 单据任务提示未就绪 | 是否走过 Task_CreateTaskBill,FrmID 是否存在 |
| 状态显示错乱 | 是不是把已完成 1 当成了进行中 |
| 工具栏没有完成按钮 | BtnCompleteRole + 是不是负责人/发起人 |
| 项目表单是空的 | EnsurePrjForm 有没有跑,FrmID 是否 Prj + 模板编号 |
Handler 约定沿用 CCFlow:成功返回数据或纯文本,失败返回 err@原因。前端看到 err@ 就提示,不要当 JSON 解析。
十一、一条最小阅读清单
如果只有半天,按这个顺序打开文件:
GenerList/GL_PrjStart.tsWF_CCPrj.CreatePrj/MyPrj_Init/MyPrj_StartPrjDev2Interface.Prj_CreateBlankPrjIDPrjDev2Interface.Prj_Start+CheckPrjStartDataGenerPrj.cs、GenerTask.cs、GenerWorker.csMyPrj.vue+PrjToolBar.vueComm/taskHandler.tsTaskGener.vue(通用任务是默认路径)Admin/Template/PG_TemplateTaskGroup2Task.ts
把这条链走通,再去看文件工作区和甘特配置,不会迷路。本文只解决一件事:代码从哪进、改完往哪写。