2026-07-30-oa-leave-kit.md 7.8 KB

OA Leave Kit(请假套件)Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 在审批「2 表单设计」增加「套件」菜单与请假套件:拖入后得到套件容器(工时属性)+ 四个基础子字段;发起时按双段工时自动算时长(可手改);提交时后端权威重算/校验。

Architecture: FormCreate 扩展 leaveKit 布局容器(input: false,可拖入子组件,默认带 4 子字段)+ menu: 'kit'。工时在容器 props。纯函数 calcLeaveHours 前后端各一份。发起页对 schema 中 leaveKit 绑定 watch;start_instance 调用 apply_leave_kit_hours。

Tech Stack: Vue 3 · @form-create/designer · Element Plus · FastAPI · pytest · Node node:test(前端算法单测)

Spec: docs/superpowers/specs/2026-07-30-oa-leave-kit-design.md
Demo: docs/mockups/oa-form-kit-tab-demo.html


File map

Path Responsibility
Create: web-admin/src/utils/oa-leave-hours.ts 重叠工时计算纯函数
Create: web-admin/src/utils/oa-leave-hours.test.ts 算法单测
Create: web-admin/src/components/oa-approval/OaLeaveKit.vue 套件容器 UI(设计器/运行时)
Create: web-admin/src/utils/oa-leave-kit-rule.ts DragRule:menu=kit,默认 4 子字段 + 工时 props
Create: web-admin/src/utils/oa-form-create-leave-kit.ts addMenu('kit') + setupOaLeaveKit
Create: web-admin/src/utils/oa-leave-kit-runtime.ts 从 schema 解析 leaveKit、应用自动时长、手改标记
Modify: web-admin/src/main.ts 注册 leaveKit
Modify: web-admin/src/oa-fc-host/main.ts 同上
Modify: web-admin/src/views/oa-approval/start/index.vue 发起页联动时长
Create: backend/app/services/oa_leave_hours.py 同算法 + apply_leave_kit_hours
Create: backend/tests/test_oa_leave_hours.py pytest
Modify: backend/app/services/oa_publish.py LAYOUT_TYPES 加 leaveKit
Modify: backend/app/services/oa_engine.py start_instance 调用 apply
Modify: docs/superpowers/specs/2026-07-30-oa-leave-kit-design.md 状态 → 实现中/已实现

Task 1: Leave hours algorithm (TDD, frontend)

Files:

  • Create: web-admin/src/utils/oa-leave-hours.ts
  • Create: web-admin/src/utils/oa-leave-hours.test.ts

  • [ ] Step 1: Write failing tests

// web-admin/src/utils/oa-leave-hours.test.ts
import { describe, it } from 'node:test'
import assert from 'node:assert/strict'
import { calcLeaveHours, DEFAULT_LEAVE_WORK_HOURS } from './oa-leave-hours'

describe('calcLeaveHours', () => {
  const wh = DEFAULT_LEAVE_WORK_HOURS // 08:30-12:00, 13:00-17:30

  it('same day morning+afternoon sample 09:00-15:00 => 5.0', () => {
    assert.equal(calcLeaveHours('2026-07-30 09:00', '2026-07-30 15:00', wh), 5.0)
  })

  it('only morning', () => {
    assert.equal(calcLeaveHours('2026-07-30 08:30', '2026-07-30 12:00', wh), 3.5)
  })

  it('gap across lunch not counted', () => {
    assert.equal(calcLeaveHours('2026-07-30 11:00', '2026-07-30 14:00', wh), 2.0)
  })

  it('end before start => null', () => {
    assert.equal(calcLeaveHours('2026-07-30 15:00', '2026-07-30 09:00', wh), null)
  })

  it('invalid => null', () => {
    assert.equal(calcLeaveHours('', '2026-07-30 09:00', wh), null)
  })
})
  • Step 2: Run tests — expect FAIL
cd web-admin && pnpm exec tsx --test src/utils/oa-leave-hours.test.ts
  • Step 3: Implement oa-leave-hours.ts

Export:

  • DEFAULT_LEAVE_WORK_HOURS = { morningStart, morningEnd, afternoonStart, afternoonEnd }
  • calcLeaveHours(start, end, workHours): number | null — 1 decimal, null if invalid/end≤start
  • Overlap minutes with morning+afternoon segments per calendar day

  • [ ] Step 4: Run tests — expect PASS

  • [ ] Step 5: Commit(仅当用户要求 commit 时执行;本会话默认可暂缓)


