DG9 统一全站 UI 布局样板迁移
关联 Issue: DG9-0..DG9-12
这个提交包含在:
@@ -1,22 +1,104 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
export function ListWorkspace({ className, children }: { className?: string; children: ReactNode }) {
|
||||
type WorkspaceChildrenProps = {
|
||||
className?: string;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
type WorkspaceHeaderProps = {
|
||||
eyebrow?: string;
|
||||
title: string;
|
||||
subtitle?: ReactNode;
|
||||
meta?: ReactNode;
|
||||
actions?: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function ListWorkspace({ className, children }: WorkspaceChildrenProps) {
|
||||
return <div className={cn("page-scaffold", className)}>{children}</div>;
|
||||
}
|
||||
|
||||
export function FilterBar({ className, children }: { className?: string; children: ReactNode }) {
|
||||
export function WorkspaceHeader({ eyebrow, title, subtitle, meta, actions, className }: WorkspaceHeaderProps) {
|
||||
return (
|
||||
<header className={cn("flex flex-col gap-4 md:flex-row md:items-end md:justify-between", className)}>
|
||||
<div className="min-w-0 max-w-3xl">
|
||||
{eyebrow ? <div className="text-xs font-bold uppercase text-primary">{eyebrow}</div> : null}
|
||||
<h1 className="mt-1 text-2xl font-semibold text-[var(--color-text-primary)] md:text-3xl">{title}</h1>
|
||||
{subtitle ? <div className="mt-2 text-sm leading-6 text-[var(--color-text-secondary)]">{subtitle}</div> : null}
|
||||
{meta ? <div className="mt-3 flex flex-wrap gap-2">{meta}</div> : null}
|
||||
</div>
|
||||
{actions ? <div className="flex shrink-0 flex-wrap items-center gap-2">{actions}</div> : null}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export function FilterBar({ className, children }: WorkspaceChildrenProps) {
|
||||
return <section className={cn("card-std p-4 md:p-5", className)}>{children}</section>;
|
||||
}
|
||||
|
||||
export function StatStrip({ className, children }: { className?: string; children: ReactNode }) {
|
||||
return <section className={cn("grid gap-4 md:grid-cols-3", className)}>{children}</section>;
|
||||
export function FilterGrid({ className, children }: WorkspaceChildrenProps) {
|
||||
return <div className={cn("grid gap-3 md:grid-cols-2 xl:grid-cols-4", className)}>{children}</div>;
|
||||
}
|
||||
|
||||
export function ActionToolbar({ className, children }: { className?: string; children: ReactNode }) {
|
||||
export function StatStrip({
|
||||
className,
|
||||
columns = 3,
|
||||
children,
|
||||
}: WorkspaceChildrenProps & { columns?: 2 | 3 | 4 }) {
|
||||
const columnClass = {
|
||||
2: "md:grid-cols-2",
|
||||
3: "md:grid-cols-3",
|
||||
4: "md:grid-cols-2 xl:grid-cols-4",
|
||||
}[columns];
|
||||
return <section className={cn("grid gap-4", columnClass, className)}>{children}</section>;
|
||||
}
|
||||
|
||||
export function WorkspaceSection({
|
||||
title,
|
||||
description,
|
||||
actions,
|
||||
className,
|
||||
children,
|
||||
}: WorkspaceChildrenProps & {
|
||||
title?: string;
|
||||
description?: ReactNode;
|
||||
actions?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className={cn("card-std space-y-4 p-4 md:p-5", className)}>
|
||||
{title || description || actions ? (
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
||||
<div className="min-w-0">
|
||||
{title ? <h2 className="text-base font-semibold text-[var(--color-text-primary)]">{title}</h2> : null}
|
||||
{description ? <div className="mt-1 text-sm leading-6 text-[var(--color-text-secondary)]">{description}</div> : null}
|
||||
</div>
|
||||
{actions ? <div className="flex shrink-0 flex-wrap items-center gap-2">{actions}</div> : null}
|
||||
</div>
|
||||
) : null}
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export function ActionToolbar({ className, children }: WorkspaceChildrenProps) {
|
||||
return <div className={cn("flex flex-wrap items-center justify-between gap-3", className)}>{children}</div>;
|
||||
}
|
||||
|
||||
export function PrimaryTable({ className, children }: { className?: string; children: ReactNode }) {
|
||||
export function PrimaryTable({ className, children }: WorkspaceChildrenProps) {
|
||||
return <section className={cn("table-container p-0", className)}>{children}</section>;
|
||||
}
|
||||
|
||||
export function WorkspaceEmptyState({
|
||||
className,
|
||||
children = "暂无数据",
|
||||
}: {
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className={cn("rounded-xl border border-dashed border-zinc-300 px-4 py-8 text-center text-sm text-zinc-500", className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户