fix: prevent controlled component errors
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>
This commit is contained in:
@@ -9,43 +9,49 @@ 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: 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: "关于作者",
|
||||
},
|
||||
siteConfig: { ...defaultSiteConfig },
|
||||
menuConfig: { ...defaultMenuConfig },
|
||||
pageConfig: { ...defaultPageConfig },
|
||||
})
|
||||
const [saved, setSaved] = useState(false)
|
||||
const [mounted, setMounted] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (settings.siteConfig) {
|
||||
setLocalSettings({
|
||||
siteConfig: settings.siteConfig,
|
||||
menuConfig: settings.menuConfig,
|
||||
pageConfig: settings.pageConfig,
|
||||
})
|
||||
}
|
||||
}, [settings])
|
||||
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)
|
||||
@@ -53,6 +59,17 @@ export default function SiteConfigPage() {
|
||||
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">
|
||||
@@ -88,7 +105,7 @@ export default function SiteConfigPage() {
|
||||
<Input
|
||||
id="site-name"
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.siteConfig.siteName}
|
||||
value={localSettings.siteConfig.siteName || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -104,7 +121,7 @@ export default function SiteConfigPage() {
|
||||
<Input
|
||||
id="site-title"
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.siteConfig.siteTitle}
|
||||
value={localSettings.siteConfig.siteTitle || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -121,7 +138,7 @@ export default function SiteConfigPage() {
|
||||
<Input
|
||||
id="site-desc"
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.siteConfig.siteDescription}
|
||||
value={localSettings.siteConfig.siteDescription || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -138,7 +155,7 @@ export default function SiteConfigPage() {
|
||||
<Input
|
||||
id="logo"
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.siteConfig.logo}
|
||||
value={localSettings.siteConfig.logo || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -154,7 +171,7 @@ export default function SiteConfigPage() {
|
||||
<Input
|
||||
id="favicon"
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.siteConfig.favicon}
|
||||
value={localSettings.siteConfig.favicon || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -187,7 +204,7 @@ export default function SiteConfigPage() {
|
||||
id="primary-color"
|
||||
type="color"
|
||||
className="w-16 h-10 bg-[#0a1628] border-gray-700 cursor-pointer"
|
||||
value={localSettings.siteConfig.primaryColor}
|
||||
value={localSettings.siteConfig.primaryColor || "#00CED1"}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -197,7 +214,7 @@ export default function SiteConfigPage() {
|
||||
/>
|
||||
<Input
|
||||
className="bg-[#0a1628] border-gray-700 text-white flex-1"
|
||||
value={localSettings.siteConfig.primaryColor}
|
||||
value={localSettings.siteConfig.primaryColor || "#00CED1"}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -209,7 +226,7 @@ export default function SiteConfigPage() {
|
||||
</div>
|
||||
<div
|
||||
className="w-24 h-24 rounded-xl flex items-center justify-center text-white font-bold"
|
||||
style={{ backgroundColor: localSettings.siteConfig.primaryColor }}
|
||||
style={{ backgroundColor: localSettings.siteConfig.primaryColor || "#00CED1" }}
|
||||
>
|
||||
预览
|
||||
</div>
|
||||
@@ -231,7 +248,7 @@ export default function SiteConfigPage() {
|
||||
<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}
|
||||
checked={config?.enabled ?? true}
|
||||
onCheckedChange={(checked) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -245,7 +262,7 @@ export default function SiteConfigPage() {
|
||||
<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}
|
||||
value={config?.label || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -257,8 +274,8 @@ export default function SiteConfigPage() {
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<span className={`text-sm ${config.enabled ? "text-green-400" : "text-gray-500"}`}>
|
||||
{config.enabled ? "显示" : "隐藏"}
|
||||
<span className={`text-sm ${config?.enabled ? "text-green-400" : "text-gray-500"}`}>
|
||||
{config?.enabled ? "显示" : "隐藏"}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -280,7 +297,7 @@ export default function SiteConfigPage() {
|
||||
<Label className="text-gray-300">首页标题</Label>
|
||||
<Input
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.pageConfig.homeTitle}
|
||||
value={localSettings.pageConfig.homeTitle || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -293,7 +310,7 @@ export default function SiteConfigPage() {
|
||||
<Label className="text-gray-300">首页副标题</Label>
|
||||
<Input
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.pageConfig.homeSubtitle}
|
||||
value={localSettings.pageConfig.homeSubtitle || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -308,7 +325,7 @@ export default function SiteConfigPage() {
|
||||
<Label className="text-gray-300">目录页标题</Label>
|
||||
<Input
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.pageConfig.chaptersTitle}
|
||||
value={localSettings.pageConfig.chaptersTitle || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -321,7 +338,7 @@ export default function SiteConfigPage() {
|
||||
<Label className="text-gray-300">匹配页标题</Label>
|
||||
<Input
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.pageConfig.matchTitle}
|
||||
value={localSettings.pageConfig.matchTitle || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -336,7 +353,7 @@ export default function SiteConfigPage() {
|
||||
<Label className="text-gray-300">我的页标题</Label>
|
||||
<Input
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.pageConfig.myTitle}
|
||||
value={localSettings.pageConfig.myTitle || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
@@ -349,7 +366,7 @@ export default function SiteConfigPage() {
|
||||
<Label className="text-gray-300">关于作者标题</Label>
|
||||
<Input
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
value={localSettings.pageConfig.aboutTitle}
|
||||
value={localSettings.pageConfig.aboutTitle || ""}
|
||||
onChange={(e) =>
|
||||
setLocalSettings((prev) => ({
|
||||
...prev,
|
||||
|
||||
Reference in New Issue
Block a user