From 1cfffab48aa9f05b5c43e9d2dcaf05348451c48d Mon Sep 17 00:00:00 2001 From: chenpei Date: Thu, 9 Jul 2026 18:53:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=80=83=E6=A0=B8?= =?UTF-8?q?=E8=B0=83=E7=A0=94=E6=9C=88=E5=BA=A6=E7=9C=8B=E6=9D=BF=E5=89=8D?= =?UTF-8?q?=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/performance-survey/[[...path]]/page.tsx | 445 +++++++++++--------- design-qa.md | 35 ++ 2 files changed, 285 insertions(+), 195 deletions(-) create mode 100644 design-qa.md diff --git a/app/performance-survey/[[...path]]/page.tsx b/app/performance-survey/[[...path]]/page.tsx index 2f722dd..413eb06 100644 --- a/app/performance-survey/[[...path]]/page.tsx +++ b/app/performance-survey/[[...path]]/page.tsx @@ -368,9 +368,7 @@ function Dashboard() { const [tab, setTab] = useState(() => ( ['personal', 'public', 'manage'].includes(requestedTab) ? requestedTab : 'personal' )) - const surveys = useApi(tab === 'manage' ? '/surveys' : null, [tab]) const me = useApi('/me', []) - const canManage = Boolean(me.data?.permissions?.isDepartmentAdmin) const displayName = me.data?.employee?.displayName || me.data?.employee?.flowerName || me.data?.user?.name || '当前用户' const departmentLabel = me.data?.employee?.departmentName || me.data?.user?.department || '未同步部门' const positionLabel = [me.data?.employee?.positionTitle, me.data?.employee?.positionLevel].filter(Boolean).join(' · ') @@ -395,23 +393,25 @@ function Dashboard() { } return ( -
-
-
-

我的调研

-
-
- {displayName} - · - {departmentLabel} - {positionLabel && ( - <> +
+
+
+
+

我的调研

+
+ {displayName} · - {positionLabel} - - )} + {departmentLabel} + {positionLabel && ( + <> + · + {positionLabel} + + )} +
+
-
+
{[ { id: 'personal', label: '个人看板' }, { id: 'public', label: '公共看板' }, @@ -421,7 +421,7 @@ function Dashboard() { key={item.id} type="button" onClick={() => selectTab(item.id)} - className={`shrink-0 border-b-2 px-1 pb-3 text-[14px] font-semibold transition-colors ${ + className={`shrink-0 border-b-2 px-0 pb-3 text-[14px] font-semibold transition-colors ${ tab === item.id ? 'border-[#0052d9] text-[#0052d9]' : 'border-transparent text-[#344054] hover:text-[#0052d9] dark:text-gray-300' @@ -433,18 +433,11 @@ function Dashboard() {
- {tab === 'manage' && surveys.loading && 加载中...} - {surveys.error && } {tab !== 'manage' && ( )} {tab === 'manage' && ( - { - await surveys.refresh() - }} - /> + )}
) @@ -529,12 +522,12 @@ function PerformanceDashboardBoard({ view }: { view: 'personal' | 'public' }) { const templates = dashboard.data?.templates || [] const permissions = dashboard.data?.permissions || {} const personalRows = (dashboard.data?.personal?.rows || []).filter((row: any) => row.hasData) - const publicRows = dashboard.data?.public?.rows || [] + const publicRows = (dashboard.data?.public?.rows || []).filter((row: any) => row.hasData) return (
{view === 'public' && ( -
+

公共看板

{(department || employee) && ( @@ -544,13 +537,13 @@ function PerformanceDashboardBoard({ view }: { view: 'personal' | 'public' }) { setDepartment('') setEmployee(null) }} - className="rounded-full bg-[#f3f6fb] px-3 py-1.5 text-xs font-semibold text-[#475467] hover:bg-[#e8eef7] dark:bg-gray-800 dark:text-gray-200" + className="rounded-full border border-[#dbe5f2] bg-white px-3 py-1.5 text-xs font-semibold text-[#475467] hover:bg-[#f8fafc] dark:border-gray-700 dark:bg-gray-950 dark:text-gray-200" > 清除 )}
-
+
return ( -
- {rows.map((row) => ( -
-
-
{row.month}
-
满分 120
-
-
- {templates.map((template) => ( - onOpenDetail({ - month: row.month, - employeeId: row.employee.id, - templateCode: template.code, - })} - /> +
+
+ {rows.map((row) => ( +
+
+
{row.month}
+
/120
+
+
+ {templates.map((template) => ( + onOpenDetail({ + month: row.month, + employeeId: row.employee.id, + templateCode: template.code, + })} + /> + ))} +
+
+ ))} +
+
+ + + + + {templates.map((template) => ( + + ))} + + + + {rows.map((row) => ( + + + {templates.map((template) => ( + + ))} + ))} - - - ))} + +
月份{template.name}
{row.month} + onOpenDetail({ + month: row.month, + employeeId: row.employee.id, + templateCode: template.code, + })} + /> +
+
) } @@ -651,20 +677,20 @@ function MonthlyPublicLedger({ if (rows.length === 0) return return ( -
- {rows.map((row) => ( -
-
+
+
+ {rows.map((row) => ( +
{row.employee.displayName}
{[row.employee.departmentName, row.employee.positionLevel || row.employee.levelType].filter(Boolean).join(' · ')}
-
+
{templates.map((template) => ( ))}
-
-
- ))} +
+ ))} +
+
+ + + + + + {templates.map((template) => ( + + ))} + + + + {rows.map((row) => ( + + + + {templates.map((template) => ( + + ))} + + ))} + +
人员组织 / 职级{template.name}
{row.employee.displayName} +
{row.employee.departmentName || '-'}
+
{row.employee.positionLevel || row.employee.levelType || '-'}
+
+ onOpenDetail({ + month: row.month, + employeeId: row.employee.id, + templateCode: template.code, + })} + /> +
+
) } @@ -704,8 +767,8 @@ function MonthlyScoreCell({ const canOpen = Boolean(hasScore && (canOpenOverride ?? cell?.detailAllowed)) const className = `${compact ? 'min-h-[72px] px-3 py-3' : 'min-h-[88px] px-3.5 py-3.5'} rounded-[14px] border text-left transition-colors ${ canOpen - ? 'border-[#cfe0ff] bg-[#f7fbff] hover:border-[#1677ff] hover:bg-[#eef6ff] dark:border-blue-500/30 dark:bg-blue-500/10' - : 'border-[#e7edf5] bg-[#f8fafc] dark:border-gray-700 dark:bg-gray-800' + ? 'border-[#cfe0ff] bg-white hover:border-[#1677ff] hover:bg-[#f7fbff] dark:border-blue-500/30 dark:bg-blue-500/10' + : 'border-[#e7edf5] bg-[#fafbfc] dark:border-gray-700 dark:bg-gray-800' }` const content = ( <> @@ -730,9 +793,44 @@ function MonthlyScoreCell({ ) } +function MonthlyTableScoreCell({ + cell, + canOpenOverride, + onClick, +}: { + cell: any + canOpenOverride?: boolean + onClick: () => void +}) { + const hasScore = cell && cell.score !== null && cell.score !== undefined + const canOpen = Boolean(hasScore && (canOpenOverride ?? cell?.detailAllowed)) + const content = ( + <> + + {hasScore ? formatNullableScore(cell.score) : '-'} + + {hasScore && /120} + + ) + + if (!canOpen) { + return
{content}
+ } + + return ( + + ) +} + function EmptyLedger({ title }: { title: string }) { return ( -
+
0

{title}

@@ -756,8 +854,8 @@ function MonthlyScoreDetailModal({ if (!request) return null return ( -
-
+
+
{detail.data?.month || request.month}
@@ -781,7 +879,7 @@ function MonthlyScoreDetailModal({ {detail.error && } {detail.data && (
-
+
最终分
{formatNullableScore(detail.data.score)} /120 @@ -912,7 +1010,7 @@ function SingleEmployeeFilter({ onChange={(event) => setKeyword(event.target.value)} onFocus={() => keyword.trim() && setOpen(true)} placeholder={value ? value.displayName : '筛选人员'} - className="w-full rounded-lg border border-[#dbe5f2] bg-white px-3 py-2 text-sm font-medium text-[#101828] dark:border-gray-700 dark:bg-gray-900 dark:text-white" + className="h-11 w-full rounded-[12px] border border-[#dbe5f2] bg-white px-3 text-sm font-medium text-[#101828] outline-none transition-colors focus:border-[#1677ff] focus:ring-4 focus:ring-[#1677ff]/10 dark:border-gray-700 dark:bg-gray-950 dark:text-white" /> {value && ( {createdSurvey && ( -
- 已创建,系统会按评价人发送钉钉通知。 +
+ 已发布
)}
@@ -1875,11 +1930,11 @@ function EmployeePicker({ onKeyDown={(event) => { if (event.key === 'Escape') setOpen(false) }} - className="rounded-lg border border-gray-300 px-3 py-2 text-sm dark:border-gray-600 dark:bg-gray-900 dark:text-white" + className="h-11 rounded-[12px] border border-[#dbe5f2] bg-white px-3 text-sm font-medium text-gray-900 outline-none transition-colors focus:border-[#1677ff] focus:ring-4 focus:ring-[#1677ff]/10 dark:border-gray-700 dark:bg-gray-950 dark:text-white" /> {open && keyword && ( -
+
{loading ? (
搜索中...
) : error ? ( @@ -1893,7 +1948,7 @@ function EmployeePicker({ type="button" disabled={selectedIds.has(employee.id)} onClick={() => selectEmployee(employee)} - className="w-full px-3 py-2.5 text-left text-sm hover:bg-gray-50 disabled:opacity-50 dark:hover:bg-gray-700" + className="w-full px-3 py-2.5 text-left text-sm transition-colors hover:bg-[#f6f9ff] disabled:opacity-50 dark:hover:bg-gray-800" >
{employee.displayName}
@@ -2058,15 +2113,15 @@ function DepartmentPicker({ return (
diff --git a/design-qa.md b/design-qa.md new file mode 100644 index 0000000..de29b8d --- /dev/null +++ b/design-qa.md @@ -0,0 +1,35 @@ +**视觉来源** +- Stitch 专案:`https://stitch.withgoogle.com/projects/10867034024263334547` +- 本次对齐的设计稿: + - `个人看板桌面端-优化版` + - `公共看板桌面端-优化版` + - `分数详情抽屉桌面端-优化版` + - `发起考核桌面端-优化版` + - 既有移动端稿:`个人看板` / `公共看板` / `详情弹层` + +**实现范围** +- 文件:`app/performance-survey/[[...path]]/page.tsx` +- 页面:`/performance-survey` +- 主入口: + - `tab=personal` + - `tab=public` + - `tab=manage` + +**验证结果** +- Lint:`npm run lint -- --file "app/performance-survey/[[...path]]/page.tsx"` 通过。 +- 空白检查:`git diff --check -- "app/performance-survey/[[...path]]/page.tsx"` 通过。 +- 浏览器验证: + - 桌面端个人看板已切换为月度台账外壳,没有旧任务标签。 + - 桌面端公共看板已切换为公共筛选 + 台账外壳,没有旧任务标签。 + - 桌面端发起考核只保留月份、模板、被考核人、评价人、发布按钮。 + - 移动端个人/公共/发起考核为白色画布,不再有淡紫色页面背景。 + - 页面未出现二维码、维护、通知记录、旧四标签工作流、模板占比说明等噪音。 + +**检查项** +- 新看板不再展示 `待处理 / 已处理 / 我发起的 / 我的结果` 旧工作流入口。 +- 公共看板渲染前会过滤无数据人员。 +- 发起考核页不再请求旧调研列表。 +- 表单控件统一为白底、细边框、克制蓝色焦点样式。 +- 桌面表格和移动卡片都保留四模板月度台账模型。 + +最终结果:通过