feat: add admin site config page and optimize my page

Add "Site Configuration" page and refactor "My" page
Expand store with site, menu, and page configurations.

#VERCEL_SKIP

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
v0
2026-01-14 07:57:58 +00:00
parent b487855d44
commit 7e1e2e7115
5 changed files with 555 additions and 117 deletions

View File

@@ -0,0 +1,14 @@
export default function Loading() {
return (
<div className="p-8 max-w-4xl mx-auto">
<div className="animate-pulse">
<div className="h-8 bg-gray-700 rounded w-1/4 mb-8" />
<div className="space-y-6">
<div className="h-64 bg-[#0f2137] rounded-xl" />
<div className="h-48 bg-[#0f2137] rounded-xl" />
<div className="h-64 bg-[#0f2137] rounded-xl" />
</div>
</div>
</div>
)
}

367
app/admin/site/page.tsx Normal file
View File

@@ -0,0 +1,367 @@
"use client"
import { useState, useEffect } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { Input } from "@/components/ui/input"
import { Button } from "@/components/ui/button"
import { Switch } from "@/components/ui/switch"
import { useStore } from "@/lib/store"
import { Save, Globe, Menu, FileText, Palette } from "lucide-react"
export default function SiteConfigPage() {
const { settings, updateSettings } = useStore()
const [localSettings, setLocalSettings] = useState({
siteConfig: settings.siteConfig || {
siteName: "卡若日记",
siteTitle: "一场SOUL的创业实验场",
siteDescription: "来自Soul派对房的真实商业故事",
logo: "/logo.png",
favicon: "/favicon.ico",
primaryColor: "#00CED1",
},
menuConfig: settings.menuConfig || {
home: { enabled: true, label: "首页" },
chapters: { enabled: true, label: "目录" },
match: { enabled: true, label: "匹配" },
my: { enabled: true, label: "我的" },
},
pageConfig: settings.pageConfig || {
homeTitle: "一场SOUL的创业实验场",
homeSubtitle: "来自Soul派对房的真实商业故事",
chaptersTitle: "我要看",
matchTitle: "语音匹配",
myTitle: "我的",
aboutTitle: "关于作者",
},
})
const [saved, setSaved] = useState(false)
useEffect(() => {
if (settings.siteConfig) {
setLocalSettings({
siteConfig: settings.siteConfig,
menuConfig: settings.menuConfig,
pageConfig: settings.pageConfig,
})
}
}, [settings])
const handleSave = () => {
updateSettings(localSettings)
setSaved(true)
setTimeout(() => setSaved(false), 2000)
}
return (
<div className="p-8 max-w-4xl mx-auto">
<div className="flex justify-between items-center mb-8">
<div>
<h2 className="text-2xl font-bold text-white"></h2>
<p className="text-gray-400 mt-1"></p>
</div>
<Button
onClick={handleSave}
className={`${saved ? "bg-green-500" : "bg-[#00CED1]"} hover:bg-[#20B2AA] text-white transition-colors`}
>
<Save className="w-4 h-4 mr-2" />
{saved ? "已保存" : "保存设置"}
</Button>
</div>
<div className="space-y-6">
{/* 网站基础信息 */}
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<Globe className="w-5 h-5 text-[#00CED1]" />
</CardTitle>
<CardDescription className="text-gray-400"></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="site-name" className="text-gray-300">
</Label>
<Input
id="site-name"
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.siteConfig.siteName}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
siteConfig: { ...prev.siteConfig, siteName: e.target.value },
}))
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="site-title" className="text-gray-300">
</Label>
<Input
id="site-title"
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.siteConfig.siteTitle}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
siteConfig: { ...prev.siteConfig, siteTitle: e.target.value },
}))
}
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="site-desc" className="text-gray-300">
</Label>
<Input
id="site-desc"
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.siteConfig.siteDescription}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
siteConfig: { ...prev.siteConfig, siteDescription: e.target.value },
}))
}
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="logo" className="text-gray-300">
Logo地址
</Label>
<Input
id="logo"
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.siteConfig.logo}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
siteConfig: { ...prev.siteConfig, logo: e.target.value },
}))
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="favicon" className="text-gray-300">
Favicon地址
</Label>
<Input
id="favicon"
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.siteConfig.favicon}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
siteConfig: { ...prev.siteConfig, favicon: e.target.value },
}))
}
/>
</div>
</div>
</CardContent>
</Card>
{/* 主题颜色 */}
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<Palette className="w-5 h-5 text-[#00CED1]" />
</CardTitle>
<CardDescription className="text-gray-400"></CardDescription>
</CardHeader>
<CardContent>
<div className="flex items-center gap-4">
<div className="space-y-2 flex-1">
<Label htmlFor="primary-color" className="text-gray-300">
</Label>
<div className="flex items-center gap-3">
<Input
id="primary-color"
type="color"
className="w-16 h-10 bg-[#0a1628] border-gray-700 cursor-pointer"
value={localSettings.siteConfig.primaryColor}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
siteConfig: { ...prev.siteConfig, primaryColor: e.target.value },
}))
}
/>
<Input
className="bg-[#0a1628] border-gray-700 text-white flex-1"
value={localSettings.siteConfig.primaryColor}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
siteConfig: { ...prev.siteConfig, primaryColor: e.target.value },
}))
}
/>
</div>
</div>
<div
className="w-24 h-24 rounded-xl flex items-center justify-center text-white font-bold"
style={{ backgroundColor: localSettings.siteConfig.primaryColor }}
>
</div>
</div>
</CardContent>
</Card>
{/* 菜单配置 */}
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<Menu className="w-5 h-5 text-[#00CED1]" />
</CardTitle>
<CardDescription className="text-gray-400"></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{Object.entries(localSettings.menuConfig).map(([key, config]) => (
<div key={key} className="flex items-center justify-between p-4 bg-[#0a1628] rounded-lg">
<div className="flex items-center gap-4 flex-1">
<Switch
checked={config.enabled}
onCheckedChange={(checked) =>
setLocalSettings((prev) => ({
...prev,
menuConfig: {
...prev.menuConfig,
[key]: { ...config, enabled: checked },
},
}))
}
/>
<span className="text-gray-300 w-16 capitalize">{key}</span>
<Input
className="bg-[#0f2137] border-gray-700 text-white max-w-[200px]"
value={config.label}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
menuConfig: {
...prev.menuConfig,
[key]: { ...config, label: e.target.value },
},
}))
}
/>
</div>
<span className={`text-sm ${config.enabled ? "text-green-400" : "text-gray-500"}`}>
{config.enabled ? "显示" : "隐藏"}
</span>
</div>
))}
</CardContent>
</Card>
{/* 页面标题配置 */}
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<FileText className="w-5 h-5 text-[#00CED1]" />
</CardTitle>
<CardDescription className="text-gray-400"></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label className="text-gray-300"></Label>
<Input
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.pageConfig.homeTitle}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
pageConfig: { ...prev.pageConfig, homeTitle: e.target.value },
}))
}
/>
</div>
<div className="space-y-2">
<Label className="text-gray-300"></Label>
<Input
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.pageConfig.homeSubtitle}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
pageConfig: { ...prev.pageConfig, homeSubtitle: e.target.value },
}))
}
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label className="text-gray-300"></Label>
<Input
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.pageConfig.chaptersTitle}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
pageConfig: { ...prev.pageConfig, chaptersTitle: e.target.value },
}))
}
/>
</div>
<div className="space-y-2">
<Label className="text-gray-300"></Label>
<Input
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.pageConfig.matchTitle}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
pageConfig: { ...prev.pageConfig, matchTitle: e.target.value },
}))
}
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label className="text-gray-300"></Label>
<Input
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.pageConfig.myTitle}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
pageConfig: { ...prev.pageConfig, myTitle: e.target.value },
}))
}
/>
</div>
<div className="space-y-2">
<Label className="text-gray-300"></Label>
<Input
className="bg-[#0a1628] border-gray-700 text-white"
value={localSettings.pageConfig.aboutTitle}
onChange={(e) =>
setLocalSettings((prev) => ({
...prev,
pageConfig: { ...prev.pageConfig, aboutTitle: e.target.value },
}))
}
/>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
)
}