import React, { useState, useEffect, useMemo, useRef } from 'react'; import { useThemeStore } from '../stores/themeStore'; import { Newspaper, MessageSquareOff } from 'lucide-react'; import './BizPage.scss'; export interface BizAccount { username: string; name: string; avatar: string; type: string; last_time: number; formatted_last_time: string; } export const BizAccountList: React.FC<{ onSelect: (account: BizAccount) => void; selectedUsername?: string; searchKeyword?: string; }> = ({ onSelect, selectedUsername, searchKeyword }) => { const [accounts, setAccounts] = useState([]); const [loading, setLoading] = useState(false); const [myWxid, setMyWxid] = useState(''); useEffect(() => { const initWxid = async () => { try { const wxid = await window.electronAPI.config.get('myWxid'); if (wxid) { setMyWxid(wxid as string); } } catch (e) { console.error("获取 myWxid 失败:", e); } }; initWxid().then(_r => { }); }, []); useEffect(() => { const fetch = async () => { if (!myWxid) { return; } setLoading(true); try { const res = await window.electronAPI.biz.listAccounts(myWxid) setAccounts(res || []); } catch (err) { console.error('获取服务号列表失败:', err); } finally { setLoading(false); } }; fetch().then(_r => { } ); }, [myWxid]); const filtered = useMemo(() => { let result = accounts; if (searchKeyword) { const q = searchKeyword.toLowerCase(); result = accounts.filter(a => (a.name && a.name.toLowerCase().includes(q)) || (a.username && a.username.toLowerCase().includes(q)) ); } return result.sort((a, b) => { if (a.username === 'gh_3dfda90e39d6') return -1; // 微信支付置顶 if (b.username === 'gh_3dfda90e39d6') return 1; return b.last_time - a.last_time; }); }, [accounts, searchKeyword]); if (loading) return
加载中...
; return (
{filtered.map(item => (
onSelect(item)} className={`biz-account-item ${selectedUsername === item.username ? 'active' : ''} ${item.username === 'gh_3dfda90e39d6' ? 'pay-account' : ''}`} >
{item.name || item.username} {item.formatted_last_time}
{/*{item.username === 'gh_3dfda90e39d6' && (*/} {/*
微信支付
*/} {/*)}*/}
{item.type === '0' ? '公众号' : item.type === '1' ? '服务号' : item.type === '2' ? '企业号' : item.type === '3' ? '企业附属' : '未知'}
))}
); }; export const BizMessageArea: React.FC<{ account: BizAccount | null; }> = ({ account }) => { const themeMode = useThemeStore((state) => state.themeMode); const [messages, setMessages] = useState([]); const [loading, setLoading] = useState(false); const [offset, setOffset] = useState(0); const [hasMore, setHasMore] = useState(true); const limit = 20; const messageListRef = useRef(null); const lastScrollHeightRef = useRef(0); const isInitialLoadRef = useRef(true); const [myWxid, setMyWxid] = useState(''); useEffect(() => { const initWxid = async () => { try { const wxid = await window.electronAPI.config.get('myWxid'); if (wxid) { setMyWxid(wxid as string); } } catch (e) { } }; initWxid(); }, []); const isDark = useMemo(() => { if (themeMode === 'dark') return true; if (themeMode === 'system') { return window.matchMedia('(prefers-color-scheme: dark)').matches; } return false; }, [themeMode]); useEffect(() => { if (account && myWxid) { setMessages([]); setOffset(0); setHasMore(true); isInitialLoadRef.current = true; loadMessages(account.username, 0); } }, [account, myWxid]); const loadMessages = async (username: string, currentOffset: number) => { if (loading || !myWxid) return; setLoading(true); if (messageListRef.current) { lastScrollHeightRef.current = messageListRef.current.scrollHeight; } try { let res; if (username === 'gh_3dfda90e39d6') { res = await window.electronAPI.biz.listPayRecords(myWxid, limit, currentOffset); } else { res = await window.electronAPI.biz.listMessages(username, myWxid, limit, currentOffset); } if (res) { if (res.length < limit) setHasMore(false); setMessages(prev => { const combined = currentOffset === 0 ? res : [...res, ...prev]; const uniqueMessages = Array.from(new Map(combined.map(item => [item.local_id || item.create_time, item])).values()); return uniqueMessages.sort((a, b) => a.create_time - b.create_time); }); setOffset(currentOffset + limit); } } catch (err) { console.error('加载消息失败:', err); } finally { setLoading(false); } }; useEffect(() => { if (!messageListRef.current) return; if (isInitialLoadRef.current && messages.length > 0) { messageListRef.current.scrollTop = messageListRef.current.scrollHeight; isInitialLoadRef.current = false; } else if (messages.length > 0 && !isInitialLoadRef.current && !loading) { const newScrollHeight = messageListRef.current.scrollHeight; const heightDiff = newScrollHeight - lastScrollHeightRef.current; if (heightDiff > 0 && messageListRef.current.scrollTop < 100) { messageListRef.current.scrollTop += heightDiff; } } }, [messages, loading]); const handleScroll = (e: React.UIEvent) => { const target = e.currentTarget; // 向上滚动到顶部附近触发加载更多(更旧的消息) if (target.scrollTop < 50) { if (!loading && hasMore && account) { loadMessages(account.username, offset); } } }; if (!account) { return (

请选择一个服务号查看消息

); } const formatMessageTime = (timestamp: number) => { if (!timestamp) return ''; const date = new Date(timestamp * 1000); const now = new Date(); const isToday = date.toDateString() === now.toDateString(); if (isToday) { return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false }); } const yesterday = new Date(now); yesterday.setDate(now.getDate() - 1); if (date.toDateString() === yesterday.toDateString()) { return `昨天 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`; } const isThisYear = date.getFullYear() === now.getFullYear(); if (isThisYear) { return `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`; } return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`; }; const defaultImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MDAiIGhlaWdodD0iMTgwIj48cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjE4MCIgZmlsbD0iI2Y1ZjVmNSIvPjwvc3ZnPg=='; return (

{account.name}

{hasMore && messages.length > 0 && (
{loading ? '加载中...' : '向上滚动加载更多历史消息'}
)} {!loading && messages.length === 0 && (

暂无本地记录

该公众号在当前数据库中没有可显示的聊天历史

)} {messages.map((msg, index) => { const showTime = true; return (
{showTime && (
{formatMessageTime(msg.create_time)}
)} {account.username === 'gh_3dfda90e39d6' ? (
{msg.merchant_icon ? :
¥
} {msg.merchant_name || '微信支付'}
{msg.title}
{msg.description}
{/*
{msg.formatted_time}
*/}
) : (
window.electronAPI.shell.openExternal(msg.url)} className="main-article">

{msg.title}

{msg.des &&
{msg.des}
} {msg.content_list && msg.content_list.length > 1 && (
{msg.content_list.slice(1).map((item: any, idx: number) => (
window.electronAPI.shell.openExternal(item.url)} className="sub-item"> {item.title} {item.cover && }
))}
)}
)}
); })} {loading && offset === 0 &&
加载中...
}
); }; const BizPage: React.FC = () => { const [selectedAccount, setSelectedAccount] = useState(null); return (
); } export default BizPage;