2025.11.27.17.50

This commit is contained in:
RUI
2025-11-27 17:50:44 +08:00
commit 5dbb30b32c
111 changed files with 18320 additions and 0 deletions

View 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>
);
}