Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/apps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@ export const KpiApp = App.create({
id: 'group_entry', type: 'group', label: '填报与审核', icon: 'file-spreadsheet',
children: [
{ id: 'nav_sheets', type: 'object', objectName: 'kpi_entry_sheet', label: '填报单', icon: 'file-spreadsheet' },
// ── 三个待办直达项(#36)──────────────────────────────────────────
// 队列本来就有(`EntrySheetViews.listViews.hr` / `.leader`、
// `CheckTaskViews.listViews.pending`),问题在够不着:填报单有 8 个具名列表视图,
// 页签栏放不下就把后几个折进「还有 N 个」溢出菜单,「待领导审批」正好落在里面 ——
// 分管领导是唯一按手册找不到自己队列的角色(平台
// objectstack-ai/objectstack#14883、#14952)。这里用 `viewName` 从侧边栏直指
// 那三个视图,把「点组 → 点填报单 → 展开溢出菜单 → 点页签」压成一次点击。
//
// 用 `viewName` 而不是 `filters`:两者互斥(spec `app.zod.ts` 的
// `assertNavTargetExclusivity`,`filters` 落到 `/:objectName/data` 裸数据面),
// 而具名视图已经带好了列与过滤,重复一遍只会多一处会跟视图走散的真值。
//
// 这三项**不加** `requiredPermissions`:它们指向的对象在所有岗位的权限集里都有读权,
// 看得到几行由数据范围决定(部门填报人员点进「待人力审核」是一张空表,不是报错)。
// 加能力闸门等于把「配置类菜单按岗位裁剪」的口径套到待办上,反而挡掉本该用它的人。
{ id: 'nav_my_checks', type: 'object', objectName: 'kpi_check_task', viewName: 'pending', label: '待我核对', icon: 'clipboard-check' },
{ id: 'nav_hr_queue', type: 'object', objectName: 'kpi_entry_sheet', viewName: 'hr', label: '待人力审核', icon: 'user-check' },
{ id: 'nav_leader_queue', type: 'object', objectName: 'kpi_entry_sheet', viewName: 'leader', label: '待领导审批', icon: 'shield-check' },
// ── 全量入口 ────────────────────────────────────────────────────
{ id: 'nav_lines', type: 'object', objectName: 'kpi_entry_line', label: '填报明细', icon: 'table' },
{ id: 'nav_checks', type: 'object', objectName: 'kpi_check_task', label: '分公司核对', icon: 'clipboard-check' },
{ id: 'nav_bonus', type: 'object', objectName: 'kpi_bonus', label: '加减分', icon: 'plus-minus' },
Expand Down
101 changes: 88 additions & 13 deletions src/pages/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import { definePage } from '@objectstack/spec/ui';

/**
* 工作台 —— 填报人员的待办入口(#34)。
* 工作台 —— 六个岗位共用的待办入口(#34 建、#36 扩)。
*
* 设计目标是「一屏填数出分提交」:登录后直接落在本页,不再先跳「填报明细」再跳「填报单」。
* 两个 `object-grid` 区块合起来构成一条不跳页的动线 ——
* 设计目标是「登录即处理」:落地页上直接排开当前该我动手的几摞事,不再「回列表 → 重选页签 →
* 打开 → 审」。五个 `object-grid` 区块合起来构成一条不跳页的动线 ——
*
* 1. 「待填报的指标」= `kpi_entry_line` 的可编辑网格(`editable: true`),首列是「所属填报单」,
* 后面九列是填一行数当场要看的项;实际值与备注可改,其余列在字段定义上就是 `readonly`,
* 网格照此渲染。保存后 `entry-line.hook` 逐行算分,完成率 / 得分率 / 最终得分当场回填到
* 同一张表。
* 2. 「填报单」= `kpi_entry_sheet` 的只读网格,行操作挂 `kpi_sheet_submit`(「提交填报」),
* 填完即可在同一页提交。
* 3. 「待我核对」= `kpi_check_task` 中状态为「待核对」的任务,行操作挂「确认无误」「提出争议」。
* 4. 「待人力审核」= `kpi_entry_sheet` 中状态为「人力审核中」的单,行操作挂「审核通过」「驳回」。
* 5. 「待领导审批」= 同上,状态为「领导审批中」。
*
* **能看到哪些行**由数据层决定(对象 OWD `private` / `controlled_by_parent` + 方案发布时
* 写入的动态共享规则 + 权限集 `readScope`)—— 视图筛选是展示范围,不是安全边界,这里不拿
* `filter` 当权限用。
* **区块不按岗位显隐**(#36 的实现口径)。两个原因,一个是能力、一个是设计:
*
* - 能力:页面组件的 `visibleWhen` 只绑 `record` / `current_user` / `page.<var>`(spec
* `ui/page.zod.ts`),没有岗位绑定。实测写 `'kpi_dept_reporter' in current_user.positions`
* 后,**连部门填报人员本人都看不到网格**(谓词求值为空 = 对所有人隐藏),已回退。
* - 设计:**能看到哪些行由数据层决定** —— 对象 OWD `private` / `controlled_by_parent` +
* 方案发布时写入的动态共享规则 + 权限集 `readScope`。部门填报人员在「待人力审核」里
* 本来就一行都读不到,分公司核对人员在「待我核对」里只读得到本分公司的任务;按岗位再
* 显隐一次是把同一条边界画两遍,而画在前端的那一遍不是边界。视图筛选是展示范围,不是
* 安全边界,这里不拿 `filter` 当权限用。
*
* 空区块**保留**、不隐藏:它给「这摞事现在是空的」一个确定的回答,比区块时有时无好读;
* 三个待办区块 `pageSize` 收到 10(填报动线的两块仍是 25),空态只占一行标题加一句空提示。
*
* **屏幕上怎么分辨哪一行是哪一张单**是另一回事(评审 F3),三处按平台实际能到的程度处理:
*
Expand All @@ -26,19 +39,42 @@ import { definePage } from '@objectstack/spec/ui';
* 「filters on 'sheet.status', which follows the relationship 'sheet' into another object
* — a filter reaches only columns of 'kpi_entry_line' itself … Denormalise the value onto
* 'kpi_entry_line' (a stored field, written when the source changes) and filter that.」
* 本对象上没有任何字段编码填报单状态,而反规范化要新增字段 + 写入路径,超出本工作项范围。
* `kpi_entry_line` 上没有任何字段编码填报单状态(#36 复核过一遍字段表:sheet /
* plan_indicator / indicator / indicator_name / unit / direction / scoring_method /
* target_value / weight / actual_value / completion_rate / score_rate / score /
* adjusted_score / final_score / is_adjusted / adjust_type_applied / calc_trace /
* last_adjustment / remark —— 没有一个是填报单状态的副本),而反规范化要新增字段 +
* 写入路径,#36 明写「明细上若无该字段则如实记录,不加字段」。
* 退化的替代条件(如「实际值为空」)会让保存后的行当场从网格里消失,连带毁掉「同页出分」,
* 比不过滤更糟,因此不做。已提交 / 已归档的行仍会出现在网格里,改动在保存时被 `sheet.hook`
* 的冻结闸门拦下并给出三段式提示 —— 拦得住,只是晚一步。
* - 「对 org 范围岗位隐藏本区块」**做不到**:页面组件的 `visibleWhen` 只绑 `record` /
* `current_user` / `page.<var>`(spec `ui/page.zod.ts`),没有岗位绑定。实测写
* `'kpi_dept_reporter' in current_user.positions` 后,**连部门填报人员本人都看不到网格**
* (谓词求值为空 = 对所有人隐藏),已回退。
* - 三个待办区块**能**过滤,因为过滤的是本对象自己的 `status` 列(`kpi_check_task.status`、
* `kpi_entry_sheet.status`),不跨关系。
*
* 行动作用 `object-grid` 的 `rowActions`(spec `component.zod.ts` 记有读点,
* `ObjectGrid.tsx:1927`),挂的是 `actions/index.ts` 里既有的对象级动作 —— 它们本就带
* `locations: ['record_header', 'list_item']` 与状态谓词,本页只是把它们摆到行上,
* 不新增动作、不改动作定义。驳回原因 / 争议内容的必填口径写在动作参数上,行级入口与
* 记录页入口共用同一份定义,不会因为入口不同而松动。
*
* ⚠️ 页面网格行上的「打开 ›」按钮点了没反应(平台 objectstack-ai/objectstack#16276),
* 本页的动线不依赖它:待办都在行操作菜单里就地办完。
*
* 区块标题按需求 §1.4 术语表用「填报单」「指标」的表内名词,不用「我的」——`kpi_home` 是
* `isDefault: true` 的默认落地页,六个岗位共用;对管理员 / 人力审核这类 org 范围岗位,
* 网格里本来就不止「我的」行,标题不该替它们说话。
* 网格里本来就不止「我的」行,标题不该替它们说话。「待我核对」是例外,也只是例外地跟着
* 设计方案 §5.5 的原文走(该节把三个队列点名为「待我核对」「待人力审核」「待领导审批」)。
*/

/** 只读待办网格的共同形状:不可编辑、按 10 行压缩、行操作就地办完。 */
const TODO_GRID = { editable: false, pageSize: 10 } as const;

/** 三个待办区块共用的填报单列:够认出是哪张单、够看出分,不铺满一屏。 */
const SHEET_TODO_COLUMNS = ['name', 'subject', 'status', 'line_count', 'weight_total', 'indicator_score', 'bonus_total', 'total_score'];

/** 待办区块的行操作:审核通过 / 驳回,两者都在 `actions/index.ts` 里定义。 */
const SHEET_REVIEW_ACTIONS = ['kpi_sheet_approve', 'kpi_sheet_reject'];

export const HomePage = definePage({
name: 'kpi_home',
label: '工作台',
Expand All @@ -51,7 +87,7 @@ export const HomePage = definePage({
{
name: 'main', width: 'full',
components: [
{ type: 'element:text', properties: { content: '部门填报人员:在下方「待填报的指标」直接填实际值,保存即出分,再到「填报单」点「提交填报」;分公司核对人员:在「分公司核对」确认或提出争议;人力审核 / 分管领导:在「填报单」对应队列里审核通过或驳回(驳回必须填写原因);审批通过后在「结果与归档」查看四个维度的汇总并归档。' } },
{ type: 'element:text', properties: { content: '部门填报人员:在下方「待填报的指标」直接填实际值,保存即出分,再到「填报单」点「提交填报」;分公司核对人员:在「待我核对」确认无误或提出争议(争议内容必填);人力审核 / 分管领导:在「待人力审核」「待领导审批」就地审核通过或驳回(驳回原因必填);审批通过后在「结果与归档」查看四个维度的汇总并归档。当前没有待办的区块会显示为空,不影响其他区块。' } },
{ type: 'element:text', properties: { content: '待填报的指标', variant: 'subheading' } },
{
type: 'object-grid',
Expand All @@ -76,6 +112,45 @@ export const HomePage = definePage({
pageSize: 25,
},
},
{ type: 'element:divider', properties: {} },
{ type: 'element:text', properties: { content: '待我核对', variant: 'subheading' } },
{
type: 'object-grid',
properties: {
...TODO_GRID,
objectName: 'kpi_check_task',
columns: ['name', 'sheet', 'branch', 'status', 'comment'],
filter: [{ field: 'status', operator: 'equals', value: 'pending' }],
rowActions: ['kpi_check_confirm', 'kpi_check_dispute'],
sort: [{ field: 'created_at', order: 'asc' }],
},
},
{ type: 'element:divider', properties: {} },
{ type: 'element:text', properties: { content: '待人力审核', variant: 'subheading' } },
{
type: 'object-grid',
properties: {
...TODO_GRID,
objectName: 'kpi_entry_sheet',
columns: SHEET_TODO_COLUMNS,
filter: [{ field: 'status', operator: 'equals', value: 'hr_reviewing' }],
rowActions: SHEET_REVIEW_ACTIONS,
sort: [{ field: 'submitted_at', order: 'asc' }],
},
},
{ type: 'element:divider', properties: {} },
{ type: 'element:text', properties: { content: '待领导审批', variant: 'subheading' } },
{
type: 'object-grid',
properties: {
...TODO_GRID,
objectName: 'kpi_entry_sheet',
columns: SHEET_TODO_COLUMNS,
filter: [{ field: 'status', operator: 'equals', value: 'leader_approving' }],
rowActions: SHEET_REVIEW_ACTIONS,
sort: [{ field: 'submitted_at', order: 'asc' }],
},
},
],
},
],
Expand Down
9 changes: 9 additions & 0 deletions src/translations/zh-CN.objects.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,15 @@ export const zhCNTranslations: TranslationData = {
nav_sheets: {
label: "填报单"
},
nav_my_checks: {
label: "待我核对"
},
nav_hr_queue: {
label: "待人力审核"
},
nav_leader_queue: {
label: "待领导审批"
},
nav_lines: {
label: "填报明细"
},
Expand Down
Loading