"use client" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { EmptyState } from "@/components/ui/empty-state" import { Switch } from "@/components/ui/switch" import { updateShopTemplate } from "@/lib/api" import { getShopSections } from "@/lib/domain/shop-template" import { toApiError } from "@/lib/errors" import { useMyShop } from "@/lib/hooks/use-my-shop" import { notifyInfo } from "@/lib/toast" import type { Shop, ShopSection } from "@/lib/types" import { AlertCircle, Eye, EyeOff, GripVertical } from "lucide-react" import Link from "next/link" import { type DragEvent, useEffect, useState } from "react" const sectionLabels: Record = { banner: "横幅图片", intro: "店铺简介", services: "服务列表", players: "打手展示", announcements: "公告栏", reviews: "评价展示", } const sectionDescriptions: Record = { banner: "店铺顶部的横幅图片", intro: "店铺的文字介绍", services: "展示店铺提供的服务", players: "展示签约打手列表", announcements: "店铺公告和活动信息", reviews: "展示用户评价", } export default function ShopTemplatesPage() { const { shop, setShop, loading, error } = useMyShop() if (loading) { return (
) } if (error) { return (
) } if (!shop) { return (
) } return } function ShopTemplatesEditor({ shop, setShop, }: { shop: Shop setShop: (shop: Shop | null) => void }) { const [sections, setSections] = useState(getShopSections(shop)) const [dragIndex, setDragIndex] = useState(null) const [dropIndex, setDropIndex] = useState(null) const [showSavedToast, setShowSavedToast] = useState(false) const [saving, setSaving] = useState(false) useEffect(() => { if (!showSavedToast) { return } const timer = window.setTimeout(() => { setShowSavedToast(false) }, 2000) return () => { window.clearTimeout(timer) } }, [showSavedToast]) const toggleSection = (type: ShopSection["type"]) => { setSections((prev) => prev.map((s) => (s.type === type ? { ...s, enabled: !s.enabled } : s))) } const handleDragStart = (index: number) => { setDragIndex(index) } const handleDragOver = (event: DragEvent, index: number) => { event.preventDefault() if (dragIndex === null || dragIndex === index) { setDropIndex(null) return } setDropIndex(index) } const handleDrop = (event: DragEvent, index: number) => { event.preventDefault() if (dragIndex === null || dragIndex === index) { setDropIndex(null) return } setSections((prev) => { const next = [...prev] const [draggedSection] = next.splice(dragIndex, 1) next.splice(index, 0, draggedSection) return next.map((section, order) => ({ ...section, order })) }) setDragIndex(null) setDropIndex(null) } const handleDragEnd = () => { setDragIndex(null) setDropIndex(null) } const handleSaveTemplate = async () => { setSaving(true) try { const templateConfig = { ...shop.templateConfig, sections, } await updateShopTemplate(shop.id, templateConfig) setShop({ ...shop, templateConfig }) setShowSavedToast(true) } catch (error) { notifyInfo(toApiError(error).msg) } finally { setSaving(false) } } return (
{showSavedToast ? (
模板已保存
) : null}

模板编辑

自定义店铺主页的展示内容和顺序

页面组件 {sections.map((section, index) => (
toggleSection(section.type)} />
))}
提示

关闭开关可隐藏对应组件,不会删除已有内容

保存后立即生效,访客将看到最新的店铺主页

) }