Files
go_win_template/frontend/src/components/Dashboard.tsx
Geliebte e266d3098a feat: 抖音直播间弹幕监控 - Go+Wails 实现
后端:
- SQLite 数据层 (rooms/users/chat_logs/settings/error_logs)
- 抖音 WebSocket 抓取器 (proto 解析, 心跳, watchdog)
- __ac_signature 算法 (纯 Go 移植)
- a_bogus/sign.js 签名 (goja JS 引擎)
- Echo HTTP API 服务器 (端口 5000)
- SSE 事件广播
- 自动保活 + 授权守护
- PAYS 云授权对接

前端:
- 顶栏: 品牌 + 日志/调试/客户名单/授权/文档/房间控制/主题切换
- 添加直播间 (链接解析 + 关键词)
-  Dashboard (卡片/列表视图, 公开/匿名房筛选, 分页)
- 详情页 (多 tab 消息流, 右侧用户面板, 颜色标记, 精准客户, 已看标记)
- 精准客户名单 (跨房间汇总)
- 订阅授权页面 (登录/注册/购买)
- 使用帮助 + 系统调试
- 白昼/黑夜主题切换 (CSS 变量 + iconfont)
- 底部版权栏
2026-09-18 04:25:57 +08:00

103 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useMemo } from 'react'
import RoomCard from './RoomCard'
import AddRoomBar from './AddRoomBar'
import type { RoomData } from '../types'
interface DashboardProps {
rooms: RoomData[]
viewMode: 'card' | 'list'
cardCols: number
listPerPage: number
onAddRoom: (url: string) => void
onToggleRoom: (id: string, action?: string) => void
onRemoveRoom: (id: string) => void
onSetActive: (id: string) => void
}
export default function Dashboard({ rooms, viewMode, cardCols, listPerPage, onAddRoom, onToggleRoom, onRemoveRoom, onSetActive }: DashboardProps) {
const [page, setPage] = useState(0)
const paginatedRooms = useMemo(() => {
if (viewMode === 'list') {
const start = page * listPerPage
return rooms.slice(start, start + listPerPage)
}
return rooms
}, [rooms, page, listPerPage, viewMode])
const totalPages = viewMode === 'list' ? Math.ceil(rooms.length / listPerPage) : 0
return (
<div className="dash-zone">
<AddRoomBar onAdd={onAddRoom} />
{viewMode === 'card' ? (
<div className="room-grid" style={{ gridTemplateColumns: `repeat(${cardCols}, 1fr)` }}>
{paginatedRooms.map(room => (
<RoomCard
key={room.id}
room={room}
onToggle={(a) => onToggleRoom(room.id, a)}
onRemove={() => onRemoveRoom(room.id)}
onDetail={() => onSetActive(room.id)}
/>
))}
{paginatedRooms.length === 0 && (
<div className="empty-state">
<p>暂无房间</p>
<p>在上方输入框添加直播间URL开始监控</p>
</div>
)}
</div>
) : (
<div className="room-list">
<table className="list-table">
<thead>
<tr>
<th>状态</th>
<th>房间</th>
<th>主播</th>
<th>类型</th>
<th>观众</th>
<th>消息</th>
<th>精准</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{paginatedRooms.map(room => (
<tr key={room.id} className={`list-row ${room.status}`} onClick={() => onSetActive(room.id)}>
<td><span className={`status-dot ${room.status}`} /></td>
<td className="list-title">{room.title}</td>
<td>{room.anchor_id}</td>
<td><span className={`kind-badge ${room.kind}`}>{room.kind === 'public' ? '公开' : '匿名'}</span></td>
<td>{room.viewers}</td>
<td>{room.total_msg}</td>
<td>{room.lead_count}</td>
<td className="list-actions">
<button className="list-btn start" onClick={e => { e.stopPropagation(); onToggleRoom(room.id, 'start') }}>▶</button>
<button className="list-btn stop" onClick={e => { e.stopPropagation(); onToggleRoom(room.id, 'stop') }}>⏹</button>
<button className="list-btn del" onClick={e => { e.stopPropagation(); onRemoveRoom(room.id) }}>✕</button>
</td>
</tr>
))}
</tbody>
</table>
{paginatedRooms.length === 0 && (
<div className="empty-state">
<p>暂无房间</p>
<p>在上方输入框添加直播间URL开始监控</p>
</div>
)}
</div>
)}
{totalPages > 1 && (
<div className="pagination">
<button className="page-btn" disabled={page === 0} onClick={() => setPage(p => p - 1)}>‹</button>
<span className="page-info">{page + 1} / {totalPages}</span>
<button className="page-btn" disabled={page >= totalPages - 1} onClick={() => setPage(p => p + 1)}>›</button>
</div>
)}
</div>
)
}