diff --git a/app/performance-survey/[[...path]]/page.tsx b/app/performance-survey/[[...path]]/page.tsx index 413eb06..df44ec8 100644 --- a/app/performance-survey/[[...path]]/page.tsx +++ b/app/performance-survey/[[...path]]/page.tsx @@ -6,7 +6,6 @@ import Link from 'next/link' import { useParams, useRouter, useSearchParams } from 'next/navigation' import { useSession } from 'next-auth/react' import { pinyin } from 'pinyin-pro' -import Header from '@/components/Header' type ApiState = { loading: boolean @@ -294,11 +293,12 @@ export default function PerformanceSurveyPage() { } as any) const isSurveySubmitPage = path[0] === 'survey' && Boolean(path[1]) + const isResultsPage = path[0] === 'results' && Boolean(path[1]) let content = if (isSurveySubmitPage && path[1]) { content = } - if (path[0] === 'results' && path[1]) { + if (isResultsPage && path[1]) { content = } @@ -311,11 +311,8 @@ export default function PerformanceSurveyPage() { } return ( -
-
-
-
- +
+ {isResultsPage && } {content}
) @@ -331,16 +328,16 @@ function PerformanceSurveyMobileHeader({ session }: { session: any }) { : '' return ( -
-
+
+
-
+
{avatar ? ( {name} @@ -348,9 +345,9 @@ function PerformanceSurveyMobileHeader({ session }: { session: any }) { getSurveyInitials(name) )}
-
考核调研
+
考核调研
-
由壹点「素材库」
+
由壹点「素材库」
) @@ -370,8 +367,7 @@ function Dashboard() { )) const me = useApi('/me', []) 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(' · ') + const avatar = me.data?.user?.avatar || me.data?.user?.image || '' const availableTabIds = useMemo( () => ['personal', 'public', 'manage'], [], @@ -391,55 +387,118 @@ function Dashboard() { setTab(nextTab) router.replace(`/performance-survey?tab=${encodeURIComponent(nextTab)}`, { scroll: false }) } + const tabItems = [ + { id: 'personal', label: '个人看板' }, + { id: 'public', label: '公共看板' }, + { id: 'manage', label: '发起考核' }, + ] + const pageTitle = tab === 'public' ? '公共看板' : tab === 'manage' ? '新建考核计划' : '个人绩效概览' + const pageSubtitle = tab === 'personal' + ? '过去 12 个月的考核详细数据' + : tab === 'public' + ? '' + : '' return ( -
-
-
-
-

我的调研

-
- {displayName} - · - {departmentLabel} - {positionLabel && ( - <> - · - {positionLabel} - - )} -
-
-
-
- {[ - { id: 'personal', label: '个人看板' }, - { id: 'public', label: '公共看板' }, - { id: 'manage', label: '发起考核' }, - ].map((item) => ( - - ))} -
-
+ <> + +
+
+

{pageTitle}

+ {pageSubtitle &&

{pageSubtitle}

} +
- {tab !== 'manage' && ( - - )} - {tab === 'manage' && ( - - )} -
+ {tab !== 'manage' && ( + + )} + {tab === 'manage' && ( + + )} +
+ + + ) +} + +function PerformanceSurveyDashboardHeader({ + activeTab, + avatar, + displayName, + onSelectTab, + tabItems, +}: { + activeTab: string + avatar: string + displayName: string + onSelectTab: (tab: string) => void + tabItems: Array<{ id: string; label: string }> +}) { + return ( +
+
+
+
+ {avatar ? ( + {displayName} + ) : ( + {getSurveyInitials(displayName)} + )} +
+
考核调研
+
+ +
+ 由壹点「素材库」 +
+
+ 由壹点 +
+
+
) } @@ -527,9 +586,22 @@ function PerformanceDashboardBoard({ view }: { view: 'personal' | 'public' }) { return (
{view === 'public' && ( -
-
-

公共看板

+
+
+
统计周期
+ setMonth(event.target.value)} + className="h-10 rounded border border-[#e2e2e2] bg-white px-3 text-[13px] font-semibold text-[#1a1c1c] outline-none transition-colors focus:border-[#0050cb]" + /> +
组织架构
+ setDepartment(value)} + /> + {(department || employee) && ( )}
-
- setMonth(event.target.value)} - className="h-11 rounded-[12px] border border-[#dbe5f2] bg-white px-3 text-sm font-semibold 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" - /> - setDepartment(value)} - /> - -
)} - {dashboard.loading && 看板加载中...} + {dashboard.loading && ( +
+ 看板加载中... +
+ )} {dashboard.error && } {dashboard.data && ( <> @@ -601,15 +663,15 @@ function MonthlyPersonalLedger({ return (
-
+
{rows.map((row) => (
-
{row.month}
-
/120
+
{formatMonthLabel(row.month)}
+
CURRENT
{templates.map((template) => ( @@ -628,20 +690,21 @@ function MonthlyPersonalLedger({
))}
-
+
- - + + {templates.map((template) => ( - + ))} + - + {rows.map((row) => ( - - + + {templates.map((template) => ( ))} + ))} @@ -678,19 +742,22 @@ function MonthlyPublicLedger({ return (
-
+
{rows.map((row) => (
-
-
{row.employee.displayName}
-
+
+
+
{row.employee.displayName}
+
{[row.employee.departmentName, row.employee.positionLevel || row.employee.levelType].filter(Boolean).join(' · ')} +
+ {formatAverageScore(row)}
-
+
{templates.map((template) => ( ))}
-
+
月份
月份{template.name}{getTemplateDisplayName(template)}综合评分
{row.month}
{row.month} {formatAverageScore(row)}
- - - + + + {templates.map((template) => ( - + ))} + - + {rows.map((row) => ( - - - + + {templates.map((template) => ( - ))} + ))} @@ -765,19 +842,19 @@ function MonthlyScoreCell({ }) { const hasScore = cell && cell.score !== null && cell.score !== undefined 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 ${ + const className = `${compact ? 'min-h-[58px] px-2 py-2' : 'min-h-[86px] px-3 py-3'} border text-left transition-colors ${ canOpen - ? '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' + ? 'border-[#c2c6d8] bg-white hover:border-[#0050cb] hover:bg-[#f9fbff]' + : 'border-[#e2e2e2] bg-[#f9f9f9]' }` const content = ( <> -
{template.name}
+
{getTemplateDisplayName(template)}
- + {hasScore ? formatNullableScore(cell.score) : '-'} - {hasScore && /120} + {hasScore && /120}
) @@ -806,22 +883,22 @@ function MonthlyTableScoreCell({ const canOpen = Boolean(hasScore && (canOpenOverride ?? cell?.detailAllowed)) const content = ( <> - + {hasScore ? formatNullableScore(cell.score) : '-'} - {hasScore && /120} + {hasScore && /120} ) if (!canOpen) { - return
{content}
+ return
{content}
} return ( @@ -830,9 +907,9 @@ function MonthlyTableScoreCell({ function EmptyLedger({ title }: { title: string }) { return ( -
-
0
-

{title}

+
+
0
+

{title}

) } @@ -854,49 +931,49 @@ function MonthlyScoreDetailModal({ if (!request) return null return ( -
-
-
+
+
+
-
{detail.data?.month || request.month}
-

+
{detail.data?.month || request.month}
+

{detail.data?.template?.name || '考核明细'}

{detail.data?.employee && ( -

{detail.data.employee.displayName}

+

{detail.data.employee.displayName}

)}
- {detail.loading &&
加载中...
} + {detail.loading &&
加载中...
} {detail.error && } {detail.data && (
-
-
最终分
-
- {formatNullableScore(detail.data.score)} /120 +
+
最终分
+
+ {formatNullableScore(detail.data.score)} /120
{detail.data.workItems?.length > 0 && (
{detail.data.workItems.map((item: any, index: number) => ( -
+
-
作品 {index + 1}
-

{item.title}

- {item.description &&

{item.description}

} +
作品 {index + 1}
+

{item.title}

+ {item.description &&

{item.description}

}
-
{formatNullableScore(item.averageScore)}
+
{formatNullableScore(item.averageScore)}
@@ -915,19 +992,19 @@ function MonthlyScoreDetailModal({ function ScoreParticipantList({ items }: { items: any[] }) { if (!items.length) { - return
暂无评分记录
+ return
暂无评分记录
} return ( -
+
{items.map((item) => ( -
+
-
{item.name}
+
{item.name}
{formatNullableScore(item.score)}
- {item.comment &&

{item.comment}

} - {item.submittedAt &&
{formatDate(item.submittedAt)}
} + {item.comment &&

{item.comment}

} + {item.submittedAt &&
{formatDate(item.submittedAt)}
}
))}
@@ -964,6 +1041,32 @@ function TemplateSectionCard({ item }: { item: any }) { ) } +function getTemplateDisplayName(template: any) { + const name = String(template?.shortName || template?.name || '') + return name + .replace(/满意度调研/g, '') + .replace(/考核/g, '') + .replace(/\s*[·\-]\s*\d+%/g, '') + .replace(/\s*\d+%/g, '') + .trim() || name || '-' +} + +function formatMonthLabel(value: unknown) { + const text = String(value || '') + const match = text.match(/^(\d{4})-(\d{2})/) + if (!match) return text + return `${match[1]}年${Number(match[2])}月` +} + +function formatAverageScore(row: any) { + const values = Object.values(row?.cells || {}) + .map((cell: any) => Number(cell?.score)) + .filter((value) => Number.isFinite(value)) + if (values.length === 0) return '-' + const average = values.reduce((sum, value) => sum + value, 0) / values.length + return formatNullableScore(average) +} + function formatNullableScore(value: unknown) { if (value === null || value === undefined || value === '') return '-' const numberValue = Number(value) @@ -1010,7 +1113,7 @@ function SingleEmployeeFilter({ onChange={(event) => setKeyword(event.target.value)} onFocus={() => keyword.trim() && setOpen(true)} placeholder={value ? value.displayName : '筛选人员'} - 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" + className="h-10 w-full rounded border border-[#e2e2e2] bg-white px-3 text-[13px] font-semibold text-[#1a1c1c] outline-none transition-colors focus:border-[#0050cb]" /> {value && ( )} {open && ( -
+
{loading ? ( -
搜索中...
+
搜索中...
) : items.length === 0 ? ( -
无匹配人员
+
无匹配人员
) : ( items.map((employee) => ( )) )} @@ -1478,8 +1581,8 @@ function DepartmentFilterTree({ return (
@@ -1488,7 +1591,7 @@ function DepartmentFilterTree({ onClick={() => hasChildren && toggle(department.id)} className={`flex h-6 w-6 shrink-0 items-center justify-center rounded border text-xs ${ hasChildren - ? 'border-gray-300 text-gray-600 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-600' + ? 'border-[#e2e2e2] text-[#5f5e5e] hover:bg-[#f4f3f3]' : 'border-transparent text-transparent' }`} aria-label={hasChildren ? `${isExpanded ? '收起' : '展开'}${department.name}` : undefined} @@ -1516,24 +1619,24 @@ function DepartmentFilterTree({ {open && ( -
+
setKeyword(event.target.value)} placeholder="搜索组织" - className="mb-2 h-10 w-full rounded-[12px] border border-[#dbe5f2] px-3 text-sm text-[#101828] outline-none focus:border-[#1677ff] focus:ring-4 focus:ring-[#1677ff]/10 dark:border-gray-700 dark:bg-gray-950 dark:text-white" + className="mb-2 h-9 w-full rounded border border-[#eeeeee] px-3 text-sm text-[#1a1c1c] outline-none focus:border-[#0050cb]" /> {createdSurvey && ( -
+
已发布
)}
+ +
+
+
+ {surveyKind !== 'item_course' ? ( + + ) : ( +
+
{selectedTemplateInfo?.itemName || '调研事项'}
+ setItemName(event.target.value)} + placeholder={selectedTemplateInfo?.itemNamePlaceholder || '填写名称'} + className="h-10 w-full rounded border border-[#eeeeee] px-3 text-[14px] font-semibold outline-none transition-colors focus:border-[#0050cb]" + /> +
+ )} +
+
+ setManualEvaluatorEmployees((current) => mergeEmployeesById(current, [employee]))} + onRemoveEmployee={removeEvaluatorEmployee} + /> +
+
+ { + setEvaluatorDepartmentEmployees((current) => { + const next = { ...current } + if (checked) { + next[department] = employees + } else { + delete next[department] + } + return next + }) + }} + /> +
+
+
) } @@ -1915,8 +2018,8 @@ function EmployeePicker({ return (
-
人员组织 / 职级
人员组织 / 职级{template.name}{getTemplateDisplayName(template)}综合评分
{row.employee.displayName} +
+
+
{getSurveyInitials(row.employee.displayName)}
+
+
{row.employee.displayName}
+ {row.employee.jobNumber &&
{row.employee.jobNumber}
} +
+
+
{row.employee.departmentName || '-'}
{row.employee.positionLevel || row.employee.levelType || '-'}
+ {formatAverageScore(row)}