v1.2.64 - 添加联系我们功能和反馈查看面板

This commit is contained in:
甲辰生产 2026-04-08 16:33:09 +08:00
parent e1ac548115
commit 441c8a5aa7
4 changed files with 130 additions and 3 deletions

View File

@ -255,3 +255,15 @@ class UserRole:
cls.USER: "用户"
}
return names.get(role, "用户")
class UserFeedback(Base):
"""用户反馈表"""
__tablename__ = "user_feedbacks"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.f99_90_id"), nullable=False)
content = Column(Text, nullable=False)
contact = Column(String(100), default="")
status = Column(String(20), default="pending") # pending, resolved
created_at = Column(DateTime, default=datetime.utcnow)

View File

@ -303,3 +303,47 @@ def delete_user(
db.commit()
return {"message": "删除成功"}
@router.post("/feedback")
async def submit_feedback(
content: str,
contact: str = "",
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""用户提交反馈"""
from app.models import UserFeedback
feedback = UserFeedback(
user_id=current_user.f99_90_id,
content=content,
contact=contact,
status="pending"
)
db.add(feedback)
db.commit()
return {"success": True, "message": "反馈已提交"}
@router.get("/feedback/list")
async def list_feedbacks(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""获取用户反馈列表(仅管理员)"""
if current_user.role != "admin":
return {"error": "无权限"}, 403
from app.models import UserFeedback
feedbacks = db.query(UserFeedback).order_by(UserFeedback.created_at.desc()).all()
return [{
"id": f.id,
"user_id": f.user_id,
"content": f.content,
"contact": f.contact,
"status": f.status,
"created_at": f.created_at.isoformat() if f.created_at else None
} for f in feedbacks]

View File

@ -9,6 +9,9 @@ export default function Home() {
const [seekStats, setSeekStats] = useState({ seekCount: 0, matchedCount: 0 })
const [recentPosts, setRecentPosts] = useState([])
const [dragonStats, setDragonStats] = useState({})
const [showFeedback, setShowFeedback] = useState(false)
const [feedbackContent, setFeedbackContent] = useState('')
const [feedbackContact, setFeedbackContact] = useState('')
const currentPath = window.location.hash.slice(1) || '/'
useEffect(() => {
@ -65,7 +68,26 @@ export default function Home() {
}).catch(() => {})
//
fetch('/api/yichens/stats/dragons-today').then(res => res.json()).then(data => {
//
const submitFeedback = async () => {
if (!feedbackContent.trim()) return alert('请输入反馈内容')
try {
const res = await fetch('/api/users/feedback', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` },
body: JSON.stringify({ content: feedbackContent, contact: feedbackContact })
})
const data = await res.json()
if (data.success) {
alert('反馈已提交,感谢您的意见!')
setShowFeedback(false)
setFeedbackContent('')
setFeedbackContact('')
}
} catch (e) { alert('提交失败') }
}
fetch('/api/yichens/stats/dragons-today').then(res => res.json()).then(data => {
setDragonStats(data || {})
}).catch(() => {})
@ -104,6 +126,34 @@ export default function Home() {
{ label: '总利润', value: formatMoney(stats.totalProfit) + '万', color: stats.totalProfit >= 0 ? '#10b981' : '#f43f5e' }
]
//
if (showFeedback) {
return (
<div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.8)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 9999 }}>
<div style={{ background: '#1a1a2e', borderRadius: '12px', padding: '24px', width: '90%', maxWidth: '400px' }}>
<div style={{ fontSize: '18px', fontWeight: 'bold', marginBottom: '16px', color: '#fff' }}>联系我们</div>
<textarea
value={feedbackContent}
onChange={(e) => setFeedbackContent(e.target.value)}
placeholder="请输入您的反馈内容..."
style={{ width: '100%', height: '120px', background: '#16213e', border: '1px solid #333', borderRadius: '8px', padding: '12px', color: '#fff', fontSize: '14px', marginBottom: '12px' }}
/>
<input
type="text"
value={feedbackContact}
onChange={(e) => setFeedbackContact(e.target.value)}
placeholder="联系方式(选填)"
style={{ width: '100%', background: '#16213e', border: '1px solid #333', borderRadius: '8px', padding: '12px', color: '#fff', fontSize: '14px', marginBottom: '16px' }}
/>
<div style={{ display: 'flex', gap: '12px' }}>
<button onClick={() => setShowFeedback(false)} style={{ flex: 1, padding: '12px', background: '#333', border: 'none', borderRadius: '8px', color: '#fff', cursor: 'pointer' }}>取消</button>
<button onClick={submitFeedback} style={{ flex: 1, padding: '12px', background: '#3b82f6', border: 'none', borderRadius: '8px', color: '#fff', cursor: 'pointer', fontWeight: 'bold' }}>提交</button>
</div>
</div>
</div>
)
}
return (
<div style={{
minHeight: '100vh', overflowY: 'auto',

View File

@ -15,7 +15,8 @@ export default function Info() {
const [myList, setMyList] = useState([])
const [loading, setLoading] = useState(false)
const [editingItem, setEditingItem] = useState(null)
const [filterType, setFilterType] = useState('all') // all/seek/deal
const [filterType, setFilterType] = useState('all')
const [showFeedbackTab, setShowFeedbackTab] = useState(false) // all/seek/deal
const [expandedItems, setExpandedItems] = useState({}) //
const [formData, setFormData] = useState({
@ -558,9 +559,29 @@ export default function Info() {
>
💰 行情
</button>
<button
onClick={() => setShowFeedbackTab(true)}
style={{
padding: '8px 16px',
background: showFeedbackTab ? '#8b5cf6' : '#374151',
border: 'none',
borderRadius: '6px',
color: '#fff',
fontSize: '13px',
cursor: 'pointer'
}}
>
📮 反馈
</button>
</div>
{/* 发布列表 */}
{showFeedbackTab ? (
<div style={{ marginTop: '20px' }}>
<div style={{ color: '#fff', fontSize: '16px', fontWeight: '600', marginBottom: '12px' }}>📮 用户反馈</div>
<FeedbackList />
</div>
) : (
/* 发布列表 */
{loading ? (
<div style={{ color: '#9ca3af', textAlign: 'center', padding: '30px' }}>加载中...</div>
) : filteredList.length === 0 ? (