修改logo为上传
This commit is contained in:
Vendored
+86
-86
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -28,7 +28,7 @@
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-J332wOtZ.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-CXwDXEM6.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -149,6 +149,20 @@ export async function uploadPdf(file: File, configKey: string): Promise<{ url: s
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function uploadLogo(file: File): Promise<{ url: string }> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const token = localStorage.getItem('auth_token');
|
||||
const baseUrl = (import.meta as any).env?.VITE_API_URL || 'http://localhost:8000/api';
|
||||
const res = await fetch(`${baseUrl}/admin/upload-logo`, {
|
||||
method: 'POST',
|
||||
headers: token ? { 'Authorization': `Bearer ${token}` } : {},
|
||||
body: formData,
|
||||
});
|
||||
if (!res.ok) throw new Error('上传失败');
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function getCreditRecords(filters?: {
|
||||
user_id?: string;
|
||||
user_name?: string;
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
import {
|
||||
SettingOutlined, SaveOutlined, UploadOutlined, FilePdfOutlined, EyeOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { getSystemConfigs, updateSystemConfig, uploadPdf } from '../api';
|
||||
import { getSystemConfigs, updateSystemConfig, uploadPdf, uploadLogo } from '../api';
|
||||
import type { SystemConfig } from '../types';
|
||||
|
||||
const AdminSettings: React.FC = () => {
|
||||
@@ -14,6 +14,7 @@ const AdminSettings: React.FC = () => {
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [uploading, setUploading] = useState('');
|
||||
const [form] = Form.useForm();
|
||||
const [logoPreview, setLogoPreview] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
load();
|
||||
@@ -65,6 +66,23 @@ const AdminSettings: React.FC = () => {
|
||||
return false; // prevent default upload
|
||||
};
|
||||
|
||||
const handleLogoUpload = async (file: File) => {
|
||||
setUploading('site_logo');
|
||||
try {
|
||||
const res = await uploadLogo(file);
|
||||
// Update local state
|
||||
setConfigs(prev => prev.map(c => c.key === 'site_logo' ? { ...c, value: res.url } : c));
|
||||
form.setFieldsValue({ site_logo: res.url });
|
||||
setLogoPreview(res.url);
|
||||
message.success('Logo上传成功');
|
||||
} catch {
|
||||
message.error('上传失败');
|
||||
} finally {
|
||||
setUploading('');
|
||||
}
|
||||
return false; // prevent default upload
|
||||
};
|
||||
|
||||
const groupedConfigs: Record<string, SystemConfig[]> = {
|
||||
'站点信息': configs.filter(c => c.key.startsWith('site_')),
|
||||
'协议配置': configs.filter(c => c.key === 'user_agreement_url' || c.key === 'privacy_policy_url'),
|
||||
@@ -89,6 +107,9 @@ const AdminSettings: React.FC = () => {
|
||||
};
|
||||
|
||||
const getFieldComponent = (config: SystemConfig) => {
|
||||
if (config.key === 'site_logo') {
|
||||
return <LogoUploadField config={config} />;
|
||||
}
|
||||
if (config.key === 'seo_description') {
|
||||
return <Input.TextArea rows={3} placeholder={config.description} size="large" />;
|
||||
}
|
||||
@@ -111,6 +132,71 @@ const AdminSettings: React.FC = () => {
|
||||
return <Input placeholder={config.description} size="large" />;
|
||||
};
|
||||
|
||||
const LogoUploadField: React.FC<{ config: SystemConfig }> = ({ config }) => {
|
||||
const hasLogo = config.value && config.value.startsWith('/uploads/');
|
||||
const baseUrl = (import.meta as any).env?.VITE_API_URL || 'http://localhost:8000/api';
|
||||
const logoUrl = hasLogo ? `${baseUrl.replace('/api', '')}${config.value}` : '';
|
||||
|
||||
const handleRemove = () => {
|
||||
setConfigs(prev => prev.map(c => c.key === 'site_logo' ? { ...c, value: '' } : c));
|
||||
form.setFieldsValue({ site_logo: '' });
|
||||
message.success('Logo已移除');
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: '16px', borderRadius: 10,
|
||||
border: '1px solid #f0f0f5', background: '#fafbfc',
|
||||
}}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
|
||||
<Space>
|
||||
<UploadOutlined style={{ color: '#6366f1', fontSize: 18 }} />
|
||||
<Typography.Text strong>网站Logo</Typography.Text>
|
||||
</Space>
|
||||
<Space>
|
||||
{hasLogo && (
|
||||
<Button size="small" danger onClick={handleRemove}>
|
||||
移除
|
||||
</Button>
|
||||
)}
|
||||
<Upload
|
||||
accept="image/*"
|
||||
showUploadList={false}
|
||||
beforeUpload={handleLogoUpload}
|
||||
>
|
||||
<Button size="small" type="primary" icon={<UploadOutlined />} loading={uploading === 'site_logo'}>
|
||||
{hasLogo ? '重新上传' : '上传Logo'}
|
||||
</Button>
|
||||
</Upload>
|
||||
</Space>
|
||||
</div>
|
||||
{hasLogo ? (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt="Logo预览"
|
||||
style={{
|
||||
maxWidth: 200,
|
||||
maxHeight: 80,
|
||||
objectFit: 'contain',
|
||||
border: '1px solid #e2e8f0',
|
||||
borderRadius: 8,
|
||||
padding: 8,
|
||||
}}
|
||||
/>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12, display: 'block', marginTop: 8 }}>
|
||||
建议尺寸:200x40px,支持 PNG、JPG 格式
|
||||
</Typography.Text>
|
||||
</div>
|
||||
) : (
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||
尚未上传Logo,将使用系统默认Logo
|
||||
</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const PdfUploadField: React.FC<{ config: SystemConfig }> = ({ config }) => {
|
||||
const label = config.key === 'user_agreement_url' ? '用户协议' : '隐私政策';
|
||||
const hasFile = config.value && config.value.startsWith('/uploads/');
|
||||
|
||||
@@ -1577,6 +1577,104 @@ async def admin_generate_video(
|
||||
return {"message": "ok", "record_id": record_id}
|
||||
|
||||
|
||||
# ── File Uploads ─────────────────────────────────────────
|
||||
|
||||
import os
|
||||
from fastapi import File, UploadFile
|
||||
|
||||
|
||||
@router.post("/upload-pdf")
|
||||
async def upload_pdf(
|
||||
file: UploadFile = File(...),
|
||||
config_key: str | None = None,
|
||||
admin: User = Depends(get_admin_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Upload a PDF file and save URL to system config."""
|
||||
from app.config import settings
|
||||
from app.utils.id_gen import generate_id
|
||||
|
||||
if not file.filename:
|
||||
raise HTTPException(status_code=400, detail="请选择文件")
|
||||
|
||||
if not file.filename.lower().endswith(".pdf"):
|
||||
raise HTTPException(status_code=400, detail="仅支持PDF格式")
|
||||
|
||||
content = await file.read()
|
||||
if len(content) > 10 * 1024 * 1024:
|
||||
raise HTTPException(status_code=400, detail="文件大小不能超过10MB")
|
||||
|
||||
safe_name = f"pdf_{generate_id()}.pdf"
|
||||
file_path = os.path.join(settings.UPLOAD_LOCAL_PATH, safe_name)
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(content)
|
||||
|
||||
url = f"/uploads/{safe_name}"
|
||||
|
||||
if config_key:
|
||||
result = await db.execute(
|
||||
select(SystemConfig).where(SystemConfig.key == config_key).limit(1)
|
||||
)
|
||||
config = result.scalar_one_or_none()
|
||||
if config:
|
||||
config.value = url
|
||||
else:
|
||||
db.add(SystemConfig(
|
||||
id=generate_id(),
|
||||
key=config_key,
|
||||
value=url,
|
||||
))
|
||||
await db.commit()
|
||||
|
||||
return {"url": url}
|
||||
|
||||
|
||||
@router.post("/upload-logo")
|
||||
async def upload_logo(
|
||||
file: UploadFile = File(...),
|
||||
admin: User = Depends(get_admin_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Upload a Logo image file and save URL to system config."""
|
||||
from app.config import settings
|
||||
from app.utils.id_gen import generate_id
|
||||
|
||||
if not file.filename:
|
||||
raise HTTPException(status_code=400, detail="请选择文件")
|
||||
|
||||
allowed_extensions = ('.png', '.jpg', '.jpeg', '.gif', '.webp')
|
||||
if not file.filename.lower().endswith(allowed_extensions):
|
||||
raise HTTPException(status_code=400, detail="仅支持 PNG、JPG、GIF、WebP 格式图片")
|
||||
|
||||
content = await file.read()
|
||||
if len(content) > 2 * 1024 * 1024:
|
||||
raise HTTPException(status_code=400, detail="文件大小不能超过2MB")
|
||||
|
||||
safe_name = "site_logo.png"
|
||||
file_path = os.path.join(settings.UPLOAD_LOCAL_PATH, safe_name)
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(content)
|
||||
|
||||
url = f"/uploads/{safe_name}"
|
||||
|
||||
result = await db.execute(
|
||||
select(SystemConfig).where(SystemConfig.key == "site_logo").limit(1)
|
||||
)
|
||||
config = result.scalar_one_or_none()
|
||||
if config:
|
||||
config.value = url
|
||||
else:
|
||||
db.add(SystemConfig(
|
||||
id="cfg_site_logo",
|
||||
key="site_logo",
|
||||
value=url,
|
||||
description="网站Logo图片",
|
||||
))
|
||||
await db.commit()
|
||||
|
||||
return {"url": url}
|
||||
|
||||
|
||||
# ── Payment Stats ────────────────────────────────────────
|
||||
|
||||
|
||||
|
||||
@@ -777,48 +777,6 @@ def create_app() -> FastAPI:
|
||||
except Exception as e:
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
@application.post("/api/admin/upload-pdf")
|
||||
async def upload_pdf(
|
||||
file: UploadFile = File(...),
|
||||
config_key: str = Form(...),
|
||||
):
|
||||
"""Upload a PDF file and save URL to system config."""
|
||||
from app.dependencies import get_db
|
||||
from app.models.system_config import SystemConfig
|
||||
from app.models.base import async_session
|
||||
from sqlalchemy import select
|
||||
|
||||
if not file.filename or not file.filename.endswith('.pdf'):
|
||||
raise HTTPException(status_code=400, detail="仅支持PDF文件")
|
||||
|
||||
# Save file
|
||||
safe_name = f"{config_key}.pdf"
|
||||
file_path = os.path.join(settings.UPLOAD_LOCAL_PATH, safe_name)
|
||||
content = await file.read()
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(content)
|
||||
|
||||
url = f"/uploads/{safe_name}"
|
||||
|
||||
# Update system config
|
||||
async with async_session() as db:
|
||||
result = await db.execute(
|
||||
select(SystemConfig).where(SystemConfig.key == config_key).limit(1)
|
||||
)
|
||||
config = result.scalar_one_or_none()
|
||||
if config:
|
||||
config.value = url
|
||||
else:
|
||||
db.add(SystemConfig(
|
||||
id=f"cfg_{config_key}",
|
||||
key=config_key,
|
||||
value=url,
|
||||
description="用户协议" if "agreement" in config_key else "隐私政策",
|
||||
))
|
||||
await db.commit()
|
||||
|
||||
return {"url": url}
|
||||
|
||||
@application.get("/internal/", response_class=HTMLResponse)
|
||||
async def index():
|
||||
return """<!DOCTYPE html>
|
||||
|
||||
Reference in New Issue
Block a user