【协作】日程、任务、计划与汇报
日程、任务、计划与汇报,由 yudao-module-oa 后端模块的 schedule、task、plan、workreport 包实现,前端实现在 @/views/oa/schedule/list、@/views/oa/task/list、@/views/oa/plan/list、@/views/oa/workreport 目录。
日程用于安排时间,任务用于分配工作和反馈进度,工作计划记录阶段目标,工作汇报记录实际完成情况。四类数据分别保存,首页汇总展示待办任务、工作计划和日程。
本文涉及表如下图所示:
# 1. 日程管理
日程管理,由 OaScheduleController 提供接口(/oa/schedule)。
# 1.1 主表表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
日程表 oa_schedule,保存日程的标题、起止时间、优先级和提醒设置:
CREATE TABLE `oa_schedule` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`type` tinyint NOT NULL COMMENT '日程类型',
`priority` tinyint NOT NULL COMMENT '优先级',
`title` varchar(100) NOT NULL COMMENT '标题',
`description` varchar(1000) DEFAULT NULL COMMENT '描述',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`remind` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否提醒',
`reminded` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已提醒',
PRIMARY KEY (`id`),
KEY `idx_creator_start_time` (`creator`, `start_time`),
KEY `idx_start_time_end_time` (`start_time`, `end_time`)
) ENGINE=InnoDB COMMENT='OA 日程';
① 日程没有独立的所属人字段,通用字段 creator 即日程创建人,idx_creator_start_time 索引支撑【我的日程】按创建人 + 开始时间的查询。创建人和参与人都能查看日程,修改和删除只有创建人可以操作,由 OaScheduleServiceImpl 的 validateScheduleOwner 校验。
② 枚举 type 日程类型(OaScheduleTypeEnum),对应字典 oa_schedule_type:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | REMINDER | 日程提醒 |
2 | HOLIDAY | 假日安排 |
③ 枚举 priority 优先级(OaPriorityEnum),对应字典 oa_priority。该枚举被 OA 多个功能复用,不只用于日程:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | NORMAL | 一般 |
2 | IMPORTANT | 重要 |
3 | URGENT | 紧急 |
④ remind 和 reminded 是一对提醒控制位:remind 由用户在表单勾选,表示这条日程需要提醒;reminded 由系统回写,表示这条日程已经提醒过,避免重复发送。详见 §1.3 日程提醒。
该表包含一个子表:
oa_schedule_participant(日程参与人):在新增/修改日程的【参与人】中维护,保存被邀请人及其阅读回执。
# 1.2 子表结构
参与人表 oa_schedule_participant,保存日程的参与人及其阅读回执:
CREATE TABLE `oa_schedule_participant` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`schedule_id` bigint NOT NULL COMMENT '日程编号',
`user_id` bigint NOT NULL COMMENT '参与人用户编号',
`read_status` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已读',
`read_time` datetime DEFAULT NULL COMMENT '首次阅读时间',
PRIMARY KEY (`id`),
KEY `idx_schedule_id_user_id` (`schedule_id`, `user_id`, `deleted`),
KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB COMMENT='OA 日程参与人';
① schedule_id 关联 oa_schedule 表的 id 字段,user_id 关联 system_users 表的 id 字段。一条记录表示一个后台用户参与一场日程,idx_user_id 索引支撑【我收到的日程】按参与人反查。
② 修改日程时(updateSchedule),按本次提交的参与人列表做增量对比:新增的参与人插入记录,移除的参与人删除记录,保留下来的参与人不动,因此已读状态不会因为一次编辑被清零。
③ read_status 和 read_time 是阅读回执。参与人打开日程详情时调用 updateScheduleReadStatus,只在首次阅读时写入 read_status = 1 和当前时间,重复打开不覆盖 read_time,保证记录的是第一次看到的时刻。
# 1.3 日程提醒
日程提醒由 OaScheduleReminderJob 定时触发,最终执行 OaScheduleServiceImpl 的 sendScheduleReminders:
① 扫描满足「remind = 1 且 reminded = 0 且开始时间落在未来 N 小时内」的日程。N 为提前提醒的小时数,由配置项 yudao.oa.schedule.remind-before-hours 控制,默认 24 小时。
② 逐条日程调用 sendScheduleReminder 发送,每条日程使用独立事务(通过 getSelf() 自身代理调用),某条日程失败只记录日志,不影响其它日程。
③ 发送前会二次校验日程仍需提醒,避免任务重叠执行或日程被编辑后重复发送。接收人为参与人合并创建人,同一用户只接收一次提醒。发送完成后回写 reminded = 1。
修改日程会重新进入提醒周期
修改日程时会把 reminded 复位为 0。因此调整了开始时间的日程,会按新时间重新计算并再次提醒一次。
# 1.4 管理后台
对应 [OA 办公协同 -> 日程管理 -> 日程管理] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/oa/schedule/list 目录。
# 列表与日历
列表按标题、日程类型及本人或共享范围查询日程;切换到【我的日历】,在 @/views/oa/schedule/calendar 中按日历查看工作安排。


