diff --git a/Cunkebao/app/components/ui-templates/card-grid.tsx b/Cunkebao/app/components/ui-templates/card-grid.tsx new file mode 100644 index 00000000..14453fa0 --- /dev/null +++ b/Cunkebao/app/components/ui-templates/card-grid.tsx @@ -0,0 +1,42 @@ +"use client" + +import type React from "react" +import { cn } from "@/app/lib/utils" + +interface CardGridProps { + children: React.ReactNode + className?: string + columns?: { + sm?: number + md?: number + lg?: number + xl?: number + } + gap?: "none" | "sm" | "md" | "lg" +} + +/** + * 自适应卡片网格组件 + * 根据屏幕尺寸自动调整卡片布局 + */ +export function CardGrid({ children, className, columns = { sm: 1, md: 2, lg: 3, xl: 4 }, gap = "md" }: CardGridProps) { + // 根据gap参数设置间距 + const gapClasses = { + none: "gap-0", + sm: "gap-2", + md: "gap-4", + lg: "gap-6", + } + + // 根据columns参数设置网格列数 + const getGridCols = () => { + const cols = [] + if (columns.sm) cols.push(`grid-cols-${columns.sm}`) + if (columns.md) cols.push(`md:grid-cols-${columns.md}`) + if (columns.lg) cols.push(`lg:grid-cols-${columns.lg}`) + if (columns.xl) cols.push(`xl:grid-cols-${columns.xl}`) + return cols.join(" ") + } + + return
{children}
+} diff --git a/Cunkebao/app/components/ui-templates/cards.tsx b/Cunkebao/app/components/ui-templates/cards.tsx new file mode 100644 index 00000000..543268a7 --- /dev/null +++ b/Cunkebao/app/components/ui-templates/cards.tsx @@ -0,0 +1,138 @@ +"use client" +import { Card } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Clock, TrendingUp, Users } from "lucide-react" + +interface StatsCardProps { + title: string + value: string | number + description?: string + trend?: number + trendLabel?: string + className?: string + valueClassName?: string +} + +/** + * 统计数据卡片 + * 用于展示关键指标数据 + */ +export function StatsCard({ + title, + value, + description, + trend, + trendLabel, + className = "", + valueClassName = "text-xl font-bold text-blue-600", +}: StatsCardProps) { + return ( + +
{title}
+
{value}
+ {description &&
{description}
} + {trend !== undefined && ( +
= 0 ? "text-green-600" : "text-red-600"}`}> + {trend >= 0 ? "↑" : "↓"} {Math.abs(trend)}% {trendLabel || ""} +
+ )} +
+ ) +} + +interface DistributionPlanCardProps { + id: string + name: string + status: "active" | "paused" | "completed" + source: string + sourceIcon: string + targetGroups: string[] + totalUsers: number + dailyAverage: number + lastUpdated: string + createTime: string + creator: string + onView?: (id: string) => void + onEdit?: (id: string) => void + onDelete?: (id: string) => void + onToggleStatus?: (id: string, status: "active" | "paused") => void +} + +/** + * 流量分发计划卡片 + * 用于展示流量分发计划信息 + */ +export function DistributionPlanCard({ + id, + name, + status, + source, + sourceIcon, + targetGroups, + totalUsers, + dailyAverage, + lastUpdated, + createTime, + creator, + onView, + onEdit, + onDelete, + onToggleStatus, +}: DistributionPlanCardProps) { + return ( + +
+
+ {sourceIcon} +

{name}

+ + {status === "active" ? "进行中" : status === "completed" ? "已完成" : "已暂停"} + +
+
+ {onToggleStatus && status !== "completed" && ( + + )} + {onView && ( + + )} + {onEdit && ( + + )} +
+
+ +
+
+
目标人群:{targetGroups.join(", ")}
+
总流量:{totalUsers} 人
+
+
+
日均获取:{dailyAverage} 人
+
创建人:{creator}
+
+
+ +
+
+ + 上次更新:{lastUpdated} +
+
创建时间:{createTime}
+
+
+ ) +} diff --git a/Cunkebao/app/components/ui-templates/form-layout.tsx b/Cunkebao/app/components/ui-templates/form-layout.tsx new file mode 100644 index 00000000..0ca625ef --- /dev/null +++ b/Cunkebao/app/components/ui-templates/form-layout.tsx @@ -0,0 +1,87 @@ +"use client" + +import type React from "react" +import { cn } from "@/app/lib/utils" + +interface FormLayoutProps { + children: React.ReactNode + className?: string + layout?: "vertical" | "horizontal" | "responsive" + labelWidth?: string + gap?: "none" | "sm" | "md" | "lg" +} + +/** + * 自适应表单布局组件 + * 支持垂直、水平和响应式布局 + */ +export function FormLayout({ + children, + className, + layout = "responsive", + labelWidth = "w-32", + gap = "md", +}: FormLayoutProps) { + // 根据gap参数设置间距 + const gapClasses = { + none: "space-y-0", + sm: "space-y-2", + md: "space-y-4", + lg: "space-y-6", + } + + // 根据layout参数设置布局类 + const getLayoutClasses = () => { + switch (layout) { + case "horizontal": + return "form-horizontal" + case "vertical": + return "form-vertical" + case "responsive": + return "form-vertical md:form-horizontal" + default: + return "form-vertical" + } + } + + return ( +
+ {children} +
+ ) +} + +export default FormLayout + +interface FormItemProps { + children: React.ReactNode + label?: React.ReactNode + className?: string + required?: boolean + error?: string +} + +/** + * 表单项组件 + * 配合FormLayout使用 + */ +export function FormItem({ children, label, className, required, error }: FormItemProps) { + return ( +
+ {label && ( +
+ {label} + {required && *} +
+ )} +
+ {children} + {error &&
{error}
} +
+
+ ) +} diff --git a/Cunkebao/app/components/ui-templates/forms.tsx b/Cunkebao/app/components/ui-templates/forms.tsx new file mode 100644 index 00000000..71d00342 --- /dev/null +++ b/Cunkebao/app/components/ui-templates/forms.tsx @@ -0,0 +1,273 @@ +"use client" + +/** + * 表单组件模板 + * + * 包含项目中常用的各种表单组件 + */ + +import type React from "react" +import { Label } from "@/components/ui/label" +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" +import { Slider } from "@/components/ui/slider" +import { Switch } from "@/components/ui/switch" + +interface FormFieldProps { + label: string + htmlFor: string + required?: boolean + description?: string + error?: string + children: React.ReactNode +} + +/** + * 表单字段容器 + * 用于包装表单控件 + */ +export function FormField({ label, htmlFor, required = false, description, error, children }: FormFieldProps) { + return ( +
+ + {children} + {description &&

{description}

} + {error &&

{error}

} +
+ ) +} + +interface TextInputFieldProps { + label: string + id: string + value: string + onChange: (value: string) => void + placeholder?: string + required?: boolean + description?: string + error?: string + type?: string +} + +/** + * 文本输入字段 + * 用于文本输入 + */ +export function TextInputField({ + label, + id, + value, + onChange, + placeholder, + required = false, + description, + error, + type = "text", +}: TextInputFieldProps) { + return ( + + onChange(e.target.value)} placeholder={placeholder} /> + + ) +} + +interface TextareaFieldProps { + label: string + id: string + value: string + onChange: (value: string) => void + placeholder?: string + required?: boolean + description?: string + error?: string +} + +/** + * 多行文本输入字段 + * 用于多行文本输入 + */ +export function TextareaField({ + label, + id, + value, + onChange, + placeholder, + required = false, + description, + error, +}: TextareaFieldProps) { + return ( + +