AppShell.tsx 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. "use client";
  2. import Link from "next/link";
  3. import { ArrowLeft, Database, RefreshCw } from "lucide-react";
  4. export function AppShell({
  5. children,
  6. title = "运营流程账本",
  7. titleExtra,
  8. subtitle,
  9. runId,
  10. showBack,
  11. backHref,
  12. onRefresh,
  13. actions
  14. }: {
  15. children: React.ReactNode;
  16. title?: string;
  17. titleExtra?: React.ReactNode;
  18. subtitle?: string;
  19. runId?: string;
  20. showBack?: boolean;
  21. backHref?: string;
  22. onRefresh?: () => void;
  23. actions?: React.ReactNode;
  24. }) {
  25. const resolvedBackHref = backHref || (runId ? `/runs/${encodeURIComponent(runId)}` : "/runs");
  26. return (
  27. <main className="app-shell">
  28. <div className="scroll-area">
  29. <header className="page-header">
  30. <div className="header-left">
  31. {showBack ? (
  32. <Link className="toolbar-button" href={resolvedBackHref}>
  33. <ArrowLeft size={15} />
  34. 返回
  35. </Link>
  36. ) : null}
  37. <Link className="brand" href="/runs">
  38. <Database size={16} />
  39. <span>{title}</span>
  40. </Link>
  41. {titleExtra}
  42. {subtitle ? <span className="header-subtitle">{subtitle}</span> : null}
  43. </div>
  44. <div className="toolbar">
  45. {actions}
  46. {onRefresh ? (
  47. <button className="icon-button" type="button" title="刷新" aria-label="刷新" onClick={onRefresh}>
  48. <RefreshCw size={15} />
  49. </button>
  50. ) : null}
  51. </div>
  52. </header>
  53. {children}
  54. </div>
  55. </main>
  56. );
  57. }