Files
qihuaiflow/frontend/src/pages/front/WechatHistory.tsx
T
QihuAI Design 82afe14a23 fix(wechat/token/market): 修复微信对话时间、Token数据重置、技能市场复活、媒体内联
1. 微信Bot对话记录时间按北京时间渲染(ImBotHub/WechatHistory 的 formatTime 显式 +08:00 + Asia/Shanghai);
2. 新增 /app/api/chat/media/relay 媒体中转端点(白名单 agnES 域名, 经香港代理代取),
   改写 _wechat_send_media 用平台中转地址,微信/iLink 可内联播放图片/视频;
3. 删除 014_clean_test_data.sql:该迁移每次重启重跑并 DELETE token_usage_logs/
   messages/conversations/knowledge 等,导致 Token 驾驶舱数据每次重启被清空;
4. 002_init_data.sql 移除 market_apps 自动播种(原 WHERE COUNT=0 在用户删空后重启复活),
   技能市场交由用户后台维护,重启不再复活。
2026-07-16 09:19:23 +08:00

106 lines
4.6 KiB
TypeScript

// @ts-nocheck
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { imUserAPI } from '@/api';
const WechatHistory: React.FC = () => {
const [loading, setLoading] = useState(true);
const [messages, setMessages] = useState<any[]>([]);
const [toast, setToast] = useState<{ msg: string; type: 'success' | 'error' } | null>(null);
const bottomRef = useRef<HTMLDivElement>(null);
const showToast = (msg: string, type: 'success' | 'error' = 'success') => {
setToast({ msg, type });
setTimeout(() => setToast(null), 3000);
};
const load = useCallback(async () => {
setLoading(true);
try {
const res = await imUserAPI.history();
if ((res as any)?.success && (res as any).data) {
setMessages((res as any).data.messages || []);
} else {
showToast('加载失败', 'error');
}
} catch (e) {
showToast('加载失败', 'error');
} finally {
setLoading(false);
}
}, []);
useEffect(() => { load(); }, [load]);
useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]);
const formatTime = (t: string) => {
if (!t) return '-';
// 后端按北京时间存储(无时区标记的 ISO),此处显式按 Asia/Shanghai 渲染,
// 避免浏览器本地时区导致对话时间错乱(与微信 Bot 绑定页一致)。
const iso = /[Z+]/.test(t) ? t : t + '+08:00';
const d = new Date(iso);
if (isNaN(d.getTime())) return t;
return d.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false });
};
return (
<div style={{ minHeight: '100%', background: '#FFFFFF', color: '#1A1F36', padding: '32px' }}>
<div style={{ maxWidth: '860px', margin: '0 auto' }}>
{/* Header */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '24px' }}>
<div>
<h1 style={{ fontSize: '24px', fontWeight: 700, margin: 0, color: '#1A1F36' }}>微信对话记录</h1>
<p style={{ color: '#6B7280', fontSize: '14px', margin: '6px 0 0 0' }}>
你通过个人微信与七虎AI 的对话历史,按时间顺序记录,可随时回看(仅本人可见,按账号隔离)。
</p>
</div>
<button onClick={load} disabled={loading} style={btnStyle}>{loading ? '刷新中…' : '刷新'}</button>
</div>
{/* Toast */}
{toast && (
<div style={{
position: 'fixed', top: '20px', right: '20px', zIndex: 9999,
padding: '12px 24px', borderRadius: '8px',
background: toast.type === 'success' ? '#10b981' : '#ef4444',
color: '#fff', fontSize: '14px', boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
}}>{toast.msg}</div>
)}
{/* 记录列表 */}
<div style={{ background: '#F8F9FC', borderRadius: '12px', padding: '16px', minHeight: '420px' }}>
{messages.length === 0 ? (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '380px', color: '#9CA3AF', fontSize: '14px', textAlign: 'center', padding: '0 24px' }}>
{loading ? '加载中…' : '暂无对话记录。请先在「微信Bot」中绑定并发送消息,对话记录会显示在这里。'}
</div>
) : (
messages.map((m, i) => (
<div key={i} style={{ display: 'flex', justifyContent: m.role === 'user' ? 'flex-end' : 'flex-start', marginBottom: '14px' }}>
<div style={{ maxWidth: '78%' }}>
<div style={{ fontSize: '11px', color: '#9CA3AF', marginBottom: '4px', textAlign: m.role === 'user' ? 'right' : 'left' }}>
{m.role === 'user' ? '我(微信)' : '七虎AI'} · {formatTime(m.created_at)}
</div>
<div style={{
padding: '10px 14px', borderRadius: '12px', fontSize: '14px', lineHeight: '1.6',
whiteSpace: 'pre-wrap', wordBreak: 'break-word',
background: m.role === 'user' ? '#5B5FED' : '#FFFFFF',
color: m.role === 'user' ? '#fff' : '#1A1F36',
border: m.role === 'user' ? 'none' : '1px solid #E5E7EB',
}}>{m.content}</div>
</div>
</div>
))
)}
<div ref={bottomRef} />
</div>
</div>
</div>
);
};
const btnStyle: React.CSSProperties = {
padding: '8px 20px', background: '#5B5FED', border: 'none', borderRadius: '6px',
color: '#fff', fontSize: '14px', fontWeight: 500, cursor: 'pointer',
};
export default WechatHistory;