feat(front-end): 初始化STP前端工程并提交页面框架

引入前端项目基础结构、页面骨架和UI组件,建立可运行的开发与构建配置,方便后续按模块迭代。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-22 14:25:22 +08:00
parent f6ff2e3625
commit 7f0f79420c
107 changed files with 18103 additions and 0 deletions
@@ -0,0 +1,190 @@
import { Sparkles, Check } from "lucide-react";
import { cn } from "../ui/utils";
import { Badge } from "../ui/badge";
import type { ProjectStatus } from "../../data/mock";
/** AI 生成 / AI 能力标签 */
export function AiBadge({ label = "AI 生成", className }: { label?: string; className?: string }) {
return (
<span
className={cn(
"inline-flex items-center gap-1 rounded-md bg-gradient-to-r from-violet-500 to-blue-500 px-2 py-0.5 text-xs font-medium text-white",
className,
)}
>
<Sparkles className="size-3" />
{label}
</span>
);
}
/** 页面标题区 */
export function PageHeader({
title,
description,
actions,
}: {
title: string;
description?: string;
actions?: React.ReactNode;
}) {
return (
<div className="mb-6 flex items-start justify-between gap-4">
<div>
<h1 className="text-foreground">{title}</h1>
{description && <p className="mt-1 text-sm text-muted-foreground">{description}</p>}
</div>
{actions && <div className="flex shrink-0 items-center gap-2">{actions}</div>}
</div>
);
}
const statusColor: Record<string, string> = {
: "bg-slate-100 text-slate-700",
: "bg-amber-100 text-amber-700",
: "bg-sky-100 text-sky-700",
: "bg-indigo-100 text-indigo-700",
线: "bg-blue-100 text-blue-700",
: "bg-violet-100 text-violet-700",
: "bg-teal-100 text-teal-700",
: "bg-emerald-100 text-emerald-700",
};
export function StatusBadge({ status }: { status: ProjectStatus | string }) {
return (
<span
className={cn(
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
statusColor[status] ?? "bg-slate-100 text-slate-700",
)}
>
{status}
</span>
);
}
export function RiskBadge({ risk }: { risk: string }) {
const map: Record<string, string> = {
: "bg-emerald-50 text-emerald-700 border-emerald-200",
: "bg-amber-50 text-amber-700 border-amber-200",
: "bg-red-50 text-red-700 border-red-200",
};
return (
<Badge variant="outline" className={cn("border", map[risk])}>
{risk}
</Badge>
);
}
/** AI 审核结论标签 */
export function AiResultTag({ result }: { result: string }) {
const ok = result === "资质有效";
const map: Record<string, string> = {
: "bg-emerald-50 text-emerald-700 border-emerald-200",
: "bg-amber-50 text-amber-700 border-amber-200",
: "bg-red-50 text-red-700 border-red-200",
};
return (
<span
className={cn(
"inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-xs font-medium",
map[result] ?? "bg-slate-50 text-slate-700 border-slate-200",
)}
>
<Sparkles className="size-3" />
{result}
</span>
);
}
/** 横向步骤条 */
export function Steps({
steps,
current,
}: {
steps: string[];
current: number;
}) {
return (
<div className="flex items-center">
{steps.map((s, i) => {
const done = i < current;
const active = i === current;
return (
<div key={s} className="flex flex-1 items-center last:flex-none">
<div className="flex items-center gap-2">
<div
className={cn(
"flex size-8 shrink-0 items-center justify-center rounded-full border-2 text-sm font-medium transition-colors",
done && "border-primary bg-primary text-primary-foreground",
active && "border-primary bg-primary/10 text-primary",
!done && !active && "border-border bg-card text-muted-foreground",
)}
>
{done ? <Check className="size-4" /> : i + 1}
</div>
<span
className={cn(
"whitespace-nowrap text-sm",
active ? "font-medium text-foreground" : "text-muted-foreground",
)}
>
{s}
</span>
</div>
{i < steps.length - 1 && (
<div className={cn("mx-3 h-0.5 flex-1 rounded", done ? "bg-primary" : "bg-border")} />
)}
</div>
);
})}
</div>
);
}
export interface ApprovalNode {
name: string;
role: string;
status: "completed" | "current" | "pending";
time?: string;
}
/** 审批流程跟踪条 */
export function ApprovalTracker({ nodes }: { nodes: ApprovalNode[] }) {
return (
<div className="flex flex-col gap-0">
{nodes.map((n, i) => (
<div key={n.name} className="flex gap-3">
<div className="flex flex-col items-center">
<div
className={cn(
"flex size-7 shrink-0 items-center justify-center rounded-full text-xs",
n.status === "completed" && "bg-emerald-500 text-white",
n.status === "current" && "bg-primary text-white ring-4 ring-primary/15",
n.status === "pending" && "bg-muted text-muted-foreground",
)}
>
{n.status === "completed" ? <Check className="size-4" /> : i + 1}
</div>
{i < nodes.length - 1 && (
<div className={cn("my-1 w-0.5 flex-1", n.status === "completed" ? "bg-emerald-500" : "bg-border")} />
)}
</div>
<div className="pb-5">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-foreground">{n.role}</span>
{n.status === "current" && (
<span className="rounded bg-primary/10 px-1.5 py-0.5 text-xs text-primary"></span>
)}
{n.status === "completed" && (
<span className="rounded bg-emerald-50 px-1.5 py-0.5 text-xs text-emerald-600"></span>
)}
</div>
<p className="text-xs text-muted-foreground">{n.name}</p>
{n.time && <p className="mt-0.5 text-xs text-muted-foreground/70">{n.time}</p>}
</div>
</div>
))}
</div>
);
}
@@ -0,0 +1,55 @@
import {
LayoutDashboard,
FolderKanban,
Building2,
UserCog,
BarChart3,
Settings,
ClipboardCheck,
ShieldAlert,
PackageOpen,
PackagePlus,
Wallet,
FileSignature,
GitMerge,
Users,
Workflow,
KeyRound,
ScrollText,
Network,
FileCheck2,
Gavel,
FileText,
Award,
Eye,
CheckCircle2,
TrendingUp,
} from "lucide-react";
export const ICONS: Record<string, any> = {
LayoutDashboard,
FolderKanban,
Building2,
UserCog,
BarChart3,
Settings,
ClipboardCheck,
ShieldAlert,
PackageOpen,
PackagePlus,
Wallet,
FileSignature,
GitMerge,
Users,
Workflow,
KeyRound,
ScrollText,
Network,
FileCheck2,
Gavel,
FileText,
Award,
Eye,
CheckCircle2,
TrendingUp,
};