215 lines
7.9 KiB
TypeScript
215 lines
7.9 KiB
TypeScript
"use client"
|
|
|
|
import { DollarSign, Edit, ExternalLink, ListOrdered, Star, Users } from "lucide-react"
|
|
import Link from "next/link"
|
|
import { useState } from "react"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { Input } from "@/components/ui/input"
|
|
import { Label } from "@/components/ui/label"
|
|
import { Separator } from "@/components/ui/separator"
|
|
import { Textarea } from "@/components/ui/textarea"
|
|
import { resolveOwnerShop } from "@/lib/domain/resolve-current-shop"
|
|
import type { Shop } from "@/lib/types"
|
|
import { useAuthStore } from "@/store/auth"
|
|
import { useShopStore } from "@/store/shops"
|
|
|
|
export default function ShopManagementPage() {
|
|
const userId = useAuthStore((state) => state.user?.id)
|
|
const shops = useShopStore((state) => state.shops)
|
|
const shop = resolveOwnerShop(userId, shops)
|
|
const updateShop = useShopStore((state) => state.updateShop)
|
|
const updateAnnouncement = useShopStore((state) => state.updateAnnouncement)
|
|
const addAnnouncement = useShopStore((state) => state.addAnnouncement)
|
|
|
|
if (!shop) {
|
|
return <div className="text-sm text-muted-foreground">当前账号没有可管理的店铺</div>
|
|
}
|
|
|
|
return (
|
|
<ShopManagementContent
|
|
key={shop.id}
|
|
shop={shop}
|
|
updateShop={updateShop}
|
|
updateAnnouncement={updateAnnouncement}
|
|
addAnnouncement={addAnnouncement}
|
|
/>
|
|
)
|
|
}
|
|
|
|
function ShopManagementContent({
|
|
shop,
|
|
updateShop,
|
|
updateAnnouncement,
|
|
addAnnouncement,
|
|
}: {
|
|
shop: Shop
|
|
updateShop: (shopId: string, patch: Partial<Omit<Shop, "id" | "owner">>) => void
|
|
updateAnnouncement: (shopId: string, index: number, announcement: string) => void
|
|
addAnnouncement: (shopId: string, announcement: string) => void
|
|
}) {
|
|
const [name, setName] = useState(shop.name)
|
|
const [description, setDescription] = useState(shop.description)
|
|
|
|
return (
|
|
<div className="container mx-auto max-w-6xl px-4 py-8 space-y-8">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-2xl font-bold">店铺管理</h1>
|
|
<Button variant="outline" size="sm" asChild>
|
|
<Link href={`/shop/${shop.id}`}>
|
|
<ExternalLink className="mr-1 h-4 w-4" />
|
|
查看店铺主页
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
|
<Card className="hover:shadow-[var(--shadow-card)]">
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium">总订单</CardTitle>
|
|
<ListOrdered className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{shop.totalOrders}</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card className="hover:shadow-[var(--shadow-card)]">
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium">评分</CardTitle>
|
|
<Star className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{shop.rating}</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card className="hover:shadow-[var(--shadow-card)]">
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium">签约打手</CardTitle>
|
|
<Users className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{shop.playerCount}</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card className="hover:shadow-[var(--shadow-card)]">
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium">抽成比例</CardTitle>
|
|
<DollarSign className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">
|
|
{shop.commissionType === "percentage"
|
|
? `${shop.commissionValue}%`
|
|
: `¥${shop.commissionValue}`}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<Card className="hover:shadow-[var(--shadow-card)]">
|
|
<CardHeader>
|
|
<CardTitle className="text-base">基本信息</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="shop-name">店铺名称</Label>
|
|
<Input id="shop-name" value={name} onChange={(event) => setName(event.target.value)} />
|
|
</div>
|
|
<div className="space-y-2">
|
|
<Label htmlFor="shop-desc">店铺简介</Label>
|
|
<Textarea
|
|
id="shop-desc"
|
|
value={description}
|
|
onChange={(event) => setDescription(event.target.value)}
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
<Button
|
|
onClick={() =>
|
|
updateShop(shop.id, {
|
|
name,
|
|
description,
|
|
})
|
|
}
|
|
>
|
|
<Edit className="mr-1 h-4 w-4" />
|
|
保存修改
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="hover:shadow-[var(--shadow-card)]">
|
|
<CardHeader>
|
|
<CardTitle className="text-base">公告管理</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3">
|
|
{shop.announcements.map((announcement) => (
|
|
<div
|
|
key={announcement}
|
|
className="flex items-center justify-between rounded-md border p-3"
|
|
>
|
|
<span className="text-sm">{announcement}</span>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => {
|
|
const next = window.prompt("", announcement)
|
|
if (next === null) return
|
|
const value = next.trim()
|
|
if (!value) return
|
|
const index = shop.announcements.indexOf(announcement)
|
|
if (index < 0) return
|
|
updateAnnouncement(shop.id, index, value)
|
|
}}
|
|
>
|
|
编辑
|
|
</Button>
|
|
</div>
|
|
))}
|
|
<Separator />
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => {
|
|
const next = window.prompt("", "")
|
|
if (next === null) return
|
|
const value = next.trim()
|
|
if (!value) return
|
|
addAnnouncement(shop.id, value)
|
|
}}
|
|
>
|
|
添加公告
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="hover:shadow-[var(--shadow-card)]">
|
|
<CardHeader>
|
|
<CardTitle className="text-base">运营设置</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3 text-sm">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-muted-foreground">派单模式</span>
|
|
<Badge variant="outline">
|
|
{shop.dispatchMode === "manual" ? "手动派单" : "自动派单"}
|
|
</Badge>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-muted-foreground">允许多店挂靠</span>
|
|
<Badge variant={shop.allowMultiShop ? "default" : "secondary"}>
|
|
{shop.allowMultiShop ? "是" : "否"}
|
|
</Badge>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-muted-foreground">允许独立接单</span>
|
|
<Badge variant={shop.allowIndependentOrders ? "default" : "secondary"}>
|
|
{shop.allowIndependentOrders ? "是" : "否"}
|
|
</Badge>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|