v1.2.30 - 寻号行情功能基本完善稳定版
主要更新: - 资讯页面配号逻辑修复(match_pattern重复去掉J0前缀问题) - 管理面板用户角色显示优化(信息员正确显示) - Info页面优化(显示所有帖子、用户名、折叠展开效果) - B前端API代理配置修改(指向C后端) - 修复前端部署时Logo丢失问题(使用Vite public目录) 涉及文件: - frontend/src/pages/News.jsx (配号逻辑、折叠效果) - frontend/src/pages/Admin.jsx (角色显示) - frontend/src/pages/Info.jsx (用户名显示、获取所有帖子) - frontend/public/images/ (Logo文件) - backend/app/routers/information.py (配号逻辑修复) - config/VERSION (版本更新)
This commit is contained in:
parent
188458d2bc
commit
4d3b467a5a
|
|
@ -193,7 +193,8 @@ def match_pattern(col_number: str, pattern: str) -> bool:
|
|||
# F = 非23457
|
||||
# G = 非123457
|
||||
|
||||
col_num = col_number[2:] if col_number.startswith('J0') else col_number # 去掉J0前缀
|
||||
# 注意:col_number已经是去掉J0前缀后的8位号码,不需要再处理
|
||||
col_num = col_number
|
||||
|
||||
for i, p in enumerate(pattern):
|
||||
if i >= len(col_num):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,128 @@
|
|||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy import Table, MetaData
|
||||
from sqlalchemy.orm import Session
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
from datetime import datetime, date
|
||||
from app.core.database import get_db, engine
|
||||
from app.models.models import User
|
||||
from app.routers.auth import get_current_user
|
||||
|
||||
router = APIRouter(prefix="/api/news", tags=["资讯"])
|
||||
metadata = MetaData()
|
||||
|
||||
# 分类表
|
||||
categories_table = Table('news_categories', metadata, autoload_with=engine)
|
||||
news_table = Table('news', metadata, autoload_with=engine)
|
||||
user_posts_table = Table('user_posts', metadata, autoload_with=engine)
|
||||
users_table = Table('users', metadata, autoload_with=engine)
|
||||
deals_table = Table('deals', metadata, autoload_with=engine)
|
||||
notifications_table = Table('notifications', metadata, autoload_with=engine)
|
||||
|
||||
# ============ 获取分类 ============
|
||||
@router.get("/categories")
|
||||
def get_categories(db: Session = Depends(get_db)):
|
||||
results = db.query(categories_table).order_by(categories_table.c.sort_order).all()
|
||||
return [dict(r._mapping) for r in results]
|
||||
|
||||
# ============ 获取资讯 ============
|
||||
@router.get("")
|
||||
def get_news(
|
||||
category_id: Optional[int] = None,
|
||||
page: int = 1,
|
||||
limit: int = 20,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
query = db.query(news_table)
|
||||
if category_id:
|
||||
query = query.filter(news_table.c.category_id == category_id)
|
||||
offset = (page - 1) * limit
|
||||
results = query.order_by(news_table.c.created_at.desc()).offset(offset).limit(limit).all()
|
||||
return [dict(r._mapping) for r in results]
|
||||
|
||||
# ============ 获取用户发布 ============
|
||||
@router.get("/posts")
|
||||
def get_posts(
|
||||
post_type: Optional[str] = None,
|
||||
status: str = "active",
|
||||
page: int = 1,
|
||||
limit: int = 20,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
query = db.query(user_posts_table).filter(user_posts_table.c.status == status)
|
||||
if post_type:
|
||||
query = query.filter(user_posts_table.c.post_type == post_type)
|
||||
offset = (page - 1) * limit
|
||||
results = query.order_by(user_posts_table.c.created_at.desc()).offset(offset).limit(limit).all()
|
||||
return [dict(r._mapping) for r in results]
|
||||
|
||||
# ============ 创建发布 ============
|
||||
class PostCreate(BaseModel):
|
||||
post_type: str
|
||||
title: str
|
||||
content: Optional[str] = None
|
||||
zodiac_type: Optional[str] = None
|
||||
packaging: Optional[str] = None
|
||||
|
||||
@router.post("/posts")
|
||||
def create_post(
|
||||
post: PostCreate,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
result = db.execute(user_posts_table.insert().values(
|
||||
user_id=current_user.f99_90_id,
|
||||
post_type=post.post_type,
|
||||
title=post.title,
|
||||
content=post.content,
|
||||
zodiac_type=post.zodiac_type,
|
||||
packaging=post.packaging,
|
||||
status="pending"
|
||||
))
|
||||
db.commit()
|
||||
return {"success": True, "id": result.inserted_primary_key[0]}
|
||||
|
||||
# ============ 成交数据 ============
|
||||
@router.get("/deals")
|
||||
def get_deals(
|
||||
zodiac_type: Optional[str] = None,
|
||||
limit: int = 20,
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
query = db.query(deals_table)
|
||||
if zodiac_type:
|
||||
query = query.filter(deals_table.c.zodiac_type == zodiac_type)
|
||||
results = query.order_by(deals_table.c.deal_date.desc()).limit(limit).all()
|
||||
return [dict(r._mapping) for r in results]
|
||||
|
||||
# ============ 通知 ============
|
||||
@router.get("/notifications")
|
||||
def get_notifications(limit: int = 10, db: Session = Depends(get_db)):
|
||||
results = db.query(notifications_table).filter(
|
||||
notifications_table.c.is_published == True
|
||||
).order_by(notifications_table.c.created_at.desc()).limit(limit).all()
|
||||
return [dict(r._mapping) for r in results]
|
||||
|
||||
# ============ 首页数据 ============
|
||||
@router.get("/home")
|
||||
def get_home(db: Session = Depends(get_db)):
|
||||
# 推荐发布
|
||||
posts = db.query(user_posts_table).filter(
|
||||
user_posts_table.c.status == "active"
|
||||
).order_by(user_posts_table.c.created_at.desc()).limit(10).all()
|
||||
|
||||
# 成交
|
||||
deals = db.query(deals_table).order_by(
|
||||
deals_table.c.deal_date.desc()
|
||||
).limit(10).all()
|
||||
|
||||
# 通知
|
||||
notices = db.query(notifications_table).filter(
|
||||
notifications_table.c.is_published == True
|
||||
).order_by(notifications_table.c.created_at.desc()).limit(5).all()
|
||||
|
||||
return {
|
||||
"posts": [dict(p._mapping) for p in posts],
|
||||
"deals": [dict(d._mapping) for d in deals],
|
||||
"notices": [dict(n._mapping) for n in notices]
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
VERSION=1.2.26
|
||||
VERSION=1.2.30
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<title>甲辰收藏 v1.2.25</title>
|
||||
<title>甲辰收藏 v1.2.26</title>
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="/static/icons/favicon.ico" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const src = path.join(__dirname, 'static', 'images');
|
||||
const dst = path.join(__dirname, 'dist', 'static', 'images');
|
||||
|
||||
if (!fs.existsSync(dst)) {
|
||||
fs.mkdirSync(dst, { recursive: true });
|
||||
}
|
||||
|
||||
if (fs.existsSync(src)) {
|
||||
fs.readdirSync(src).forEach(f => {
|
||||
const srcFile = path.join(src, f);
|
||||
const dstFile = path.join(dst, f);
|
||||
fs.copyFileSync(srcFile, dstFile);
|
||||
console.log('Copied:', f);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Logo复制完成');
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 133 KiB |
|
|
@ -0,0 +1,26 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 80">
|
||||
<defs>
|
||||
<linearGradient id="goldGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#FFE4B5"/>
|
||||
<stop offset="25%" stop-color="#FFD700"/>
|
||||
<stop offset="50%" stop-color="#FFA500"/>
|
||||
<stop offset="75%" stop-color="#DAA520"/>
|
||||
<stop offset="100%" stop-color="#B8860B"/>
|
||||
</linearGradient>
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="1.5" result="blur"/>
|
||||
<feFlood flood-color="#FFD700" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
|
||||
</filter>
|
||||
<filter id="shadow">
|
||||
<feDropShadow dx="2" dy="3" stdDeviation="2" flood-color="#000" flood-opacity="0.5"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Main title -->
|
||||
<text x="0" y="45" font-family="'Kaiti SC', 'STKaiti', 'KaiTi', 'SimKai', serif" font-size="52" font-weight="bold" fill="url(#goldGrad)" filter="url(#shadow)">甲辰收藏</text>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<text x="0" y="72" font-family="'Kaiti SC', 'STKaiti', 'KaiTi', 'SimKai', serif" font-size="20" fill="#DAA520" letter-spacing="4">生肖纪念钞管理系统</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -192,11 +192,11 @@ export default function Admin() {
|
|||
<div style={{ color: '#fbbf24', fontSize: '16px', fontWeight: 'bold' }}>{user.username}</div>
|
||||
<div style={{
|
||||
padding: '2px 8px', borderRadius: '4px',
|
||||
background: user.role === 'admin' ? 'rgba(16, 185, 129, 0.2)' : 'rgba(148, 163, 184, 0.2)',
|
||||
color: user.role === 'admin' ? '#10b981' : '#94a3b8',
|
||||
background: user.role === 'admin' ? 'rgba(16, 185, 129, 0.2)' : (user.role === 'editor' ? 'rgba(245, 158, 11, 0.2)' : 'rgba(148, 163, 184, 0.2)'),
|
||||
color: user.role === 'admin' ? '#10b981' : (user.role === 'editor' ? '#f59e0b' : '#94a3b8'),
|
||||
fontSize: '12px'
|
||||
}}>
|
||||
{user.role === 'admin' ? '👑 管理员' : '👤 用户'}
|
||||
{user.role === 'admin' ? '👑 管理员' : (user.role === 'editor' ? '📝 信息员' : '👤 用户')}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ color: '#64748b', fontSize: '13px', marginTop: '4px' }}>
|
||||
|
|
@ -300,6 +300,7 @@ export default function Admin() {
|
|||
>
|
||||
<option value="user">普通用户</option>
|
||||
<option value="admin">管理员</option>
|
||||
<option value="editor">信息员</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
|
@ -360,6 +361,7 @@ export default function Admin() {
|
|||
>
|
||||
<option value="user">普通用户</option>
|
||||
<option value="admin">管理员</option>
|
||||
<option value="editor">信息员</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@ export default function Info() {
|
|||
}
|
||||
|
||||
// 顶部tab:寻配号发布 / 发布管理
|
||||
const [activeTab, setActiveTab] = useState('publish')
|
||||
const [activeTab, setActiveTab] = useState('manage')
|
||||
const [showPublish, setShowPublish] = useState(false)
|
||||
const [publishType, setPublishType] = useState('deal') // 'seek' 或 'deal'
|
||||
const [myList, setMyList] = useState([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [editingItem, setEditingItem] = useState(null)
|
||||
const [filterType, setFilterType] = useState('all') // all/seek/deal
|
||||
const [expandedItems, setExpandedItems] = useState({}) // 展开状态
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
edition: '龙钞', type: '标百', isGraded: true, category: '成交', source: '直播', title: ''
|
||||
|
|
@ -28,6 +29,14 @@ export default function Info() {
|
|||
|
||||
const API_BASE = localStorage.getItem('API_BASE') || ''
|
||||
|
||||
// 切换展开/收起
|
||||
const toggleExpand = (itemId) => {
|
||||
setExpandedItems(prev => ({
|
||||
...prev,
|
||||
[itemId]: !prev[itemId]
|
||||
}))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTab === 'manage') fetchMyList()
|
||||
}, [activeTab])
|
||||
|
|
@ -53,7 +62,7 @@ export default function Info() {
|
|||
setLoading(true)
|
||||
try {
|
||||
const token = localStorage.getItem('token')
|
||||
const res = await fetch(`${API_BASE}/api/information/my/list`, {
|
||||
const res = await fetch(`${API_BASE}/api/information/list`, {
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
})
|
||||
if (res.ok) {
|
||||
|
|
@ -218,7 +227,6 @@ export default function Info() {
|
|||
<div style={{ position: 'sticky', top: 0, background: 'rgba(15,23,42,0.95)', backdropFilter: 'blur(10px)', padding: '16px 20px', borderBottom: '1px solid #1e293b', zIndex: 100 }}>
|
||||
<div style={{ display: 'flex', gap: '8px', background: '#1e293b', borderRadius: '12px', padding: '4px' }}>
|
||||
{[
|
||||
{ key: 'publish', label: '📝 寻配号发布' },
|
||||
{ key: 'manage', label: '📋 发布管理' }
|
||||
].map(tab => (
|
||||
<div
|
||||
|
|
@ -244,8 +252,8 @@ export default function Info() {
|
|||
</div>
|
||||
|
||||
<div style={{ padding: '20px' }}>
|
||||
{/* 寻配号发布页 */}
|
||||
{activeTab === 'publish' && (
|
||||
{/* 寻配号发布页 - 已删除 */}
|
||||
{false && (
|
||||
<div style={{ background: 'linear-gradient(145deg, #1f2937 0%, #111827 100%)', borderRadius: '16px', padding: '20px', border: '1px solid #374151', boxShadow: '0 4px 20px rgba(0,0,0,0.3)' }}>
|
||||
<h3 style={{ color: '#f9fafb', margin: '0 0 20px 0', fontSize: '18px', fontWeight: '600' }}>🔍 寻配号发布</h3>
|
||||
|
||||
|
|
@ -560,9 +568,10 @@ export default function Info() {
|
|||
) : (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
|
||||
{filteredList.map(item => (
|
||||
<div key={item.id} style={{ background: '#0f172a', borderRadius: '12px', padding: '16px', border: '1px solid #374151' }}>
|
||||
<div key={item.id} style={{ background: '#1e293b', borderRadius: '12px', padding: '16px', border: '1px solid #334155' }}>
|
||||
{/* 标题行 */}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '8px' }}>
|
||||
<div style={{ color: '#10b981', fontSize: '15px', fontWeight: '600', flex: 1 }}>{item.title}</div>
|
||||
<div style={{ color: '#fbbf24', fontSize: '15px', fontWeight: '600', flex: 1 }}>{item.title}</div>
|
||||
<span style={{
|
||||
background: item.info_type === 'deal' ? 'rgba(245,158,11,0.2)' : 'rgba(16,185,129,0.2)',
|
||||
color: item.info_type === 'deal' ? '#f59e0b' : '#10b981',
|
||||
|
|
@ -573,9 +582,53 @@ export default function Info() {
|
|||
{item.info_type === 'deal' ? '💰 行情' : '🔍 寻号'}
|
||||
</span>
|
||||
</div>
|
||||
{item.content && <div style={{ color: '#9ca3af', fontSize: '13px', marginBottom: '12px', lineHeight: '1.5' }}>{item.content}</div>}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div style={{ color: '#6b7280', fontSize: '12px' }}>{formatDate(item.created_at)}</div>
|
||||
{/* 日期+用户名 */}
|
||||
<div style={{ color: '#64748b', fontSize: '12px', marginBottom: '8px' }}>
|
||||
📅 {formatDate(item.created_at)} | 👤 {item.user_name || '匿名用户'}
|
||||
</div>
|
||||
{/* 正文 - 默认收起,点击展开 */}
|
||||
{item.content && (
|
||||
<div style={{ marginBottom: '12px' }}>
|
||||
<div
|
||||
onClick={() => toggleExpand(item.id)}
|
||||
style={{
|
||||
color: '#10b981',
|
||||
fontSize: '13px',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px',
|
||||
padding: '8px 12px',
|
||||
background: 'rgba(16,185,129,0.1)',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid rgba(16,185,129,0.2)'
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '16px' }}>{expandedItems[item.id] ? '▼' : '▶'}</span>
|
||||
<span>{expandedItems[item.id] ? '收起详情' : '展开查看详情'}</span>
|
||||
</div>
|
||||
{expandedItems[item.id] && (
|
||||
<div style={{
|
||||
color: '#e2e8f0',
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.8',
|
||||
marginTop: '12px',
|
||||
background: 'rgba(255,255,255,0.03)',
|
||||
padding: '12px',
|
||||
borderRadius: '8px',
|
||||
borderLeft: '3px solid #10b981'
|
||||
}}>
|
||||
{item.content.split('\n').map((line, i) => (
|
||||
<div key={i} style={{ marginBottom: i < item.content.split('\n').length - 1 ? '6px' : 0 }}>
|
||||
{line || ' '}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* 操作按钮 */}
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center', marginTop: '8px' }}>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<button onClick={() => handleEdit(item)} style={{ padding: '6px 12px', borderRadius: '6px', border: '1px solid #3b82f6', background: 'transparent', color: '#3b82f6', cursor: 'pointer', fontSize: '12px' }}>✏️ 编辑</button>
|
||||
<button onClick={() => handleDelete(item.id)} style={{ padding: '6px 12px', borderRadius: '6px', border: '1px solid #ef4444', background: 'transparent', color: '#ef4444', cursor: 'pointer', fontSize: '12px' }}>🗑️ 删除</button>
|
||||
|
|
|
|||
|
|
@ -27,11 +27,20 @@ export default function News() {
|
|||
})
|
||||
const [infoList, setInfoList] = useState([])
|
||||
const [viewMode, setViewMode] = useState('all')
|
||||
const [expandedItems, setExpandedItems] = useState({}) // 展开状态
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const API_BASE = localStorage.getItem('API_BASE') || ''
|
||||
const currentUser = localStorage.getItem('token') ? { id: 'logged_in' } : null
|
||||
|
||||
// 切换展开/收起
|
||||
const toggleExpand = (itemId) => {
|
||||
setExpandedItems(prev => ({
|
||||
...prev,
|
||||
[itemId]: !prev[itemId]
|
||||
}))
|
||||
}
|
||||
|
||||
// 获取资讯列表
|
||||
useEffect(() => {
|
||||
fetchInfoList()
|
||||
|
|
@ -507,10 +516,54 @@ export default function News() {
|
|||
</span>
|
||||
</div>
|
||||
)}
|
||||
{/* 正文 */}
|
||||
{/* 正文 - 默认收起,点击展开 */}
|
||||
{cleanContent && (
|
||||
<div style={{ color: '#e2e8f0', fontSize: '14px', lineHeight: '1.6', marginBottom: '8px' }}>
|
||||
{cleanContent}
|
||||
<div style={{ marginTop: '8px' }}>
|
||||
{/* 展开收起按钮 */}
|
||||
<div
|
||||
onClick={() => toggleExpand(item.id)}
|
||||
style={{
|
||||
color: '#10b981',
|
||||
fontSize: '13px',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px',
|
||||
padding: '8px 12px',
|
||||
background: 'rgba(16,185,129,0.1)',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid rgba(16,185,129,0.2)'
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '16px' }}>{expandedItems[item.id] ? '▼' : '▶'}</span>
|
||||
<span>{expandedItems[item.id] ? '收起详情' : '展开查看详情'}</span>
|
||||
</div>
|
||||
{/* 展开后的内容 */}
|
||||
{expandedItems[item.id] && (
|
||||
<div style={{
|
||||
color: '#e2e8f0',
|
||||
fontSize: '14px',
|
||||
lineHeight: '1.8',
|
||||
marginTop: '12px',
|
||||
background: 'rgba(255,255,255,0.03)',
|
||||
padding: '12px',
|
||||
borderRadius: '8px',
|
||||
borderLeft: '3px solid #10b981'
|
||||
}}>
|
||||
{cleanContent.split('\n').map((line, i) => {
|
||||
const isHighlight = line.includes('涨价') || line.includes('下跌') || line.includes('稀缺') || line.includes('热门')
|
||||
return (
|
||||
<div key={i} style={{
|
||||
marginBottom: i < cleanContent.split('\n').length - 1 ? '8px' : 0,
|
||||
color: isHighlight ? '#fbbf24' : '#e2e8f0',
|
||||
fontWeight: isHighlight ? 'bold' : 'normal'
|
||||
}}>
|
||||
{line || ' '}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* 联系方式 - 默认隐藏,显示*** */}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,240 @@
|
|||
# Logo 使用规范
|
||||
|
||||
**版本**: v1.0.0
|
||||
**更新日期**: 2026-03-16
|
||||
**状态**: ✅ 官方指定 Logo
|
||||
|
||||
---
|
||||
|
||||
## 🐉 官方 Logo
|
||||
|
||||
### 主 Logo
|
||||
|
||||
**文件**: `jiachenlong-logo.png`
|
||||
|
||||
**位置**:
|
||||
- 本地:`/static/images/jiachenlong-logo.png`
|
||||
- 前端服务器:`/var/www/html/static/images/jiachenlong-logo.png`
|
||||
|
||||
**规格**:
|
||||
- 格式:PNG
|
||||
- 大小:606KB
|
||||
- 尺寸:正方形(适合圆形裁剪)
|
||||
- 颜色:橙色(中国传统色)
|
||||
- 设计:龙型环绕 + "甲辰收藏"文字
|
||||
|
||||
---
|
||||
|
||||
## 📋 使用场景
|
||||
|
||||
### 1. 登录页面
|
||||
|
||||
**文件**: `frontend/src/pages/Login.jsx`
|
||||
|
||||
```jsx
|
||||
<img
|
||||
src="/static/images/jiachenlong-logo.png"
|
||||
alt="甲辰收藏"
|
||||
style={{
|
||||
width: '200px',
|
||||
height: '200px',
|
||||
borderRadius: '50%',
|
||||
boxShadow: '0 0 40px rgba(251, 191, 36, 0.4)',
|
||||
background: '#fff'
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
### 2. 首页
|
||||
|
||||
**文件**: `frontend/src/pages/Home.jsx`
|
||||
|
||||
```jsx
|
||||
<img
|
||||
src="/static/images/jiachenlong-logo.png"
|
||||
alt="甲辰收藏"
|
||||
style={{
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
borderRadius: '50%',
|
||||
objectFit: 'cover'
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
### 3. 藏品详情页
|
||||
|
||||
**文件**: `frontend/src/pages/Detail.jsx`
|
||||
|
||||
```jsx
|
||||
<img
|
||||
src="/static/images/jiachenlong-logo.png"
|
||||
alt="甲辰收藏"
|
||||
onError={(e) => {
|
||||
e.target.src = '/static/images/jiachenlong-logo.png';
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 样式规范
|
||||
|
||||
### 圆形样式(推荐)
|
||||
|
||||
```css
|
||||
.logo {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 0 40px rgba(251, 191, 36, 0.4);
|
||||
background: #fff;
|
||||
}
|
||||
```
|
||||
|
||||
### 小尺寸(导航栏等)
|
||||
|
||||
```css
|
||||
.logo-small {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
```
|
||||
|
||||
### 中等尺寸
|
||||
|
||||
```css
|
||||
.logo-medium {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 部署规范
|
||||
|
||||
### 部署脚本
|
||||
|
||||
**文件**: `scripts/deploy.sh`
|
||||
|
||||
部署脚本会自动:
|
||||
1. ✅ 检查 Logo 文件是否存在
|
||||
2. ✅ 部署前端构建文件
|
||||
3. ✅ 部署 Logo 到服务器
|
||||
4. ✅ 重启 Nginx
|
||||
|
||||
### 部署命令
|
||||
|
||||
```bash
|
||||
# 测试环境
|
||||
./scripts/deploy.sh 1.0.0 test
|
||||
|
||||
# 生产环境
|
||||
./scripts/deploy.sh 1.0.0 production
|
||||
```
|
||||
|
||||
### 手动部署
|
||||
|
||||
```bash
|
||||
# 1. 构建前端
|
||||
cd frontend
|
||||
npm run build
|
||||
|
||||
# 2. 部署到服务器
|
||||
scp -r dist/* root@8.149.137.26:/var/www/html/
|
||||
scp static/images/jiachenlong-logo.png root@8.149.137.26:/var/www/html/static/images/
|
||||
|
||||
# 3. 重启 Nginx
|
||||
ssh root@8.149.137.26 "nginx -s reload"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 必须遵守
|
||||
|
||||
1. ✅ **统一使用** `jiachenlong-logo.png`
|
||||
2. ✅ **禁止使用** 旧版 `logo.jpg`、`dragon-logo.jpg`、`title_logo.svg`
|
||||
3. ✅ **保持比例** - 始终使用正方形容器
|
||||
4. ✅ **圆形裁剪** - 使用 `border-radius: 50%`
|
||||
5. ✅ **白色背景** - Logo 需要白色背景衬托
|
||||
|
||||
### 禁止行为
|
||||
|
||||
- ❌ 不要修改 Logo 颜色
|
||||
- ❌ 不要拉伸变形
|
||||
- ❌ 不要添加其他效果
|
||||
- ❌ 不要使用其他 Logo 文件
|
||||
|
||||
---
|
||||
|
||||
## 📁 文件位置
|
||||
|
||||
### 本地开发
|
||||
|
||||
```
|
||||
jiachenlong/
|
||||
└── static/
|
||||
└── images/
|
||||
└── jiachenlong-logo.png # ✅ 官方 Logo
|
||||
```
|
||||
|
||||
### 前端服务器
|
||||
|
||||
```
|
||||
/var/www/html/
|
||||
└── static/
|
||||
└── images/
|
||||
└── jiachenlong-logo.png # ✅ 官方 Logo
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 更新流程
|
||||
|
||||
如需更新 Logo:
|
||||
|
||||
1. **替换文件**
|
||||
```bash
|
||||
cp new-logo.png /static/images/jiachenlong-logo.png
|
||||
```
|
||||
|
||||
2. **重新构建**
|
||||
```bash
|
||||
cd frontend
|
||||
npm run build
|
||||
```
|
||||
|
||||
3. **部署到服务器**
|
||||
```bash
|
||||
./scripts/deploy.sh 1.0.1 production
|
||||
```
|
||||
|
||||
4. **验证部署**
|
||||
```bash
|
||||
curl http://8.149.137.26/static/images/jiachenlong-logo.png -o /tmp/logo-check.png
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Logo 对比
|
||||
|
||||
| 文件 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| `jiachenlong-logo.png` | ✅ **官方指定** | 橙色圆形龙型 Logo |
|
||||
| `logo.jpg` | ❌ 废弃 | 旧版 Logo |
|
||||
| `dragon-logo.jpg` | ❌ 废弃 | 旧版龙型 Logo |
|
||||
| `title_logo.svg` | ❌ 废弃 | 旧版 SVG Logo |
|
||||
|
||||
---
|
||||
|
||||
**所有部署必须使用 `jiachenlong-logo.png`!**
|
||||
|
||||
**最后更新**: 2026-03-16
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# 图片资源
|
||||
|
||||
本目录存放项目的所有图片资源。
|
||||
|
||||
## 📁 文件列表
|
||||
|
||||
- `logo.jpg` - 系统主 Logo(106KB, 512x512)
|
||||
|
||||
## 🎨 使用方式
|
||||
|
||||
### 前端访问
|
||||
```jsx
|
||||
<img src="/static/images/logo.jpg" alt="logo" />
|
||||
```
|
||||
|
||||
### 后端访问(FastAPI)
|
||||
```python
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
```
|
||||
|
||||
## 📐 建议尺寸
|
||||
|
||||
- **Logo**: 512x512 或更大(用于缩放)
|
||||
- **背景图**: 1920x1080(全屏背景)
|
||||
- **头像**: 200x200(用户头像)
|
||||
|
||||
## 📦 格式建议
|
||||
|
||||
- **Logo**: PNG(透明背景)或 JPG
|
||||
- **照片**: JPG(压缩比好)
|
||||
- **图标**: SVG(矢量可缩放)或 PNG
|
||||
|
||||
---
|
||||
|
||||
**最后更新**: 2026-03-16
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 133 KiB |
|
|
@ -0,0 +1,26 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 80">
|
||||
<defs>
|
||||
<linearGradient id="goldGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#FFE4B5"/>
|
||||
<stop offset="25%" stop-color="#FFD700"/>
|
||||
<stop offset="50%" stop-color="#FFA500"/>
|
||||
<stop offset="75%" stop-color="#DAA520"/>
|
||||
<stop offset="100%" stop-color="#B8860B"/>
|
||||
</linearGradient>
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="1.5" result="blur"/>
|
||||
<feFlood flood-color="#FFD700" flood-opacity="0.6"/>
|
||||
<feComposite in2="blur" operator="in"/>
|
||||
<feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>
|
||||
</filter>
|
||||
<filter id="shadow">
|
||||
<feDropShadow dx="2" dy="3" stdDeviation="2" flood-color="#000" flood-opacity="0.5"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<!-- Main title -->
|
||||
<text x="0" y="45" font-family="'Kaiti SC', 'STKaiti', 'KaiTi', 'SimKai', serif" font-size="52" font-weight="bold" fill="url(#goldGrad)" filter="url(#shadow)">甲辰收藏</text>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<text x="0" y="72" font-family="'Kaiti SC', 'STKaiti', 'KaiTi', 'SimKai', serif" font-size="20" fill="#DAA520" letter-spacing="4">生肖纪念钞管理系统</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
Loading…
Reference in New Issue