Task 2: Backend leave hours + publish whitelist

Files:

  • Create: backend/app/services/oa_leave_hours.py
  • Create: backend/tests/test_oa_leave_hours.py
  • Modify: backend/app/services/oa_publish.py — add leaveKit to LAYOUT_TYPES
  • Modify: backend/app/services/oa_engine.py — after formulas, call apply_leave_kit_hours

  • [ ] Step 1: pytest failing for calc_leave_hours 09:00–15:00 → 5.0

  • [ ] Step 2: Implement calc_leave_hours + apply_leave_kit_hours(schema, data):

    • Walk rules for type == leaveKit
    • Find children fields: prefer props startField/endField/durationField if set, else by title/order (select, datePicker, datePicker, inputNumber)
    • If duration props durationManual truthy: validate ≥0 one decimal only
    • Else overwrite duration with calc result
  • [ ] Step 3: Wire start_instance

  • [ ] Step 4: pytest PASS


Task 3: FormCreate leaveKit container + kit menu

Files:

  • Create: OaLeaveKit.vue — 虚线框 + 标题「请假套件」+ <slot />
  • Create: oa-leave-kit-rule.ts — DragRule
  • Create: oa-form-create-leave-kit.ts — addMenu + component + addDragRule
  • Modify: main.ts, oa-fc-host/main.ts

DragRule essentials:

{
  menu: 'kit',
  icon: 'icon-date',
  label: '请假',
  name: 'leaveKit',
  input: false,
  drag: true,
  mask: false,
  inside: true,
  rule() {
    return {
      type: 'leaveKit',
      props: { ...DEFAULT_LEAVE_WORK_HOURS },
      children: [ /* select, 2x datePicker datetime, inputNumber duration */ ]
    }
  },
  props() {
    return [ /* 4x time inputs for morning/afternoon */ ]
  }
}

setupOaLeaveKit:

FcDesigner.addMenu({ name: 'kit', title: '套件', list: [] })
FcDesigner.component('leaveKit', OaLeaveKit)
formCreate.component('leaveKit', OaLeaveKit)
FcDesigner.addDragRule(leaveKitDragRule)

子字段 field 用 uniqueId;时长 props.precision=1, props.durationManual=false;日期 props.type='datetime', format/valueFormat=YYYY-MM-DD HH:mm。


Task 4: Start form runtime linkage

Files:

  • Create: oa-leave-kit-runtime.ts — listLeaveKits(schema), applyLeaveKitDurations(schema, formData, { respectManual })
  • Modify: start/index.vue — on form-create change / watch formData,未手改时写回时长;用户改时长字段时设 durationManual(若无法写回 schema,用并行隐藏字段或 formData 旁路 __leaveDurationManual_<field>;优先改 rule props 若 api 允许)

手改标记落地(择一,实现时选可工作的):

  1. 时长字段 props.durationManual(设计器可读)+ 提交时由前端把 manual 状态塞进 formData 特殊 key,后端读取;或
  2. formData 增加 __manual_<durationField>: true,后端识别后不覆盖。

采用 2(更简单跑通):发起人手改时长 → 设 formData['__manual_' + durationField] = true;后端 apply_leave_kit_hours 检查该 key。

「恢复自动计算」:首期发起页可不做按钮,手改后仅断开;设计器 Demo 已有示意。Spec 中的恢复按钮可作为二期(或 start 页时长旁小链,有则加)。


Task 5: Spec status + graphify + smoke checklist

  • Update design doc status
  • ./scripts/update-graph.sh frontend(若改了 web-admin)
  • Manual: designer 套件 Tab → 拖请假 → 配工时 → 发起算时长

Spec coverage

Spec 项 Task
套件 Tab 双列卡片 T3
4 基础字段 + 套件工时 T3
双段重叠算法 1 位小数 T1/T2
手改后不联动 T4 + T2
发布白名单 leaveKit T2
提交权威 T2
多套 field 唯一 T3 uniqueId
恢复自动计算 T4 可选/二期

Execution

用户已要求继续开发:本会话采用 Inline Execution(executing-plans)。