From c33bb7308eab784cdcff2b2056deca10bee65dfc Mon Sep 17 00:00:00 2001
From: yueyang <2495544247@qq.com>
Date: Tue, 27 Oct 2026 18:10:54 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E8=81=94=E5=8A=A8=E7=B3=BB=E7=BB=9F?=
=?UTF-8?q?=E5=8C=96=E5=BE=85=E5=8A=9E=E8=B4=9F=E8=B4=A3=E4=BA=BA=E5=92=8C?=
=?UTF-8?q?=E8=B4=A3=E4=BB=BB=E4=BA=BA=20(#697)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## 实际改动
- 提醒筛选项“无提醒”改为“已完成”,保持原有已完成筛选逻辑。
- 总负责人和责任人建立双向关联:选择总负责人后只显示对应责任人,选择责任人时自动勾选对应总负责人。
- 编辑弹窗将原生联想输入改为统一的勾选面板,并使用同一套负责人关联逻辑。
- 责任人关联同时支持固定映射和现有数据推导,惠兰等未在固定清单中的人员也能从历史数据正确归属。
- DESIGN.md 与机器设计规范新增跨设备控件准则,后续页面统一复用内置字体和自绘控件。
## 验证证据
- SystemizationTodosTest 通过:23 tests,343 assertions。
- 覆盖已完成筛选标签、固定人员映射、历史数据推导和自定义责任人控件。
- 不涉及数据库迁移、金额、权限或上游数据。
## 合并前复查
- 基于最新 origin/main(da546877)开发。
- 已自审完整 diff,未包含敏感信息或无关修改。
- 提交:fa7d2608。
Reviewed-on: https://git.1080p.hk/WuYongJie/CaiWuXiTong/pulls/697
---
.impeccable/design.json | 19 ++-
DESIGN.md | 15 +-
.../Business/SystemizationTodos.php | 46 ++++-
.../pages/systemization-todos.blade.php | 158 ++++++++++++++++--
.../Business/SystemizationTodosTest.php | 52 ++++++
5 files changed, 263 insertions(+), 27 deletions(-)
diff --git a/.impeccable/design.json b/.impeccable/design.json
index 73962735..74b73848 100644
--- a/.impeccable/design.json
+++ b/.impeccable/design.json
@@ -173,7 +173,7 @@
"refersTo": "button-primary",
"description": "浅色内容区的主要操作,使用可读深金而不是亮金。",
"html": "",
- "css": ".ds-btn-primary { height: 34px; padding: 0 14px; border: 1px solid #856632; border-radius: 8px; background: #856632; color: #fff; font: 500 13px/1.5 'JetBrains Mono', 'Microsoft YaHei', monospace; cursor: pointer; transition: background 150ms cubic-bezier(.22,1,.36,1), border-color 150ms cubic-bezier(.22,1,.36,1); } .ds-btn-primary:hover { background: #6A5129; border-color: #6A5129; } .ds-btn-primary:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(133,102,50,.20); } .ds-btn-primary:active { transform: translateY(1px); } .ds-btn-primary:disabled { opacity: .55; cursor: not-allowed; transform: none; }"
+ "css": ".ds-btn-primary { height: 34px; padding: 0 14px; border: 1px solid #856632; border-radius: 8px; background: #856632; color: #fff; font: 500 13px/1.5 'JetBrains Mono', 'CaiWu CJK', monospace; cursor: pointer; transition: background 150ms cubic-bezier(.22,1,.36,1), border-color 150ms cubic-bezier(.22,1,.36,1); } .ds-btn-primary:hover { background: #6A5129; border-color: #6A5129; } .ds-btn-primary:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(133,102,50,.20); } .ds-btn-primary:active { transform: translateY(1px); } .ds-btn-primary:disabled { opacity: .55; cursor: not-allowed; transform: none; }"
},
{
"name": "Secondary Button",
@@ -181,7 +181,7 @@
"refersTo": "button-secondary",
"description": "次要操作保持白底、暖灰边界和正文墨色。",
"html": "",
- "css": ".ds-btn-secondary { height: 34px; padding: 0 14px; border: 1px solid #D8D2C7; border-radius: 8px; background: #fff; color: #171717; font: 500 13px/1.5 'JetBrains Mono', 'Microsoft YaHei', monospace; cursor: pointer; transition: background 150ms cubic-bezier(.22,1,.36,1), border-color 150ms cubic-bezier(.22,1,.36,1); } .ds-btn-secondary:hover { border-color: #A17D3E; background: #FBF8F1; } .ds-btn-secondary:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(133,102,50,.20); } .ds-btn-secondary:active { background: #F7F0DE; } .ds-btn-secondary:disabled { opacity: .55; cursor: not-allowed; }"
+ "css": ".ds-btn-secondary { height: 34px; padding: 0 14px; border: 1px solid #D8D2C7; border-radius: 8px; background: #fff; color: #171717; font: 500 13px/1.5 'JetBrains Mono', 'CaiWu CJK', monospace; cursor: pointer; transition: background 150ms cubic-bezier(.22,1,.36,1), border-color 150ms cubic-bezier(.22,1,.36,1); } .ds-btn-secondary:hover { border-color: #A17D3E; background: #FBF8F1; } .ds-btn-secondary:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(133,102,50,.20); } .ds-btn-secondary:active { background: #F7F0DE; } .ds-btn-secondary:disabled { opacity: .55; cursor: not-allowed; }"
},
{
"name": "Text Field",
@@ -189,7 +189,7 @@
"refersTo": "input-default",
"description": "紧凑、清晰、具有可见标签和深金焦点状态的业务输入框。",
"html": "",
- "css": ".ds-field { display: inline-flex; min-width: 220px; flex-direction: column; gap: 5px; color: #171717; font-family: 'JetBrains Mono', 'Microsoft YaHei', monospace; } .ds-field-label { color: #595959; font-size: 12px; font-weight: 500; line-height: 16px; } .ds-field-input { height: 34px; padding: 0 10px; border: 1px solid #D8D2C7; border-radius: 8px; background: #fff; color: #171717; font: 400 13px/1.5 inherit; outline: none; transition: border-color 150ms cubic-bezier(.22,1,.36,1), box-shadow 150ms cubic-bezier(.22,1,.36,1); } .ds-field-input::placeholder { color: #595959; } .ds-field-input:hover { border-color: #AFA99E; } .ds-field-input:focus { border-color: #856632; box-shadow: 0 0 0 3px rgba(133,102,50,.20); } .ds-field-input:disabled { background: #EEE9DF; color: #595959; cursor: not-allowed; }"
+ "css": ".ds-field { display: inline-flex; min-width: 220px; flex-direction: column; gap: 5px; color: #171717; font-family: 'JetBrains Mono', 'CaiWu CJK', monospace; } .ds-field-label { color: #595959; font-size: 12px; font-weight: 500; line-height: 16px; } .ds-field-input { height: 34px; padding: 0 10px; border: 1px solid #D8D2C7; border-radius: 8px; background: #fff; color: #171717; font: 400 13px/1.5 inherit; outline: none; transition: border-color 150ms cubic-bezier(.22,1,.36,1), box-shadow 150ms cubic-bezier(.22,1,.36,1); } .ds-field-input::placeholder { color: #595959; } .ds-field-input:hover { border-color: #AFA99E; } .ds-field-input:focus { border-color: #856632; box-shadow: 0 0 0 3px rgba(133,102,50,.20); } .ds-field-input:disabled { background: #EEE9DF; color: #595959; cursor: not-allowed; }"
},
{
"name": "Sidebar Active Item",
@@ -197,7 +197,7 @@
"refersTo": "sidebar-item-active",
"description": "浅黑功能栏上的单一圆形 chevron 活动状态。",
"html": "
",
- "css": ".ds-sidebar { width: 224px; padding: 8px; background: #1D1D1F; } .ds-sidebar-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 4px; color: #F0E4BF; font: 500 12px/16px 'JetBrains Mono', 'Microsoft YaHei', monospace; text-decoration: none; transition: background 150ms cubic-bezier(.22,1,.36,1); } .ds-sidebar-item:hover { background: rgba(240,228,191,.10); } .ds-sidebar-item.is-active { background: #2A2927; color: #F2E7C5; } .ds-sidebar-icon { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; } .ds-active-indicator { display: inline-flex; width: 16px; height: 16px; margin-left: auto; align-items: center; justify-content: center; border: 1px solid rgba(246,233,194,.72); border-radius: 999px; background: rgba(246,233,194,.14); color: #F6E9C2; font-size: 14px; } .ds-sidebar-item:focus-visible { outline: 2px solid #F2E7C5; outline-offset: 2px; }"
+ "css": ".ds-sidebar { width: 224px; padding: 8px; background: #1D1D1F; } .ds-sidebar-item { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 4px; color: #F0E4BF; font: 500 12px/16px 'JetBrains Mono', 'CaiWu CJK', monospace; text-decoration: none; transition: background 150ms cubic-bezier(.22,1,.36,1); } .ds-sidebar-item:hover { background: rgba(240,228,191,.10); } .ds-sidebar-item.is-active { background: #2A2927; color: #F2E7C5; } .ds-sidebar-icon { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; } .ds-active-indicator { display: inline-flex; width: 16px; height: 16px; margin-left: auto; align-items: center; justify-content: center; border: 1px solid rgba(246,233,194,.72); border-radius: 999px; background: rgba(246,233,194,.14); color: #F6E9C2; font-size: 14px; } .ds-sidebar-item:focus-visible { outline: 2px solid #F2E7C5; outline-offset: 2px; }"
},
{
"name": "Workspace Tabs",
@@ -205,7 +205,7 @@
"refersTo": "tab-active",
"description": "白色工作区内使用深金活动文字和单一金色下划线的局部标签切换。",
"html": "",
- "css": ".ds-tabs { display: flex; align-items: center; gap: 36px; min-height: 34px; padding: 0 32px; overflow-x: auto; border: 1px solid #D8D2C7; border-radius: 16px; background: #fff; } .ds-tab { position: relative; display: inline-flex; min-height: 34px; align-items: center; color: #595959; font: 600 14px/1.25 'JetBrains Mono', 'Microsoft YaHei', monospace; text-decoration: none; white-space: nowrap; transition: color 150ms cubic-bezier(.22,1,.36,1); } .ds-tab:hover, .ds-tab.is-active { color: #856632; } .ds-tab.is-active::after { position: absolute; right: 0; bottom: 0; left: 0; height: 3px; border-radius: 999px 999px 0 0; background: #B9954F; content: ''; } .ds-tab:focus-visible { outline: 2px solid #856632; outline-offset: 3px; }"
+ "css": ".ds-tabs { display: flex; align-items: center; gap: 36px; min-height: 34px; padding: 0 32px; overflow-x: auto; border: 1px solid #D8D2C7; border-radius: 16px; background: #fff; } .ds-tab { position: relative; display: inline-flex; min-height: 34px; align-items: center; color: #595959; font: 600 14px/1.25 'JetBrains Mono', 'CaiWu CJK', monospace; text-decoration: none; white-space: nowrap; transition: color 150ms cubic-bezier(.22,1,.36,1); } .ds-tab:hover, .ds-tab.is-active { color: #856632; } .ds-tab.is-active::after { position: absolute; right: 0; bottom: 0; left: 0; height: 3px; border-radius: 999px 999px 0 0; background: #B9954F; content: ''; } .ds-tab:focus-visible { outline: 2px solid #856632; outline-offset: 3px; }"
},
{
"name": "Business Panel",
@@ -213,21 +213,21 @@
"refersTo": "panel",
"description": "使用白色表面、暖灰边界和有限圆角的单层业务容器。",
"html": "",
- "css": ".ds-panel { padding: 16px; border: 1px solid #D8D2C7; border-radius: 12px; background: #fff; color: #171717; font-family: 'JetBrains Mono', 'Microsoft YaHei', monospace; } .ds-panel-title { margin: 0; color: #171717; font-size: 14px; font-weight: 600; line-height: 1.25; } .ds-panel-copy { max-width: 70ch; margin: 8px 0 0; color: #595959; font-size: 13px; line-height: 1.5; } .ds-panel:focus-within { border-color: #856632; }"
+ "css": ".ds-panel { padding: 16px; border: 1px solid #D8D2C7; border-radius: 12px; background: #fff; color: #171717; font-family: 'JetBrains Mono', 'CaiWu CJK', monospace; } .ds-panel-title { margin: 0; color: #171717; font-size: 14px; font-weight: 600; line-height: 1.25; } .ds-panel-copy { max-width: 70ch; margin: 8px 0 0; color: #595959; font-size: 13px; line-height: 1.5; } .ds-panel:focus-within { border-color: #856632; }"
},
{
"name": "Status Tags",
"kind": "chip",
"description": "语义颜色只表达真实业务状态,并始终带文字。",
"html": "已核对待处理异常
",
- "css": ".ds-tags { display: flex; gap: 8px; font-family: 'JetBrains Mono', 'Microsoft YaHei', monospace; } .ds-tag { display: inline-flex; min-height: 24px; align-items: center; padding: 2px 8px; border-radius: 999px; font-size: 12px; font-weight: 500; } .ds-tag-success { background: #F1F6EE; color: #53793E; } .ds-tag-warning { background: #FEF5E7; color: #935F07; } .ds-tag-danger { background: #FAE6E6; color: #B80000; }"
+ "css": ".ds-tags { display: flex; gap: 8px; font-family: 'JetBrains Mono', 'CaiWu CJK', monospace; } .ds-tag { display: inline-flex; min-height: 24px; align-items: center; padding: 2px 8px; border-radius: 999px; font-size: 12px; font-weight: 500; } .ds-tag-success { background: #F1F6EE; color: #53793E; } .ds-tag-warning { background: #FEF5E7; color: #935F07; } .ds-tag-danger { background: #FAE6E6; color: #B80000; }"
},
{
"name": "Finance Data Table",
"kind": "custom",
"description": "紧凑、可扫描、数字右对齐的财务数据表。支持列显示/隐藏、拖动排序、恢复默认及按用户和具体表格保存;冻结多层表头,横向滚动条始终可达。导出明确筛选范围和可见列顺序,保留精度并排除密码;允许导入的业务提供模板、预览、校验及失败反馈,只读镜像不写回。",
"html": "| 订单号 | 收入 | 余额 |
|---|
| 20260722001 | ¥1,280.00 | ¥8,460.00 |
",
- "css": ".ds-table { width: 100%; border-collapse: collapse; overflow: hidden; border: 1px solid #D8D2C7; border-radius: 12px; background: #fff; color: #171717; font: 400 12.5px/1.5 'JetBrains Mono', 'Microsoft YaHei', monospace; } .ds-table th { padding: 9px 10px; border-bottom: 1px solid #D8D2C7; background: #FBF8F1; color: #595959; font-size: 12px; font-weight: 600; text-align: left; } .ds-table td { padding: 9px 10px; border-bottom: 1px solid #EEE9DF; } .ds-table tbody tr:hover { background: #F7F0DE; } .ds-table .ds-num { text-align: right; font-variant-numeric: tabular-nums; } .ds-table .ds-income { color: #B80000; font-weight: 600; }"
+ "css": ".ds-table { width: 100%; border-collapse: collapse; overflow: hidden; border: 1px solid #D8D2C7; border-radius: 12px; background: #fff; color: #171717; font: 400 12.5px/1.5 'JetBrains Mono', 'CaiWu CJK', monospace; } .ds-table th { padding: 9px 10px; border-bottom: 1px solid #D8D2C7; background: #FBF8F1; color: #595959; font-size: 12px; font-weight: 600; text-align: left; } .ds-table td { padding: 9px 10px; border-bottom: 1px solid #EEE9DF; } .ds-table tbody tr:hover { background: #F7F0DE; } .ds-table .ds-num { text-align: right; font-variant-numeric: tabular-nums; } .ds-table .ds-income { color: #B80000; font-weight: 600; }"
}
],
"narrative": {
@@ -236,7 +236,7 @@
"keyCharacteristics": [
"深黑工具轨道、浅黑功能栏、浅色内容区,三层边界清楚。",
"浅金用于深色导航,深金用于浅色内容区的文字与主要操作。",
- "全站使用 JetBrains Mono,标题和数据采用固定字号,不使用流体放大。",
+ "全站使用 JetBrains Mono 与仓库内置 CaiWu CJK 字体,标题和数据采用固定字号,不使用流体放大。",
"页面标题位于内容区右侧,保持 14px;导航使用统一的紧凑字号。",
"一、二级导航统一 12px;同一层级的图标、字号和交互状态必须一致。",
"表格、筛选器和状态反馈优先,装饰性卡片、阴影和动画受到严格限制。",
@@ -250,6 +250,7 @@
{ "name": "The Fixed-Scale Rule", "body": "产品界面使用固定 rem 字号,不使用 clamp() 放大标题;桌面与移动端通过布局重排适配,不通过夸张缩放字体适配。", "section": "typography" },
{ "name": "The Stable-First-Paint Rule", "body": "关键字号、行高和容器尺寸必须由首屏 CSS 直接提供,不能等待 JavaScript 或异步样式后再缩小,禁止首次进入时出现巨大字体闪烁。", "section": "typography" },
{ "name": "The One-Family Rule", "body": "按钮、标签、导航、表格和正文都使用同一字体家族;禁止在数据界面引入装饰性展示字体。", "section": "typography" },
+ { "name": "The Cross-Platform Control Rule", "body": "业务控件不得依赖 macOS、Windows 或不同浏览器的原生外观。字体使用仓库内置 Web 字体;下拉框、复选框、日期选择、联想输入使用项目自绘控件;桌面筛选区使用明确 CSS 网格。新页面必须在 Safari 与 Chromium 下验证布局、换行和控件尺寸一致。", "section": "typography" },
{ "name": "The Flat-by-Default Rule", "body": "静态业务面板默认无阴影;若已经有 1px 边框,不得再叠加 16px 以上模糊半径的宽阴影。", "section": "elevation" },
{ "name": "The No-Nested-Cards Rule", "body": "卡片只用于真实分组;卡片内部不能再套同样外观的卡片。优先使用标题、间距、分隔线和表格分组表达层级。", "section": "elevation" }
],
diff --git a/DESIGN.md b/DESIGN.md
index 9daed852..5b3ffa6e 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -22,22 +22,22 @@ colors:
info: "#006395"
typography:
title:
- fontFamily: "JetBrains Mono, Microsoft YaHei, PingFang SC, monospace"
+ fontFamily: "JetBrains Mono, CaiWu CJK, monospace"
fontSize: "0.875rem"
fontWeight: 600
lineHeight: 1.25
body:
- fontFamily: "JetBrains Mono, Microsoft YaHei, PingFang SC, monospace"
+ fontFamily: "JetBrains Mono, CaiWu CJK, monospace"
fontSize: "0.8125rem"
fontWeight: 400
lineHeight: 1.5
label:
- fontFamily: "JetBrains Mono, Microsoft YaHei, PingFang SC, monospace"
+ fontFamily: "JetBrains Mono, CaiWu CJK, monospace"
fontSize: "0.75rem"
fontWeight: 500
lineHeight: 1.333
data:
- fontFamily: "JetBrains Mono, Microsoft YaHei, PingFang SC, monospace"
+ fontFamily: "JetBrains Mono, CaiWu CJK, monospace"
fontSize: "0.78125rem"
fontWeight: 400
lineHeight: 1.5
@@ -180,9 +180,9 @@ CaiWuXiTong 是一个需要长时间、高频率使用的财务工作台。视
## 3. Typography
-**Display Font:** JetBrains Mono (with Microsoft YaHei, PingFang SC, monospace)
+**Display Font:** JetBrains Mono (with bundled CaiWu CJK, monospace)
-**Body Font:** JetBrains Mono (with Microsoft YaHei, PingFang SC, monospace)
+**Body Font:** JetBrains Mono (with bundled CaiWu CJK, monospace)
**Label/Mono Font:** JetBrains Mono
@@ -206,6 +206,8 @@ CaiWuXiTong 是一个需要长时间、高频率使用的财务工作台。视
**The One-Family Rule.** 按钮、标签、导航、表格和正文都使用同一字体家族;禁止在数据界面引入装饰性展示字体。
+**The Cross-Platform Control Rule.** 业务控件不得依赖 macOS、Windows 或不同浏览器的原生外观。字体使用仓库内置 Web 字体;下拉框、复选框、日期选择、联想输入使用项目自绘控件;桌面筛选区使用明确 CSS 网格。新页面必须在 Safari 与 Chromium 下验证布局、换行和控件尺寸一致。
+
## 4. Elevation
界面默认采用“色调分层 + 细边界”,不是依赖大阴影堆叠。工具轨道、功能栏和内容区通过背景明度形成第一层深度;内容面板通过白色表面与细边界形成第二层。阴影只用于浮层或短暂交互反馈。
@@ -263,6 +265,7 @@ CaiWuXiTong 是一个需要长时间、高频率使用的财务工作台。视
- 标准高度 34px,8px 圆角,白底、1px 边界;标签位于字段上方,12px/500。
- 聚焦使用深金边框和半透明焦点环;占位文字至少使用辅助墨色,不允许低对比浅灰。
- 错误状态使用语义危险色、明确文字和错误图标;不能只把边框染红。
+- 不使用原生 `datalist`、系统日期框或未归一的系统下拉外观承载业务交互;关联选项使用项目自绘菜单或勾选面板,避免 macOS 与 Windows 显示不同。
### Cards / Containers
diff --git a/app/Http/Controllers/Business/SystemizationTodos.php b/app/Http/Controllers/Business/SystemizationTodos.php
index 4d758834..20035706 100644
--- a/app/Http/Controllers/Business/SystemizationTodos.php
+++ b/app/Http/Controllers/Business/SystemizationTodos.php
@@ -26,7 +26,7 @@ class SystemizationTodos extends Controller
'today' => '今天到期',
'upcoming' => '剩余天数',
'pending' => '时间待定',
- 'none' => '无提醒',
+ 'none' => '已完成',
];
private const OWNER_OPTIONS = ['人参', '晨曦', '常仪', '待定'];
private const COLUMN_WIDTHS_SETTING = 'business.systemization_todos.column_widths';
@@ -196,6 +196,7 @@ class SystemizationTodos extends Controller
'label' => $status,
]);
}
+ $todoAssignees = $this->peopleOptions('assignee');
return [
'view' => 'business.pages.systemization-todos',
@@ -206,7 +207,8 @@ class SystemizationTodos extends Controller
'todoCategories' => $categories,
'todoContentOptions' => $this->contentOptions(),
'todoOwners' => collect(self::OWNER_OPTIONS),
- 'todoAssignees' => $this->peopleOptions('assignee'),
+ 'todoAssignees' => $todoAssignees,
+ 'todoAssigneeOwners' => $this->assigneeOwnerMap($todoAssignees),
'todoStatuses' => SystemizationTodo::STATUSES,
'todoStatusOptions' => $statusOptions,
'todoReminderOptions' => collect(self::REMINDER_OPTIONS)
@@ -898,6 +900,46 @@ class SystemizationTodos extends Controller
->values();
}
+ private function assigneeOwnerMap(Collection $assignees): array
+ {
+ $derived = [];
+ SystemizationTodo::query()
+ ->whereNotNull('assignee')
+ ->where('assignee', '<>', '')
+ ->get(['owner', 'assignee'])
+ ->each(function (SystemizationTodo $todo) use (&$derived): void {
+ $owners = $this->ownerNames($todo->owner);
+ foreach ($this->personNames($todo->assignee) as $assignee) {
+ foreach ($owners as $owner) {
+ $derived[$assignee][$owner] = true;
+ }
+ }
+ });
+
+ $map = [];
+ foreach ($assignees as $assignee) {
+ if (isset(self::OWNER_ALIASES[$assignee])) {
+ $map[$assignee] = [self::OWNER_ALIASES[$assignee]];
+ continue;
+ }
+ $ownerSet = $derived[$assignee] ?? [];
+ $map[$assignee] = array_values(array_filter(
+ self::OWNER_OPTIONS,
+ static fn (string $owner): bool => isset($ownerSet[$owner]),
+ ));
+ }
+
+ return $map;
+ }
+
+ private function personNames(?string $value): array
+ {
+ return array_values(array_unique(array_filter(
+ array_map('trim', preg_split('/[、,,;;\/]+/u', trim((string) $value)) ?: []),
+ static fn (string $name): bool => $name !== '' && $name !== '—',
+ )));
+ }
+
private function normalizeOwnerFilterValues(array $owners): array
{
return collect($owners)
diff --git a/resources/views/business/pages/systemization-todos.blade.php b/resources/views/business/pages/systemization-todos.blade.php
index aa319e5a..43cce9ce 100644
--- a/resources/views/business/pages/systemization-todos.blade.php
+++ b/resources/views/business/pages/systemization-todos.blade.php
@@ -50,6 +50,7 @@
.sys-todo-filter__multi-value { font-size:.8125rem; line-height:1.35; overflow-wrap:anywhere; white-space:normal; }
.sys-todo-filter__multi-menu { position:absolute; top:calc(100% + .3rem); left:0; z-index:20; display:grid; width:max(100%,13rem); max-height:17rem; padding:.35rem; border:1px solid var(--sys-border); border-radius:.55rem; background:#fff; box-shadow:0 8px 22px rgba(23,23,23,.16); overflow:auto; }
.sys-todo-filter__multi-menu label { display:flex; flex:none; align-items:center; gap:.45rem; min-height:2rem; padding:.3rem .45rem; border-radius:.35rem; color:#333; font-size:.78rem; cursor:pointer; }
+ .sys-todo-filter__multi-menu label[hidden] { display:none; }
.sys-todo-filter__multi-menu label:hover { background:var(--sys-gold-soft); }
.sys-todo-filter__multi-menu input[type="checkbox"] { flex:0 0 auto; width:1rem; height:1rem; padding:0; accent-color:var(--sys-gold); }
.sys-todo-filter__multi-empty { padding:.65rem; color:#777; font-size:.75rem; text-align:center; }
@@ -166,6 +167,7 @@
.sys-todo-new-category[hidden] { display:none; }
.sys-todo-owner-checks { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:.2rem .45rem; max-height:7rem; padding:.4rem; border:1px solid var(--sys-border); border-radius:.45rem; background:#fff; overflow:auto; }
.sys-todo-owner-checks label { display:flex; align-items:center; gap:.35rem; min-height:1.6rem; color:#333; font-size:.76rem; cursor:pointer; }
+ .sys-todo-owner-checks label[hidden] { display:none; }
.sys-todo-owner-checks label:hover { color:var(--sys-gold); }
.sys-todo-owner-checks input[type="checkbox"] { flex:0 0 auto; width:1rem; height:1rem; min-height:0; padding:0; accent-color:var(--sys-gold); }
.sys-todo-page input[type="checkbox"] {
@@ -193,6 +195,7 @@
background-size:.72rem;
}
.sys-todo-page input[type="checkbox"]:focus-visible { outline:2px solid var(--sys-gold); outline-offset:2px; }
+ .sys-todo-assignee-note { margin:0; color:#777; font-size:.68rem; line-height:1.4; }
.sys-todo-upload-zone { display:flex; flex-wrap:wrap; align-items:center; gap:.45rem .65rem; min-height:4.5rem; padding:.7rem; border:1px dashed #cfc3ad; border-radius:.55rem; background:#fcfaf5; }
.sys-todo-upload-zone input[type="file"] { position:absolute; width:1px; height:1px; padding:0; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }
.sys-todo-upload-button { display:inline-flex; align-items:center; justify-content:center; min-height:2rem; padding:0 .75rem; border:1px solid var(--sys-gold); border-radius:.4rem; background:var(--sys-gold); color:#fff; font-size:.78rem; cursor:pointer; }
@@ -397,13 +400,14 @@
@@ -776,7 +780,22 @@
-
+
+
责任人
+
+ @forelse ($todoAssignees as $assignee)
+
+ @empty
+ 暂无可选责任人
+ @endforelse
+ 当前总负责人暂无对应责任人
+
+
+
选择总负责人后可快速定位对应责任人;选择责任人也会自动关联总负责人。
+
@@ -940,8 +959,6 @@
-
-