2025.11.27.17.50
This commit is contained in:
377
src/pages/admin/settings/index.tsx
Normal file
377
src/pages/admin/settings/index.tsx
Normal file
@@ -0,0 +1,377 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import AdminLayout from '@/components/admin/AdminLayout';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Loader2, Save, Trash2, Plus, Bot } from 'lucide-react';
|
||||
import { useForm, useFieldArray, Controller } from 'react-hook-form';
|
||||
|
||||
interface SystemSettingsForm {
|
||||
站点设置: {
|
||||
网站标题?: string;
|
||||
网站副标题?: string;
|
||||
Logo地址?: string;
|
||||
Favicon?: string;
|
||||
备案号?: string;
|
||||
全局SEO关键词?: string;
|
||||
全局SEO描述?: string;
|
||||
底部版权信息?: string;
|
||||
第三方统计代码?: string;
|
||||
};
|
||||
支付宝设置: {
|
||||
AppID?: string;
|
||||
回调URL?: string;
|
||||
网关地址?: string;
|
||||
公钥?: string;
|
||||
应用公钥?: string;
|
||||
应用私钥?: string;
|
||||
};
|
||||
微信支付设置: {
|
||||
WX_APPID?: string;
|
||||
WX_MCHID?: string;
|
||||
WX_SERIAL_NO?: string;
|
||||
WX_NOTIFY_URL?: string;
|
||||
WX_API_V3_KEY?: string;
|
||||
WX_PRIVATE_KEY?: string;
|
||||
};
|
||||
阿里云短信设置: {
|
||||
AccessKeyID?: string;
|
||||
AccessKeySecret?: string;
|
||||
aliSignName?: string;
|
||||
aliTemplateCode?: string;
|
||||
};
|
||||
邮箱设置: {
|
||||
MY_MAIL?: string;
|
||||
MY_MAIL_PASS?: string;
|
||||
};
|
||||
AI配置列表: {
|
||||
名称: string;
|
||||
接口地址: string;
|
||||
API密钥?: string;
|
||||
模型: string;
|
||||
系统提示词?: string;
|
||||
流式传输?: boolean;
|
||||
是否启用: boolean;
|
||||
}[];
|
||||
}
|
||||
|
||||
export default function SystemSettings() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const { register, control, handleSubmit, reset } = useForm<SystemSettingsForm>({
|
||||
defaultValues: {
|
||||
站点设置: {},
|
||||
支付宝设置: {},
|
||||
微信支付设置: {},
|
||||
阿里云短信设置: {},
|
||||
邮箱设置: {},
|
||||
AI配置列表: []
|
||||
}
|
||||
});
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
control,
|
||||
name: "AI配置列表"
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
fetchSettings();
|
||||
}, []);
|
||||
|
||||
const fetchSettings = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/admin/settings');
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
// 重置表单数据,注意处理嵌套对象
|
||||
reset(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch settings', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async (data: any) => {
|
||||
setSaving(true);
|
||||
try {
|
||||
const res = await fetch('/api/admin/settings', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
alert('设置已保存');
|
||||
fetchSettings();
|
||||
} else {
|
||||
alert('保存失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to save settings', error);
|
||||
alert('保存出错');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<AdminLayout>
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Loader2 className="h-8 w-8 animate-spin" />
|
||||
</div>
|
||||
</AdminLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AdminLayout>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-3xl font-bold tracking-tight">系统设置</h2>
|
||||
<p className="text-muted-foreground">
|
||||
配置网站的基本信息、支付接口和第三方服务。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Tabs defaultValue="basic" className="space-y-6">
|
||||
<TabsList className="grid w-full grid-cols-4 lg:w-[500px]">
|
||||
<TabsTrigger value="basic">基础设置</TabsTrigger>
|
||||
<TabsTrigger value="payment">支付设置</TabsTrigger>
|
||||
<TabsTrigger value="service">服务设置</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="basic">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>网站基础信息</CardTitle>
|
||||
<CardDescription>
|
||||
设置网站的标题、描述和联系方式。
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="siteName">网站标题</Label>
|
||||
<Input id="siteName" {...register('站点设置.网站标题')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="siteSubTitle">网站副标题</Label>
|
||||
<Input id="siteSubTitle" {...register('站点设置.网站副标题')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="siteLogo">Logo地址</Label>
|
||||
<Input id="siteLogo" {...register('站点设置.Logo地址')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="siteFavicon">Favicon</Label>
|
||||
<Input id="siteFavicon" {...register('站点设置.Favicon')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="icp">备案号</Label>
|
||||
<Input id="icp" {...register('站点设置.备案号')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="footerCopyright">底部版权信息</Label>
|
||||
<Input id="footerCopyright" {...register('站点设置.底部版权信息')} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="seoKeywords">全局SEO关键词</Label>
|
||||
<Input id="seoKeywords" {...register('站点设置.全局SEO关键词')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="seoDesc">全局SEO描述</Label>
|
||||
<Textarea id="seoDesc" rows={3} {...register('站点设置.全局SEO描述')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="analytics">第三方统计代码</Label>
|
||||
<Textarea id="analytics" rows={4} className="font-mono text-xs" {...register('站点设置.第三方统计代码')} />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="payment">
|
||||
<div className="grid gap-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>支付宝配置</CardTitle>
|
||||
<CardDescription>
|
||||
配置支付宝支付接口参数。
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="alipayAppId">AppID</Label>
|
||||
<Input id="alipayAppId" {...register('支付宝设置.AppID')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="alipayNotifyUrl">回调URL</Label>
|
||||
<Input id="alipayNotifyUrl" {...register('支付宝设置.回调URL')} />
|
||||
</div>
|
||||
<div className="space-y-2 md:col-span-2">
|
||||
<Label htmlFor="alipayGateway">网关地址</Label>
|
||||
<Input id="alipayGateway" {...register('支付宝设置.网关地址')} />
|
||||
</div>
|
||||
<div className="space-y-2 md:col-span-2">
|
||||
<Label htmlFor="alipayPublicKey">支付宝公钥</Label>
|
||||
<Textarea id="alipayPublicKey" className="font-mono text-xs" rows={3} {...register('支付宝设置.公钥')} />
|
||||
</div>
|
||||
<div className="space-y-2 md:col-span-2">
|
||||
<Label htmlFor="alipayAppPublicKey">应用公钥 (ALIPAY_APP_PUBLIC_KEY)</Label>
|
||||
<Textarea id="alipayAppPublicKey" className="font-mono text-xs" rows={3} {...register('支付宝设置.应用公钥')} />
|
||||
</div>
|
||||
<div className="space-y-2 md:col-span-2">
|
||||
<Label htmlFor="alipayPrivateKey">应用私钥 (核心机密)</Label>
|
||||
<Textarea
|
||||
id="alipayPrivateKey"
|
||||
className="font-mono text-xs"
|
||||
rows={5}
|
||||
{...register('支付宝设置.应用私钥')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>微信支付配置</CardTitle>
|
||||
<CardDescription>
|
||||
配置微信支付接口参数。
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="wechatAppId">AppID (WX_APPID)</Label>
|
||||
<Input id="wechatAppId" {...register('微信支付设置.WX_APPID')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="wechatMchId">商户号 (WX_MCHID)</Label>
|
||||
<Input id="wechatMchId" {...register('微信支付设置.WX_MCHID')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="wechatSerialNo">证书序列号 (WX_SERIAL_NO)</Label>
|
||||
<Input id="wechatSerialNo" {...register('微信支付设置.WX_SERIAL_NO')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="wechatNotifyUrl">回调地址 (WX_NOTIFY_URL)</Label>
|
||||
<Input id="wechatNotifyUrl" {...register('微信支付设置.WX_NOTIFY_URL')} />
|
||||
</div>
|
||||
<div className="space-y-2 md:col-span-2">
|
||||
<Label htmlFor="wechatKey">API V3 密钥 (WX_API_V3_KEY)</Label>
|
||||
<Input
|
||||
id="wechatKey"
|
||||
className="font-mono"
|
||||
{...register('微信支付设置.WX_API_V3_KEY')}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2 md:col-span-2">
|
||||
<Label htmlFor="wechatPrivateKey">商户私钥 (WX_PRIVATE_KEY)</Label>
|
||||
<Textarea
|
||||
id="wechatPrivateKey"
|
||||
className="font-mono text-xs"
|
||||
rows={5}
|
||||
{...register('微信支付设置.WX_PRIVATE_KEY')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="service">
|
||||
<div className="grid gap-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>邮件服务 (SMTP)</CardTitle>
|
||||
<CardDescription>
|
||||
配置系统邮件发送服务。
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="smtpUser">邮箱地址 (MY_MAIL)</Label>
|
||||
<Input id="smtpUser" {...register('邮箱设置.MY_MAIL')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="smtpPass">授权码 (MY_MAIL_PASS)</Label>
|
||||
<Input
|
||||
id="smtpPass"
|
||||
type="password"
|
||||
{...register('邮箱设置.MY_MAIL_PASS')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>阿里云短信</CardTitle>
|
||||
<CardDescription>
|
||||
配置阿里云短信服务。
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="aliyunKey">AccessKey ID</Label>
|
||||
<Input id="aliyunKey" {...register('阿里云短信设置.AccessKeyID')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="aliyunSecret">AccessKey Secret</Label>
|
||||
<Input
|
||||
id="aliyunSecret"
|
||||
type="password"
|
||||
{...register('阿里云短信设置.AccessKeySecret')}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="aliyunSign">签名名称</Label>
|
||||
<Input id="aliyunSign" {...register('阿里云短信设置.aliSignName')} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="aliyunTemplate">模板代码</Label>
|
||||
<Input id="aliyunTemplate" {...register('阿里云短信设置.aliTemplateCode')} />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<div className="mt-6 flex justify-end">
|
||||
<Button type="submit" disabled={saving}>
|
||||
{saving ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
保存中...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Save className="mr-2 h-4 w-4" />
|
||||
保存设置
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</AdminLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user