R18-4/R21-2: 收口企微脱敏通知与 Portal 搜索摘要
这个提交包含在:
@@ -28,6 +28,15 @@ export type PortalHomePayload = {
|
||||
weeklyReport: { weekStart: string; status: "draft" | "submitted"; needsHelp: boolean; href: string } | null;
|
||||
weeklySummary: { weekStart: string; submittedCount: number; missingCount: number; needsHelpCount: number; href: string } | null;
|
||||
todoSummary: { openCount: number; highPriorityCount: number; href: string } | null;
|
||||
projectSummary:
|
||||
| {
|
||||
myCount: number;
|
||||
overdueMilestonesCount: number;
|
||||
highPriorityTaskCount: number;
|
||||
openRiskCount: number;
|
||||
href: string;
|
||||
}
|
||||
| null;
|
||||
peopleLifecycle: { onboardingCount: number; offboardingCount: number; assetReturnPendingCount: number; href: string } | null;
|
||||
adoptionSummary:
|
||||
| {
|
||||
|
||||
@@ -9,6 +9,9 @@ export type GlobalSearchHit = {
|
||||
| "purchase_request"
|
||||
| "travel_request"
|
||||
| "asset"
|
||||
| "project"
|
||||
| "contract_registry"
|
||||
| "confidential_doc_ref"
|
||||
| "weekly_report"
|
||||
| "todo";
|
||||
entityId: string;
|
||||
|
||||
@@ -18,6 +18,9 @@ function entityLabel(entityType: string) {
|
||||
if (entityType === "purchase_request") return "采购";
|
||||
if (entityType === "travel_request") return "差旅";
|
||||
if (entityType === "asset") return "资产";
|
||||
if (entityType === "project") return "项目";
|
||||
if (entityType === "contract_registry") return "合同台账";
|
||||
if (entityType === "confidential_doc_ref") return "资料引用";
|
||||
return entityType;
|
||||
}
|
||||
|
||||
@@ -32,7 +35,7 @@ export function GlobalSearchDialog({ open, onOpenChange }: { open: boolean; onOp
|
||||
});
|
||||
|
||||
const message = useMemo(() => {
|
||||
if (!trimmed) return "输入姓名、公告、资产、请假、报销、周报或待办关键字开始搜索。";
|
||||
if (!trimmed) return "输入姓名、公告、项目、合同、资产、请假、报销、周报或待办关键字开始搜索。";
|
||||
if (trimmed.length < 2) return "至少输入 2 个字符。";
|
||||
if (searchQuery.isError) {
|
||||
return isApiError(searchQuery.error)
|
||||
@@ -51,7 +54,7 @@ export function GlobalSearchDialog({ open, onOpenChange }: { open: boolean; onOp
|
||||
<div className="space-y-4">
|
||||
<Input
|
||||
autoFocus
|
||||
placeholder="搜索人员、公告、资产、请假、报销、周报、待办"
|
||||
placeholder="搜索人员、项目、合同、公告、资产、请假、报销、周报、待办"
|
||||
value={keyword}
|
||||
onChange={(event) => setKeyword(event.target.value)}
|
||||
/>
|
||||
|
||||
@@ -60,6 +60,7 @@ export function PortalHomePage() {
|
||||
<Link
|
||||
key={card.key}
|
||||
to={card.href}
|
||||
aria-label={`portal-card-${card.key}`}
|
||||
className={
|
||||
card.severity === "high"
|
||||
? "card-std border-red-200 bg-red-50 p-5"
|
||||
@@ -184,6 +185,11 @@ export function PortalHomePage() {
|
||||
我的待办:未完成 {payload.todoSummary.openCount},高优先级 {payload.todoSummary.highPriorityCount}
|
||||
</Link>
|
||||
) : null}
|
||||
{payload.projectSummary ? (
|
||||
<Link to={payload.projectSummary.href} className="block rounded-lg border border-zinc-200 px-3 py-3 hover:bg-zinc-50">
|
||||
项目摘要:我的项目 {payload.projectSummary.myCount},逾期里程碑 {payload.projectSummary.overdueMilestonesCount},风险 {payload.projectSummary.openRiskCount}
|
||||
</Link>
|
||||
) : null}
|
||||
{payload.peopleLifecycle ? (
|
||||
<Link to={payload.peopleLifecycle.href} className="block rounded-lg border border-zinc-200 px-3 py-3 hover:bg-zinc-50">
|
||||
人员闭环:入职 {payload.peopleLifecycle.onboardingCount},离职 {payload.peopleLifecycle.offboardingCount},资产待归还 {payload.peopleLifecycle.assetReturnPendingCount}
|
||||
|
||||
在新工单中引用
屏蔽一个用户