# 新增与修改
点击【新增】按钮,弹出 OaScheduleForm.vue 表单。填写日程标题、开始时间、结束时间、优先级和内容,选择参与人及提醒设置,点击【确定】保存。修改时回显原日程和参与人。

# 查看与阅读
点击日程查看详情。本人日程和共享给本人的日程由后端按创建人、参与关系筛选;参与人阅读后,通过 PUT /oa/schedule/update-read-status 更新本人的阅读回执。

# 删除
创建人点击【删除】并确认后,删除日程及其参与关系。删除后不再参与后续提醒。
# 2. 任务管理
任务管理,由 OaTaskController 提供接口(/oa/task)。
# 2.1 主表表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
任务表 oa_task,保存任务的标题、描述、起止时间和整体状态:
CREATE TABLE `oa_task` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`type` tinyint NOT NULL COMMENT '任务类型',
`status` tinyint NOT NULL COMMENT '任务整体状态',
`title` varchar(100) NOT NULL COMMENT '任务标题',
`description` varchar(2000) NOT NULL COMMENT '任务描述',
`comment` varchar(1000) DEFAULT NULL COMMENT '任务评价',
`publish_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '业务发布时间',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`top` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否置顶',
`canceled` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否取消',
PRIMARY KEY (`id`),
KEY `idx_creator_status` (`creator`, `status`),
KEY `idx_start_time_end_time` (`start_time`, `end_time`)
) ENGINE=InnoDB COMMENT='OA 任务';
① 和日程一样,任务没有独立的发布人字段,通用字段 creator 即发布人,idx_creator_status 索引支撑【我发布的】按发布人 + 状态查询。修改、删除、评价任务由 OaTaskServiceImpl 的 validateTaskPublisher 校验只能发布人操作。
② 枚举 type 任务类型(OaTaskTypeEnum),对应字典 oa_task_type:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | WORK | 公事 |
2 | PERSONAL | 私事 |
③ status 是任务整体状态。发布人在表单中设置的状态会覆盖全部接收人,接收人自行反馈时则由所有接收人的状态汇总得出。详见 §2.3 状态流转。
④ canceled 是取消标记,不占用 status 的值。任务被取消后,接收人不能再提交反馈,同时接收人才被允许把任务从【我的任务】移除。top 置顶标记只影响列表排序。
⑤ publish_time 是业务意义上的发布时间,默认取当前时间,与通用字段 create_time 的数据库落库时间区分开,便于补录历史任务。
该表包含两个子表:
oa_task_receiver(任务接收人):在新增/修改任务的【接收人】中维护,保存每名接收人各自的执行状态。oa_task_log(任务日志):在【新增反馈】时追加,保存每次状态变更和反馈内容。
# 2.2 子表结构
接收人表 oa_task_receiver,保存每名接收人各自的任务状态:
CREATE TABLE `oa_task_receiver` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`task_id` bigint NOT NULL COMMENT '任务编号',
`user_id` bigint NOT NULL COMMENT '接收人用户编号',
`status` tinyint NOT NULL COMMENT '接收人任务状态',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_task_id_user_id` (`task_id`, `user_id`, `deleted`),
KEY `idx_user_id_status` (`user_id`, `status`)
) ENGINE=InnoDB COMMENT='OA 任务接收人';
① task_id 关联 oa_task 表的 id 字段,user_id 关联 system_users 表的 id 字段。uk_task_id_user_id 唯一索引保证同一个人在同一个任务中只有一条接收记录,不会被重复分派。idx_user_id_status 索引支撑【我的任务】按接收人 + 状态查询,以及首页的待办任务统计。
② status 是该接收人自己的执行状态,取值与主表 status 同为 OaTaskStatusEnum。多人任务中各接收人的进度互相独立,任务详情里的进度条即读取本表。
任务日志表 oa_task_log,保存状态变更和反馈内容:
CREATE TABLE `oa_task_log` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`task_id` bigint NOT NULL COMMENT '任务编号',
`user_id` bigint NOT NULL COMMENT '操作人用户编号',
`status` tinyint DEFAULT NULL COMMENT '变更后的任务状态',
`content` varchar(1000) DEFAULT NULL COMMENT '反馈内容',
PRIMARY KEY (`id`),
KEY `idx_task_id_create_time` (`task_id`, `create_time`)
) ENGINE=InnoDB COMMENT='OA 任务日志';
① task_id 关联 oa_task 表的 id 字段,user_id 关联 system_users 表的 id 字段,记录本次反馈由谁提交。
② 本表只追加不修改,每调用一次 feedbackTask 插入一条记录,因此保留了完整的反馈轨迹。idx_task_id_create_time 索引支撑任务详情按时间正序展示反馈流水。
③ status 保存本次变更后的状态,与 content 反馈内容一起记录。发布人和接收人的反馈都写入本表,通过 user_id 区分。
# 2.3 状态流转
任务状态由 OaTaskServiceImpl 控制,主表 oa_task.status 和子表 oa_task_receiver.status 共用枚举 OaTaskStatusEnum,对应字典 oa_task_status:
| 状态值 | 枚举 | 说明 | 完成进度 | 可执行操作 |
|---|---|---|---|---|
1 | NEW | 新任务 | 20% | 接收人反馈、发布人调整 |
2 | RECEIVED | 已接收 | 40% | 接收人反馈、发布人调整 |
3 | IN_PROGRESS | 进行中 | 60% | 接收人反馈、发布人调整 |
4 | SUBMITTED | 已提交 | 80% | 仅发布人可确认完成 |
5 | COMPLETED | 已完成 | 100% | — |
状态流转说明
新任务(1) ──→ 已接收(2) ──→ 进行中(3) ──→ 已提交(4) ──发布人确认──→ 已完成(5)
└──────────── 接收人自行反馈 ────────────┘
- 创建任务(
createTask):初始状态取发布人在表单中选择的状态(通常是新任务(1)),所有接收人记录都初始化为同一个值。 - 修改任务(
updateTask):表单中的状态会同步覆盖全部当前接收人的进度,新加入的接收人也按该状态初始化,被移除的接收人删除其接收关系。 - 接收人反馈(
feedbackTask,非发布人调用):只能把本人的接收记录改为新任务(1)~已提交(4)之间的状态。一旦本人状态达到已提交(4)或更高,就不能再反馈,只能等发布人确认,由TASK_STATUS_TRANSITION_INVALID异常拦截。 - 发布人反馈(
feedbackTask,发布人调用):发布人不受上面的区间限制,可以直接把整体状态设为任意值(包括已完成(5)),并同步覆盖所有接收人的状态。这就是「已提交后由发布人确认完成」的实现方式。 - 整体状态的计算(
updateTaskOverallStatus):接收人反馈后,取所有接收人状态的最小值回写主表。也就是说,多人任务必须全部接收人都达到某个状态,任务整体才前进到该状态,木桶效应保证不会虚报进度。 - 取消任务:发布人在编辑表单中勾选取消,写入
canceled = 1。已取消的任务不允许再反馈,feedbackTask开头即校验。取消是独立标记,不会改变status的值。 - 无论发布人还是接收人反馈,都会向
oa_task_log追加一条日志。
# 2.4 管理后台
对应 [OA 办公协同 -> 任务管理 -> 我发布的 / 我的任务] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/oa/task/list 目录。
# 我发布的与我的任务
页面分别在 @/views/oa/task/list 和 @/views/oa/task/my。前者维护发布的任务,后者查看分配给本人的任务。

