Ensure all input values have default values and use empty string as fallback. #VERCEL_SKIP Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
385 lines
15 KiB
TypeScript
385 lines
15 KiB
TypeScript
"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"
|
|
|
|
const defaultSiteConfig = {
|
|
siteName: "卡若日记",
|
|
siteTitle: "一场SOUL的创业实验场",
|
|
siteDescription: "来自Soul派对房的真实商业故事",
|
|
logo: "/logo.png",
|
|
favicon: "/favicon.ico",
|
|
primaryColor: "#00CED1",
|
|
}
|
|
|
|
const defaultMenuConfig = {
|
|
home: { enabled: true, label: "首页" },
|
|
chapters: { enabled: true, label: "目录" },
|
|
match: { enabled: true, label: "匹配" },
|
|
my: { enabled: true, label: "我的" },
|
|
}
|
|
|
|
const defaultPageConfig = {
|
|
homeTitle: "一场SOUL的创业实验场",
|
|
homeSubtitle: "来自Soul派对房的真实商业故事",
|
|
chaptersTitle: "我要看",
|
|
matchTitle: "语音匹配",
|
|
myTitle: "我的",
|
|
aboutTitle: "关于作者",
|
|
}
|
|
|
|
export default function SiteConfigPage() {
|
|
const { settings, updateSettings } = useStore()
|
|
const [localSettings, setLocalSettings] = useState({
|
|
siteConfig: { ...defaultSiteConfig },
|
|
menuConfig: { ...defaultMenuConfig },
|
|
pageConfig: { ...defaultPageConfig },
|
|
})
|
|
const [saved, setSaved] = useState(false)
|
|
const [mounted, setMounted] = useState(false)
|
|
|
|
useEffect(() => {
|
|
setMounted(true)
|
|
setLocalSettings({
|
|
siteConfig: { ...defaultSiteConfig, ...settings.siteConfig },
|
|
menuConfig: { ...defaultMenuConfig, ...settings.menuConfig },
|
|
pageConfig: { ...defaultPageConfig, ...settings.pageConfig },
|
|
})
|
|
}, [settings.siteConfig, settings.menuConfig, settings.pageConfig])
|
|
|
|
const handleSave = () => {
|
|
updateSettings(localSettings)
|
|
setSaved(true)
|
|
setTimeout(() => setSaved(false), 2000)
|
|
}
|
|
|
|
if (!mounted) {
|
|
return (
|
|
<div className="p-8 max-w-4xl mx-auto">
|
|
<div className="animate-pulse space-y-6">
|
|
<div className="h-8 bg-gray-700 rounded w-1/3"></div>
|
|
<div className="h-64 bg-gray-700 rounded"></div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
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 || "#00CED1"}
|
|
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 || "#00CED1"}
|
|
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 || "#00CED1" }}
|
|
>
|
|
预览
|
|
</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 ?? true}
|
|
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>
|
|
)
|
|
}
|