注册支持用户名/手机号/邮箱 + 镜像站部署脚本 + AI 助手 Tool Calling 重构
- Prisma User 模型新增 username 字段(唯一索引) - 注册先查重复再创建,返回友好中文提示(非 500) - 登录支持用户名/手机号/邮箱三种方式 - 前端注册表单增加用户名输入框,预校验 2-20 位格式 - 新增 scripts/deploy.sh:一键构建并部署主站+镜像站+重启后端+重载 Nginx - 镜像站 www.yuzhiran.com.cn Nginx 配置与主站同步 - AI 助手架构升级:用户端/管理后台均采用完整 Tool Calling 架构 - 新增 UserAiAssistantService(18 工具)+ AiAssistantController - admin 助手新增 search + mark-all-notifications-read 工具 - 修复注册 500 错误:catch Prisma P2002 → BadRequestException - Baidu Analytics Script 注入 root layout
This commit is contained in:
Regular → Executable
+58
-6
@@ -4,12 +4,43 @@ import { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useT } from '@/i18n';
|
||||
import { API_BASE } from '@/lib/config';
|
||||
import Image from 'next/image';
|
||||
|
||||
interface BeianInfo {
|
||||
icp: string
|
||||
gongan: string
|
||||
gonganLink: string
|
||||
showGongan: boolean
|
||||
}
|
||||
|
||||
function getBeianInfo(): BeianInfo {
|
||||
if (typeof window === 'undefined') {
|
||||
return { icp: '京ICP备2026007249号-1', gongan: '京公网安备11011502039545号', gonganLink: 'https://beian.mps.gov.cn/#/query/webSearch?code=11011502039545', showGongan: true }
|
||||
}
|
||||
const hostname = window.location.hostname
|
||||
if (hostname === 'yuzhiran.com' || hostname === 'www.yuzhiran.com') {
|
||||
return { icp: '京ICP备2026007249号-1', gongan: '京公网安备11011502039545号', gonganLink: 'https://beian.mps.gov.cn/#/query/webSearch?code=11011502039545', showGongan: true }
|
||||
}
|
||||
if (hostname === 'yuzhiran.com.cn' || hostname === 'www.yuzhiran.com.cn') {
|
||||
return { icp: '京ICP备2026007249号-2', gongan: '京公网安备11011502039622号', gonganLink: 'https://beian.mps.gov.cn/#/query/webSearch?code=11011502039622', showGongan: true }
|
||||
}
|
||||
return { icp: '京ICP备2026007249号-1', gongan: '京公网安备11011502039545号', gonganLink: 'https://beian.mps.gov.cn/#/query/webSearch?code=11011502039545', showGongan: true }
|
||||
}
|
||||
|
||||
const qrCodes = [
|
||||
{ src: '/images/yzr/yuzhiran.jpg', alt: '微信公众号', label: '微信公众号' },
|
||||
{ src: '/images/yzr/yuzhiran-tech.jpg', alt: '微信服务号', label: '微信服务号' },
|
||||
{ src: '/images/yzr/yuzhiran-yhl.jpg', alt: '小程序', label: '小程序' },
|
||||
{ src: '/images/yzr/kefu.png', alt: '微信客服', label: '微信客服' },
|
||||
]
|
||||
|
||||
export function Footer() {
|
||||
const t = useT();
|
||||
const [config, setConfig] = useState<Record<string, string>>({});
|
||||
const [beian, setBeian] = useState<BeianInfo>({ icp: '', gongan: '', gonganLink: '', showGongan: false });
|
||||
|
||||
useEffect(() => {
|
||||
setBeian(getBeianInfo());
|
||||
fetch(`${API_BASE}/public/config`)
|
||||
.then(r => r.json()).then(data => setConfig(data || {}))
|
||||
.catch(() => {});
|
||||
@@ -18,10 +49,25 @@ export function Footer() {
|
||||
return (
|
||||
<footer className="border-t border-border bg-muted/30">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 md:py-16">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
<div className="col-span-2 md:col-span-1">
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-8">
|
||||
<div className="col-span-2 md:col-span-2">
|
||||
<h3 className="text-lg font-bold bg-gradient-to-r from-brand-600 to-brand-400 bg-clip-text text-transparent mb-4">宇之然 AI</h3>
|
||||
<p className="text-sm text-muted-foreground">{t.footer.tagline}</p>
|
||||
<p className="text-sm text-muted-foreground mb-4">{t.footer.tagline}</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{qrCodes.map((qr) => (
|
||||
<div key={qr.alt} className="text-center group">
|
||||
<Image
|
||||
src={qr.src}
|
||||
alt={qr.alt}
|
||||
width={72}
|
||||
height={72}
|
||||
className="rounded-lg bg-white transition-transform duration-200 group-hover:scale-150 group-hover:z-10 group-hover:shadow-xl cursor-pointer"
|
||||
unoptimized
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground mt-1">{qr.label}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold mb-3">{t.footer.explore}</h4>
|
||||
@@ -53,11 +99,17 @@ export function Footer() {
|
||||
<div className="mt-10 pt-8 border-t border-border">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-2 text-xs text-muted-foreground">
|
||||
<p>{t.footer.copyright.replace('{year}', String(new Date().getFullYear()))}</p>
|
||||
<p>
|
||||
<div className="flex items-center gap-3">
|
||||
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer" className="hover:text-foreground transition-colors">
|
||||
{config.icp_number ? `ICP 备案号:${config.icp_number}` : 'ICP 备案号:京ICP备XXXXXXXX号'}
|
||||
{beian.icp}
|
||||
</a>
|
||||
</p>
|
||||
{beian.showGongan && (
|
||||
<a href={beian.gonganLink} target="_blank" rel="noreferrer" className="hover:text-foreground transition-colors inline-flex items-center gap-1">
|
||||
<Image src="/images/beian/gongan-beian.png" alt="公安备案" width={16} height={16} unoptimized />
|
||||
{beian.gongan}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user