first commit
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Sync to CNB / sync (push) Has been cancelled
Delete old workflow runs / del_runs (push) Has been cancelled
Upstream Sync / Sync latest commits from upstream repo (push) Has been cancelled

This commit is contained in:
2026-01-30 16:57:44 +08:00
commit 3d175d75af
119 changed files with 35834 additions and 0 deletions

347
app/move-car/page.tsx Normal file
View File

@@ -0,0 +1,347 @@
"use client";
import { useState } from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { QRCodeSVG } from "qrcode.react";
import { Car, MessageSquare, QrCode, Info, Copy, Zap, RotateCcw } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { toast } from "sonner";
const formSchema = z.object({
plateNumber: z.string().min(1, "请输入车牌号"),
phoneNumber: z
.string()
.min(1, "请输入联系电话")
.regex(/^1[3-9]\d{9}$/, "请输入有效的手机号"),
token: z.string(),
uid: z.string(),
newEnergy: z.boolean(),
});
type FormValues = z.infer<typeof formSchema>;
export default function MoveCar() {
const [generatedUrl, setGeneratedUrl] = useState("");
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
plateNumber: "",
phoneNumber: "",
token: "",
uid: "",
newEnergy: false,
},
});
const onSubmit = (values: FormValues) => {
const url = new URL(window.location.href + "/display");
url.searchParams.append("plateNumber", values.plateNumber);
url.searchParams.append("phoneNumber", values.phoneNumber);
if (values.token) url.searchParams.append("token", values.token);
if (values.uid) url.searchParams.append("uid", values.uid);
if (values.newEnergy) url.searchParams.append("new", "true");
setGeneratedUrl(url.toString());
toast.success("码牌生成成功");
};
const copyUrl = async () => {
try {
await navigator.clipboard.writeText(generatedUrl);
toast.success("链接已复制到剪贴板");
} catch {
toast.error("复制失败");
}
};
const resetForm = () => {
form.reset();
setGeneratedUrl("");
};
return (
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
{/* Page Header */}
<div className="flex items-center space-x-4 border-b pb-4">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-linear-to-br from-amber-400 to-amber-600 shadow-lg">
<Car className="h-6 w-6 text-white" />
</div>
<div>
<h1 className="text-2xl font-bold tracking-tight"></h1>
<p className="text-muted-foreground">
</p>
</div>
</div>
<div className="grid gap-6 lg:grid-cols-2">
{/* Left: Form */}
<div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<Car className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<FormField
control={form.control}
name="plateNumber"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Input placeholder="如京A12345" {...field} className="text-lg" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="phoneNumber"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Input placeholder="如13800138000" {...field} className="text-lg" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="newEnergy"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4 shadow-sm">
<div className="space-y-0.5">
<FormLabel className="text-base flex items-center gap-2">
<Zap className="h-4 w-4 text-emerald-500" />
</FormLabel>
<FormDescription>
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<MessageSquare className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<Alert className="bg-blue-50/50 dark:bg-blue-950/20 text-blue-900 dark:text-blue-200 border-blue-200 dark:border-blue-800">
<Info className="h-4 w-4" />
<AlertTitle></AlertTitle>
<AlertDescription>
</AlertDescription>
</Alert>
<div className="grid grid-cols-2 gap-4">
<FormField
control={form.control}
name="token"
render={({ field }) => (
<FormItem>
<FormLabel className="flex items-center gap-1">
Token
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Info className="h-3 w-3 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent>
<p>WxPusher的应用Token</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</FormLabel>
<FormControl>
<Input placeholder="应用Token" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="uid"
render={({ field }) => (
<FormItem>
<FormLabel className="flex items-center gap-1">
UID
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Info className="h-3 w-3 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent>
<p>UID</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</FormLabel>
<FormControl>
<Input placeholder="用户UID" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="text-xs text-muted-foreground text-right">
<a
href="https://wxpusher.zjiecode.com/docs/#/"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline ml-1"
>
</a>
</div>
</CardContent>
</Card>
<div className="flex gap-4">
<Button type="submit" size="lg" className="flex-1 gap-2">
<QrCode className="h-4 w-4" />
</Button>
<Button type="button" variant="outline" size="lg" onClick={resetForm} className="gap-2">
<RotateCcw className="h-4 w-4" />
</Button>
</div>
</form>
</Form>
</div>
{/* Right: Preview */}
<div>
<Card className="h-full flex flex-col">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<QrCode className="h-4 w-4" />
{generatedUrl ? "生成成功" : "预览区域"}
</CardTitle>
</CardHeader>
<CardContent className="flex-1 flex flex-col justify-center">
{generatedUrl ? (
<div className="space-y-6">
<Alert variant="default" className="border-emerald-200 bg-emerald-50 dark:bg-emerald-950/20 text-emerald-900 dark:text-emerald-200">
<Info className="h-4 w-4" />
<AlertTitle></AlertTitle>
<AlertDescription>
</AlertDescription>
</Alert>
<div className="flex flex-col items-center gap-4 p-8 bg-white rounded-xl shadow-sm border mx-auto">
<QRCodeSVG value={generatedUrl} size={200} />
<span className="text-sm text-muted-foreground"></span>
</div>
<div className="space-y-2">
<span className="text-sm font-medium"></span>
<div className="p-3 bg-muted rounded-md break-all text-xs font-mono">
{generatedUrl}
</div>
<Button onClick={copyUrl} className="w-full gap-2" variant="secondary">
<Copy className="h-4 w-4" />
</Button>
</div>
</div>
) : (
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground space-y-4">
<QrCode className="h-16 w-16 opacity-20" />
<p></p>
</div>
)}
</CardContent>
</Card>
</div>
</div>
{/* Info Card */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<span className="text-xl">💡</span> 使
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid gap-6 md:grid-cols-3">
<div className="space-y-2">
<h4 className="font-medium text-sm"></h4>
<ul className="list-disc pl-4 text-xs text-muted-foreground space-y-1">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<div className="space-y-2">
<h4 className="font-medium text-sm">使</h4>
<ol className="list-decimal pl-4 text-xs text-muted-foreground space-y-1">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ol>
</div>
<div className="space-y-2">
<h4 className="font-medium text-sm"></h4>
<ul className="list-disc pl-4 text-xs text-muted-foreground space-y-1">
<li>访 WxPusher </li>
<li> Token UID</li>
<li></li>
<li></li>
</ul>
</div>
</div>
</CardContent>
</Card>
</div>
);
}