# 新增与修改
点击【新增】按钮,弹出 OaTaskForm.vue 表单,填写任务标题、类型、接收人、状态、起止时间和任务描述。置顶、取消和任务评价也在此维护。点击【确定】后,通过 /oa/task/create 或 /oa/task/update 保存。

# 反馈与完成
点击任务查看 OaTaskDetail.vue,展示总体状态、各接收人的进度和反馈记录。点击【新增反馈】打开 OaTaskFeedbackForm.vue,填写状态和反馈内容,调用 POST /oa/task/feedback。已取消任务不能继续反馈;接收人和发布人的可选状态不同。

# 删除
发布人在【我发布的】中删除任务;接收人只有在发布人已取消任务后,才能从【我的任务】移除本人的接收关系。
# 3. 工作计划
工作计划,由 OaPlanController 提供接口(/oa/plan)。
# 3.1 表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
工作计划表 oa_plan,保存计划内容、执行总结和管理人员的点评:
CREATE TABLE `oa_plan` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`type` tinyint NOT NULL COMMENT '计划类型',
`status` tinyint NOT NULL COMMENT '计划状态',
`title` varchar(100) NOT NULL COMMENT '标题',
`label` varchar(100) DEFAULT NULL COMMENT '标签',
`content` text NOT NULL COMMENT '计划内容',
`summary` text COMMENT '计划总结',
`comment` text COMMENT '计划点评',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`file_urls` json DEFAULT NULL COMMENT '附件地址列表',
PRIMARY KEY (`id`),
KEY `idx_creator_start_time` (`creator`, `start_time`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB COMMENT='OA 工作计划';
① 工作计划没有子表,是本文四个功能中唯一的单表结构。同样以通用字段 creator 作为计划所属人,idx_creator_start_time 索引支撑【计划管理】按本人 + 周期查询。修改和删除由 OaPlanServiceImpl 的 validatePlanOwner 校验只能本人操作。
② 枚举 type 计划类型(OaPlanTypeEnum),对应字典 oa_plan_type:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | DAY | 日计划 |
2 | WEEK | 周计划 |
3 | MONTH | 月计划 |
③ content、summary、comment 三个文本字段分属不同角色:content 计划内容和 summary 执行总结由计划所属人填写,comment 点评由管理人员在计划报表中填写,普通编辑表单不包含该字段。
④ 点评采用追加方式(addPlanComment):新点评换行拼接到 comment 已有内容之后,而不是覆盖,因此可以保留多次、多人的点评记录。
⑤ file_urls 是 JSON 数组,保存附件地址列表,形如 ["http://xxx/a.pdf", "http://xxx/b.png"]。
⑥ 计划报表复用 oa_plan 表,不建立独立的报表表,仅在查询时换用管理范围的数据权限。详见 §3.3 管理后台 的「计划报表与点评」。
# 3.2 状态流转
计划状态由 OaPlanServiceImpl 控制,状态字段为 status,对应枚举 OaPlanStatusEnum 和字典 oa_plan_status:
| 状态值 | 枚举 | 说明 | 可执行操作 |
|---|---|---|---|
1 | UNFINISHED | 未完成 | 编辑、删除、被点评 |
2 | FINISHED | 已完成 | 编辑、删除、被点评 |
3 | CANCELED | 已取消 | 编辑、删除、被点评 |
状态流转说明
新增计划 ──→ 未完成(1) ──┬──→ 已完成(2)
└──→ 已取消(3)
- 与任务、汇报不同,工作计划没有独立的状态变更接口(没有
submitPlan/finishPlan这类方法),状态完全由计划所属人在OaPlanForm.vue表单的【计划状态】下拉框中直接选择,随createPlan/updatePlan一并保存。 - 因此三个状态之间可以任意切换,后端不做流转合法性校验,已完成的计划也能改回未完成。上图只是表达常见的使用顺序。
- 点评(
addPlanComment):不改变计划状态,只追加comment。调用前校验计划所属人在当前用户的管理范围内(adminUserApi.getUserListBySubordinate),否则抛出PLAN_ACCESS_DENIED异常。管理人员只能点评,不能修改或删除下属的计划。
# 3.3 管理后台
对应 [OA 办公协同 -> 工作计划 -> 计划管理] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/oa/plan/list 目录。
# 列表

# 新增与修改
点击【新增】按钮,弹出 OaPlanForm.vue,填写计划类型、状态、标题、标签、起止时间、内容和总结,可以上传附件。修改时使用同一个表单。

# 计划报表与点评
对应 [OA 办公协同 -> 工作计划 -> 计划报表] 菜单,页面在 @/views/oa/plan/report。查询管理范围内的计划,查看执行情况并填写点评,由 GET /oa/plan/report-page 和 PUT /oa/plan/add-comment 提供接口。

# 删除
在计划管理中点击【删除】并确认,删除本人计划。计划报表用于查看和点评,不作为修改其他用户计划的入口。
# 4. 工作汇报
工作汇报,由 OaWorkReportController 提供接口(/oa/work-report)。
# 4.1 表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
工作汇报表 oa_work_report,保存日报、周报、月报的汇报周期和填写内容:
CREATE TABLE `oa_work_report` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`no` varchar(64) NOT NULL COMMENT '汇报单号',
`type` tinyint NOT NULL COMMENT '汇报类型',
`status` tinyint NOT NULL COMMENT '汇报状态',
`dept_id` bigint DEFAULT NULL COMMENT '汇报人所属部门编号',
`start_time` datetime NOT NULL COMMENT '周期开始时间',
`end_time` datetime NOT NULL COMMENT '周期结束时间',
`title` varchar(255) NOT NULL COMMENT '汇报标题',
`summary` text COMMENT '工作总结',
`plan` text COMMENT '工作计划补充说明',
`problem` text COMMENT '问题与协调事项',
`work_items` json DEFAULT NULL COMMENT '已完成工作项',
`plan_items` json DEFAULT NULL COMMENT '工作计划项',
`file_urls` json DEFAULT NULL COMMENT '附件地址列表',
`remark` varchar(1000) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tenant_no` (`tenant_id`, `no`),
KEY `idx_creator_type_start` (`creator`, `type`, `start_time`),
KEY `idx_dept_type_start_status` (`dept_id`, `type`, `start_time`, `status`)
) ENGINE=InnoDB COMMENT='OA 工作汇报';
① no 汇报单号由 OaNoRedisDAO 以 WR 为前缀生成,uk_tenant_no 唯一索引保证租户内单号唯一,创建时还会再查一次 selectByNo 做二次校验,重复则抛出 WORK_REPORT_NO_DUPLICATE 异常。
② 枚举 type 汇报类型(OaWorkReportTypeEnum),对应字典 oa_work_report_type:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | DAILY | 日报 |
2 | WEEKLY | 周报 |
3 | MONTHLY | 月报 |
③ dept_id 关联 system_dept 表的 id 字段,在创建时冗余汇报人当时所属的部门,后续人员调岗不会改变历史汇报的部门归属。idx_dept_type_start_status 索引即为汇报统计按部门 + 类型 + 周期 + 状态的聚合查询服务。
④ title 汇报标题允许不填:为空时由 buildTitle 按「周期 + 工作 + 类型名」自动生成,例如 2026-09-17 工作日报。
⑤ summary、plan、problem 是三段文本描述,work_items、plan_items 则是结构化的明细行,在表单中通过【新增工作项】【新增计划项】逐行维护。两者的 JSON 结构如下:
work_items(已完成工作项)与 plan_items(工作计划项)
work_items 对应 OaWorkReportDO.WorkItem,每项包含工作内容和完成进度:
[
{
"content": "完成 OA 日程模块的接口联调",
"progress": 100
},
{
"content": "梳理工作汇报统计的口径",
"progress": 60
}
]
| 字段 | 类型 | 说明 |
|---|---|---|
content | String | 工作内容 |
progress | Integer | 完成进度,取值 0 ~ 100 |
plan_items 对应 OaWorkReportDO.PlanItem,只有计划内容,没有进度(进度要等下一期汇报作为工作项填写):
[
{ "content": "推进企业云盘的权限设计评审" },
{ "content": "输出下周的迭代排期" }
]
| 字段 | 类型 | 说明 |
|---|---|---|
content | String | 计划内容 |
⑥ file_urls 是 JSON 数组,保存附件地址列表,与 oa_plan.file_urls 结构一致。
# 4.2 状态流转
汇报状态由 OaWorkReportServiceImpl 控制,状态字段为 status,对应枚举 OaWorkReportStatusEnum 和字典 oa_work_report_status:
| 状态值 | 枚举 | 说明 | 可执行操作 |
|---|---|---|---|
1 | DRAFT | 草稿 | 编辑、删除、提交 |
2 | SUBMITTED | 已提交 | 取消提交 |
状态流转说明
新增汇报 ──→ 草稿(1) ──提交──→ 已提交(2)
↑ │
└────取消提交───────┘
- 新增汇报(
createWorkReport):状态固定初始化为草稿(1),不接受表单传入的状态,同时生成单号并冗余汇报人的部门编号。 - 编辑、删除(
updateWorkReport/deleteWorkReport):先由validateWorkReportOwner校验汇报属于当前用户,再由validateWorkReportStatus校验处于草稿(1)。已提交的汇报不能修改和删除,必须先取消提交。 - 提交(
submitWorkReport):草稿(1)→已提交(2)。这里的【提交】只是改变状态,不发起 BPM 流程,也没有审批人。 - 取消提交(
cancelWorkReport):已提交(2)→草稿(1)。这是一个可逆的状态机,汇报可以在两个状态之间反复切换,方便修正内容后重新提交。 - 状态校验不通过时,统一抛出
WORK_REPORT_STATUS_INVALID异常。
草稿不向管理人员公开
汇报统计和查看下属汇报时(getWorkReport),要同时满足三个条件才放行:调用人具备 oa:work-report:statistics 权限、汇报处于 已提交(2)、且汇报人在调用人的管理范围内。任何一条不满足都抛出 WORK_REPORT_ACCESS_DENIED 异常,因此员工的草稿始终只有本人可见。
# 4.3 管理后台
对应 [OA 办公协同 -> 工作汇报 -> 我的汇报] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/oa/workreport 目录。
# 列表
切换【工作日报】【工作周报】【工作月报】,按单号、状态和汇报周期查询。

# 新增与修改
点击【新增】打开 OaWorkReportForm.vue,选择汇报周期,填写工作总结、工作计划、问题与协调、备注及附件。通过【新增工作项】【新增计划项】逐行填写明细,点击【保存】生成草稿。只有草稿可以修改、删除。

# 提交与取消提交
在列表点击【提交】,调用 PUT /oa/work-report/submit。已提交汇报可点击【取消提交】,调用 PUT /oa/work-report/cancel,恢复为草稿后再修改。
# 汇报统计
对应 [OA 办公协同 -> 工作汇报 -> 汇报统计] 菜单,页面在 @/views/oa/workreport/statistics。选择日报、周报或月报及统计周期,展示员工应填、已填、未填和填写率;点击明细查看已填汇报和未填周期,由 GET /oa/work-report/statistics 提供数据。
统计口径需要注意三点:
① 按自然周期去重。同一周期内员工可能提交了多份汇报,明细中都能查看,但「已填」只计算一次,避免重复提交拉高填写率。
② 日报的应填周期排除周末,只统计工作日;周报、月报按自然周和自然月生成应填周期。
③ 不统计未来日期。若查询的结束时间晚于当天,会被截断为当前时间,因此尚未到期的周期不会被计入「未填」。
