1305 lines
31 KiB
HTML
1305 lines
31 KiB
HTML
<!DOCTYPE html>
|
||
|
||
<html lang="zh-CN">
|
||
|
||
<head>
|
||
|
||
<meta charset="UTF-8">
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
||
<title>直播管理系统</title>
|
||
|
||
<script src="./js/tailwindcss.js"></script>
|
||
|
||
<link href="./fontawesome-free-6.4.0-web/css/all.css" rel="stylesheet">
|
||
|
||
|
||
|
||
<script>
|
||
|
||
tailwind.config = {
|
||
|
||
theme: {
|
||
|
||
extend: {
|
||
|
||
colors: {
|
||
|
||
primary: '#165DFF',
|
||
|
||
secondary: '#36CFC9',
|
||
|
||
neutral: '#F5F7FA',
|
||
|
||
'neutral-dark': '#4E5969',
|
||
|
||
success: '#00B42A',
|
||
|
||
warning: '#FF7D00',
|
||
|
||
danger: '#F53F3F',
|
||
|
||
info: '#1677FF', // 用于监听中状态 (此颜色定义保留,但监听中UI元素将被移除)
|
||
|
||
},
|
||
|
||
fontFamily: {
|
||
|
||
inter: ['Inter', 'system-ui', 'sans-serif'],
|
||
|
||
},
|
||
|
||
spacing: { // 确保所需间距已定义
|
||
|
||
'4': '1rem', // 标准间距/内边距
|
||
|
||
'3': '0.75rem', // 较小间距/内边距
|
||
|
||
'8': '2rem', // 示例: 操作按钮宽度
|
||
|
||
'10': '2.5rem', // 示例: 头像尺寸
|
||
|
||
},
|
||
|
||
fontSize: { // 添加可能需要的小字体尺寸
|
||
|
||
'xxs': '0.6rem', // ~10px
|
||
|
||
'xs': '.75rem', // 12px (default text-xs)
|
||
|
||
'sm': '.875rem', // 14px (default text-sm)
|
||
|
||
'base': '1rem', // 16px
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
</script>
|
||
|
||
<style type="text/tailwindcss">
|
||
|
||
@layer utilities {
|
||
|
||
.content-auto {
|
||
|
||
content-visibility: auto;
|
||
|
||
}
|
||
|
||
|
||
|
||
.sidebar-item {
|
||
|
||
@apply flex items-center gap-3 px-4 py-3 rounded-lg transition-all duration-200 hover:bg-primary/10 hover:text-primary;
|
||
|
||
}
|
||
|
||
|
||
|
||
.sidebar-item.active {
|
||
|
||
@apply bg-primary/10 text-primary font-medium;
|
||
|
||
}
|
||
|
||
|
||
|
||
.rule-card {
|
||
|
||
@apply bg-white rounded-xl shadow-sm border border-gray-100 transition-all duration-300 hover:shadow-md;
|
||
|
||
}
|
||
|
||
|
||
|
||
.btn-primary {
|
||
|
||
@apply bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-lg transition-all duration-200 shadow-sm hover:shadow flex items-center justify-center gap-2;
|
||
|
||
}
|
||
|
||
|
||
|
||
.btn-outline {
|
||
|
||
@apply border border-gray-200 hover:border-primary/50 text-neutral-dark hover:text-primary px-4 py-2 rounded-lg transition-all duration-200 flex items-center justify-center gap-2;
|
||
|
||
}
|
||
|
||
|
||
|
||
.btn-danger { /* 新增删除按钮样式 */
|
||
|
||
@apply bg-danger hover:bg-danger/90 text-white px-4 py-2 rounded-lg transition-all duration-200 shadow-sm hover:shadow flex items-center justify-center gap-2;
|
||
|
||
}
|
||
|
||
|
||
|
||
.fade-in {
|
||
|
||
animation: fadeIn 0.5s ease-in-out;
|
||
|
||
}
|
||
|
||
|
||
|
||
@keyframes fadeIn {
|
||
|
||
from {
|
||
|
||
opacity: 0;
|
||
|
||
transform: translateY(-10px);
|
||
|
||
}
|
||
|
||
to {
|
||
|
||
opacity: 1;
|
||
|
||
transform: translateY(0);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
.pulse {
|
||
|
||
animation: pulse 2s infinite;
|
||
|
||
}
|
||
|
||
|
||
|
||
@keyframes pulse {
|
||
|
||
0% {
|
||
|
||
transform: scale(1);
|
||
|
||
}
|
||
|
||
50% {
|
||
|
||
transform: scale(1.05);
|
||
|
||
}
|
||
|
||
100% {
|
||
|
||
transform: scale(1);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
.scrollbar-thin {
|
||
|
||
scrollbar-width: thin;
|
||
|
||
}
|
||
|
||
|
||
|
||
.scrollbar-thin::-webkit-scrollbar {
|
||
|
||
width: 4px;
|
||
|
||
}
|
||
|
||
|
||
|
||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||
|
||
background-color: rgba(156, 163, 175, 0.5);
|
||
|
||
border-radius: 4px;
|
||
|
||
}
|
||
|
||
|
||
|
||
.sticky-top-sidebar {
|
||
|
||
position: sticky;
|
||
|
||
top: 0;
|
||
|
||
z-index: 10;
|
||
|
||
}
|
||
|
||
|
||
|
||
.blacklist-item-sidebar {
|
||
|
||
@apply bg-white rounded-lg p-3 flex items-center gap-3 hover:bg-primary/5 transition-all;
|
||
|
||
}
|
||
|
||
|
||
|
||
.blacklist-item-sidebar .options-btn {
|
||
|
||
@apply flex-shrink-0 w-8 flex items-center justify-center;
|
||
|
||
}
|
||
|
||
|
||
|
||
#blacklist-container-sidebar .blacklist-item-sidebar:not(:last-child) {
|
||
|
||
margin-bottom: 0.75rem;
|
||
|
||
}
|
||
|
||
|
||
|
||
.blacklist-item-sidebar .time-text {
|
||
|
||
@apply text-xxs text-gray-400 flex-shrink-0 opacity-75;
|
||
|
||
}
|
||
|
||
|
||
|
||
.blacklist-item-sidebar .nickname-text {
|
||
|
||
@apply text-sm font-medium truncate;
|
||
|
||
}
|
||
|
||
|
||
|
||
.blacklist-item-sidebar .reason-text {
|
||
|
||
@apply text-xs text-gray-500 mt-1 truncate;
|
||
|
||
}
|
||
|
||
|
||
|
||
.checkbox-item {
|
||
|
||
@apply flex items-center;
|
||
|
||
}
|
||
|
||
|
||
|
||
/* 监听中的样式 (已移除相关UI元素的使用,此CSS类保留但可能不再作用于任何元素) */
|
||
|
||
.monitoring-status {
|
||
|
||
@apply text-info text-xs ml-2 animate-pulse;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
</style>
|
||
|
||
</head>
|
||
|
||
<body class="bg-neutral font-inter text-gray-800">
|
||
|
||
<!-- 顶部导航栏 - 移动设备 -->
|
||
|
||
<header class="lg:hidden fixed top-0 left-0 right-0 bg-white shadow-sm z-10">
|
||
|
||
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
|
||
|
||
<div class="flex items-center gap-2">
|
||
|
||
<i class="fa-solid fa-shield text-primary text-lg"></i>
|
||
|
||
<span class="text-lg font-bold text-primary">智能保镖</span>
|
||
|
||
</div>
|
||
|
||
<button id="mobile-menu-button" class="p-2 rounded-lg hover:bg-gray-100">
|
||
|
||
<i class="fa-solid fa-bars"></i>
|
||
|
||
</button>
|
||
|
||
</div>
|
||
|
||
</header>
|
||
|
||
|
||
|
||
<div class="flex min-h-screen pt-14 lg:pt-0">
|
||
|
||
<!-- 侧边栏导航 - 桌面端 -->
|
||
|
||
<aside id="sidebar"
|
||
|
||
class="lg:w-64 bg-white shadow-sm flex-shrink-0 hidden lg:block transition-all duration-300 z-20">
|
||
|
||
<div class="p-4 border-b border-gray-100">
|
||
|
||
<div class="flex items-center gap-2">
|
||
|
||
<i class="fa-solid fa-shield text-primary text-xl"></i>
|
||
|
||
<h1 class="text-lg font-bold text-primary">智能保镖</h1>
|
||
|
||
</div>
|
||
|
||
<p class="text-xs text-gray-400 mt-1">直播间智能管理助手</p>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<nav class="p-4">
|
||
|
||
<ul class="space-y-1">
|
||
|
||
<li><a href="live-management.html" class="sidebar-item active"><i class="fa-solid fa-video"></i> 直播管理</a>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
<li><a href="smart-bodyguard.html" class="sidebar-item"><i class="fa-solid fa-cogs"></i>
|
||
|
||
保镖设置</a>
|
||
|
||
</li>
|
||
|
||
<li><a href="id-whitelist-blacklist.html" class="sidebar-item"><i class="fa-solid fa-user-check"></i> ID
|
||
|
||
黑白名单</a></li>
|
||
|
||
<li><a href="id-blacklist.html" class="sidebar-item"><i class="fas fa-ban"></i> 拉黑记录</a></li>
|
||
|
||
<li><a href="#" class="sidebar-item"><i class="fa-solid fa-book"></i> 使用教程</a></li>
|
||
|
||
<li><a href="#" class="sidebar-item"><i class="fa-solid fa-comment-dots"></i> 意见反馈</a></li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
|
||
</aside>
|
||
|
||
|
||
|
||
<!-- 主内容区 -->
|
||
|
||
<main class="flex-1 overflow-y-auto bg-neutral p-4 lg:p-6">
|
||
|
||
<!-- 直播管理表格 -->
|
||
|
||
<div class="bg-white rounded-xl shadow-sm overflow-hidden mb-6">
|
||
|
||
<div class="border-b p-4 flex justify-between items-center">
|
||
|
||
<h2 class="font-medium text-lg">直播账号管理</h2>
|
||
|
||
<div class="flex gap-3">
|
||
|
||
<!-- 新增按钮调用 login() -->
|
||
|
||
<button class="btn-primary flex items-center gap-2" onclick="login()">
|
||
|
||
<i class="fa-solid fa-plus"></i>
|
||
|
||
<span>新增</span>
|
||
|
||
</button>
|
||
|
||
<!-- 刷新按钮调用 refreshTable() -->
|
||
|
||
<button class="btn-outline flex items-center gap-2" onclick="refreshTable()">
|
||
|
||
<i class="fa-solid fa-sync"></i>
|
||
|
||
<span>刷新</span>
|
||
|
||
</button>
|
||
|
||
<!-- 全局停止监听按钮 (保留,但逻辑可能依赖后端判断是否有监听任务) -->
|
||
|
||
<button id="global-stop-monitor-btn" class="btn-danger flex items-center gap-2" onclick="stopMonitoring()">
|
||
|
||
<i class="fa-solid fa-stop"></i>
|
||
|
||
<span>停止监听</span>
|
||
|
||
</button>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="overflow-x-auto">
|
||
|
||
<table class="w-full border-collapse">
|
||
|
||
<thead>
|
||
|
||
<tr class="bg-neutral">
|
||
|
||
<th class="py-3 px-4 text-left text-sm font-medium text-gray-600">序号</th>
|
||
|
||
<th class="py-3 px-4 text-left text-sm font-medium text-gray-600">抖音号</th>
|
||
|
||
<th class="py-3 px-4 text-left text-sm font-medium text-gray-600">到期时间</th>
|
||
|
||
<th class="py-3 px-4 text-left text-sm font-medium text-gray-600">主播昵称</th>
|
||
|
||
<th class="py-3 px-4 text-left text-sm font-medium text-gray-600">操作</th>
|
||
|
||
</tr>
|
||
|
||
</thead>
|
||
|
||
<tbody id="account-table-body">
|
||
|
||
<!-- 表格行将由 JavaScript 动态生成 -->
|
||
|
||
</tbody>
|
||
|
||
<tfoot class="bg-neutral">
|
||
|
||
<tr>
|
||
|
||
<td colspan="5" class="py-3 px-4 text-sm text-gray-600">
|
||
|
||
<span id="account-count-info">第 0-0 条/总共 0 条</span>
|
||
|
||
<!-- 简单的分页占位,实际分页需复杂JS逻辑 -->
|
||
|
||
<!-- <a href="#" class="px-2 py-1 rounded hover:bg-primary/10 hover:text-primary transition-all">1</a> -->
|
||
|
||
</td>
|
||
|
||
</tr>
|
||
|
||
</tfoot>
|
||
|
||
</table>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</main>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<script>
|
||
|
||
// 移动端菜单切换
|
||
|
||
document.getElementById('mobile-menu-button').addEventListener('click', function () {
|
||
|
||
const sidebar = document.getElementById('sidebar');
|
||
|
||
sidebar.classList.toggle('hidden');
|
||
|
||
if (!sidebar.classList.contains('hidden')) {
|
||
|
||
sidebar.classList.add('fixed', 'inset-0');
|
||
|
||
sidebar.classList.remove('lg:block', 'lg:w-64');
|
||
|
||
} else {
|
||
|
||
sidebar.classList.remove('fixed', 'inset-0');
|
||
|
||
sidebar.classList.add('lg:block', 'lg:w-64');
|
||
|
||
}
|
||
|
||
});
|
||
|
||
|
||
|
||
// --- 全局状态和 UI 更新函数 (UI 更新逻辑已移除) ---
|
||
|
||
|
||
|
||
// 跟踪当前正在监听的账号 url_part (此变量已不再用于UI状态切换)
|
||
|
||
// let currentlyMonitoringUrlPart = null; // 移除此行
|
||
|
||
|
||
|
||
// 更新单个账号行的操作按钮 UI (此函数已移除,UI不再根据状态切换按钮)
|
||
|
||
// function updateAccountRowUI(urlPart, isMonitoring) { ... } // 移除此函数
|
||
|
||
|
||
|
||
// 启用或禁用除指定 urlPart 以外的所有"监听"按钮 (此函数已移除)
|
||
|
||
// function toggleOtherMonitorButtons(enable, currentUrlPart) { ... } // 移除此函数
|
||
|
||
|
||
|
||
// 由 Python 调用,通知前端开始监听某个账号 (仅保留作为Python的调用接口,不再进行UI更新)
|
||
|
||
function startMonitoringUI(urlPart) {
|
||
|
||
console.log('前端接到通知: Python开始监听', urlPart);
|
||
|
||
// currentlyMonitoringUrlPart = urlPart; // 移除此行
|
||
|
||
// updateAccountRowUI(urlPart, true); // 移除此行
|
||
|
||
// UI不再根据监听状态变化
|
||
|
||
// 可以选择刷新表格来更新可能的"监听中"状态显示(如果Python将状态包含在account对象中)
|
||
|
||
// refreshTable(); // 根据需求决定是否需要刷新整个表格
|
||
|
||
}
|
||
|
||
|
||
|
||
// 由 Python 调用,通知前端停止监听某个账号 (仅保留作为Python的调用接口,不再进行UI更新)
|
||
|
||
function stopMonitoringUI(urlPart, reason) {
|
||
|
||
console.log('前端接到通知: Python停止监听', urlPart, '原因:', reason);
|
||
|
||
// currentlyMonitoringUrlPart = null; // 移除此行
|
||
|
||
// updateAccountRowUI(urlPart, false); // 移除此行
|
||
|
||
// UI不再根据监听状态变化
|
||
|
||
// 可以选择刷新表格来更新可能的"监听中"状态显示(如果Python将状态包含在account对象中)
|
||
|
||
// refreshTable(); // 根据需求决定是否需要刷新整个表格
|
||
|
||
// 可选: 显示停止原因给用户 (alert 或其他方式)
|
||
|
||
// if (reason === 'stopped_manually') {
|
||
|
||
// alert(`账号 ${urlPart} 监听已停止。`);
|
||
|
||
// } else if (reason === 'live_detected') {
|
||
|
||
// alert(`账号 ${urlPart} 已开播,正在跳转...`);
|
||
|
||
// } else if (reason === 'error') {
|
||
|
||
// alert(`账号 ${urlPart} 监听过程中发生错误并停止。`);
|
||
|
||
// }
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
// --- pywebview API 调用函数 ---
|
||
|
||
|
||
|
||
// 调用Python的login_click方法
|
||
|
||
function login() {
|
||
|
||
console.log('调用Python登录...');
|
||
|
||
if (window.pywebview) {
|
||
|
||
// 禁用新增和刷新按钮,避免重复操作
|
||
|
||
const addButton = document.querySelector('.btn-primary');
|
||
|
||
const refreshButton = document.querySelector('.btn-outline');
|
||
|
||
if (addButton) {
|
||
|
||
addButton.disabled = true;
|
||
|
||
addButton.classList.add('opacity-50', 'cursor-not-allowed');
|
||
|
||
}
|
||
|
||
if (refreshButton) {
|
||
|
||
refreshButton.disabled = true;
|
||
|
||
refreshButton.classList.add('opacity-50', 'cursor-not-allowed');
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
window.pywebview.api.login_click()
|
||
|
||
.then(success => {
|
||
|
||
if (success) {
|
||
|
||
console.log('Python登录流程已启动');
|
||
|
||
// Note: The actual UI refresh happens when Python calls updateAccountTable
|
||
|
||
} else {
|
||
|
||
console.log('Python登录流程启动失败');
|
||
|
||
// 可以显示一个提示
|
||
|
||
alert('登录失败,请重试。');
|
||
|
||
}
|
||
|
||
})
|
||
|
||
.catch(error => {
|
||
|
||
console.error('调用Python login_click出错:', error);
|
||
|
||
// 可以显示一个错误提示
|
||
|
||
alert(`登录发生错误: ${error}`);
|
||
|
||
})
|
||
|
||
.finally(() => {
|
||
|
||
// 无论成功或失败,重新启用按钮
|
||
|
||
if (addButton) {
|
||
|
||
addButton.disabled = false;
|
||
|
||
addButton.classList.remove('opacity-50', 'cursor-not-allowed');
|
||
|
||
}
|
||
|
||
if (refreshButton) {
|
||
|
||
refreshButton.disabled = false;
|
||
|
||
refreshButton.classList.remove('opacity-50', 'cursor-not-allowed');
|
||
|
||
}
|
||
|
||
});
|
||
|
||
} else {
|
||
|
||
console.error('pywebview API 未就绪.');
|
||
|
||
alert('应用正在启动中,请稍后再试。');
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
// 调用Python的refresh_accounts方法
|
||
|
||
function refreshTable() {
|
||
|
||
console.log('调用Python刷新账号列表...');
|
||
|
||
if (window.pywebview) {
|
||
|
||
// Python 的 refresh_accounts 会读取文件并调用 updateAccountTable 来更新 UI
|
||
|
||
window.pywebview.api.refresh_accounts()
|
||
|
||
.catch(error => {
|
||
|
||
console.error('调用Python refresh_accounts出错:', error);
|
||
|
||
// 可以显示一个错误提示
|
||
|
||
alert(`刷新账号列表发生错误: ${error}`);
|
||
|
||
});
|
||
|
||
} else {
|
||
|
||
console.error('pywebview API 未就绪.');
|
||
|
||
alert('应用正在启动中,请稍后再试。');
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
// 调用Python的删除账号方法
|
||
|
||
// 接收 urlPart, nickname, douyinId 作为参数
|
||
|
||
function deleteAccount(urlPart, nickname, douyinId) {
|
||
|
||
console.log('前端请求删除账号:', urlPart, '昵称:', nickname, '抖音号:', douyinId);
|
||
|
||
|
||
|
||
// 移除前端检查是否正在监听的逻辑,由后端处理
|
||
|
||
// if (currentlyMonitoringUrlPart === urlPart) { ... return; } // 移除此段
|
||
|
||
|
||
|
||
if (window.pywebview) {
|
||
|
||
const displayNickname = nickname || '未知昵称';
|
||
|
||
const displayDouyinId = douyinId || '未知抖音号';
|
||
|
||
const confirmMessage = `确定要删除账号 "${displayNickname}" (抖音号: ${displayDouyinId}) 吗?`;
|
||
|
||
|
||
|
||
const confirmDelete = confirm(confirmMessage);
|
||
|
||
if (confirmDelete) {
|
||
|
||
window.pywebview.api.delete_account(urlPart)
|
||
|
||
.then(success => {
|
||
|
||
if (success) {
|
||
|
||
console.log(`Python 成功处理删除请求,url_part: ${urlPart}`);
|
||
|
||
// 删除成功后刷新表格
|
||
|
||
refreshTable();
|
||
|
||
} else {
|
||
|
||
console.warn(`Python 处理删除请求失败或未找到账号,url_part: ${urlPart}`);
|
||
|
||
// Python端在无法删除正在监听的账号时可能已经弹出了提示,这里可以不重复弹
|
||
|
||
// alert(`删除账号 ${displayNickname} (抖音号: ${displayDouyinId}) 失败或未找到。`);
|
||
|
||
}
|
||
|
||
})
|
||
|
||
.catch(error => {
|
||
|
||
console.error(`调用Python delete_account 出错,url_part: ${urlPart}:`, error);
|
||
|
||
alert(`删除账号发生错误: ${error}`);
|
||
|
||
});
|
||
|
||
} else {
|
||
|
||
console.log('用户取消删除。');
|
||
|
||
}
|
||
|
||
|
||
|
||
} else {
|
||
|
||
console.error('pywebview API 未就绪.');
|
||
|
||
alert('应用正在启动中,请稍后再试。');
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
// 调用Python的监听账号方法
|
||
|
||
function monitorAccount(urlPart, nickname) {
|
||
|
||
console.log('前端请求监听账号:', urlPart);
|
||
|
||
|
||
|
||
// 移除前端检查是否已有账号在监听的逻辑,由后端处理
|
||
|
||
// if (currentlyMonitoringUrlPart) { ... return; } // 移除此段
|
||
|
||
|
||
|
||
if (window.pywebview) {
|
||
|
||
window.pywebview.api.monitor_account(urlPart, nickname)
|
||
|
||
.then(success => {
|
||
|
||
if (success) {
|
||
|
||
console.log(`Python 成功启动账号 ${urlPart} 的监听流程。`);
|
||
|
||
// UI更新将通过 refreshTable 或其他方式由Python触发(如果需要显示监听中状态)
|
||
|
||
// refreshTable(); // 如果Python需要在状态列显示"监听中",则需要刷新表格
|
||
|
||
} else {
|
||
|
||
console.warn(`Python 启动账号 ${urlPart} 监听失败。`);
|
||
|
||
// Python 在失败时可能已经弹窗提示,这里可以不处理或加一个通用提示
|
||
|
||
// alert('启动监听失败,请检查日志或稍后再试。');
|
||
|
||
}
|
||
|
||
})
|
||
|
||
.catch(error => {
|
||
|
||
console.error(`调用Python monitor_account 出错,url_part: ${urlPart}:`, error);
|
||
|
||
alert(`启动监听时发生错误: ${error}`);
|
||
|
||
});
|
||
|
||
} else {
|
||
|
||
console.error('pywebview API 未就绪.');
|
||
|
||
alert('应用正在启动中,请稍后再试。');
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
// 调用Python的停止监听方法 (现在是全局停止按钮触发,不依赖特定urlPart)
|
||
|
||
function stopMonitoring() { // urlPart 参数不再需要,函数签名修改
|
||
|
||
console.log('前端请求停止当前所有监听任务');
|
||
|
||
|
||
|
||
// 移除前端检查是否正在监听的逻辑,由后端处理
|
||
|
||
// if (!currentlyMonitoringUrlPart) { ... return; } // 移除此段
|
||
|
||
|
||
|
||
if (window.pywebview) {
|
||
|
||
window.pywebview.api.stop_monitoring() // 调用全局停止方法
|
||
|
||
.then(success => {
|
||
|
||
if (success) {
|
||
|
||
console.log('Python 成功停止监听。');
|
||
|
||
// UI更新将通过 refreshTable 或其他方式由Python触发
|
||
|
||
// refreshTable(); // 如果需要移除"监听中"状态,则需要刷新表格
|
||
|
||
} else {
|
||
|
||
console.warn('Python 停止监听失败 (可能没有监听任务在运行)。');
|
||
|
||
// 后端会处理没有监听任务的情况,前端无需额外处理
|
||
|
||
}
|
||
|
||
})
|
||
|
||
.catch(error => {
|
||
|
||
console.error('调用Python stop_monitoring 出错:', error);
|
||
|
||
alert(`停止监听时发生错误: ${error}`);
|
||
|
||
});
|
||
|
||
} else {
|
||
|
||
console.error('pywebview API 未就绪.');
|
||
|
||
alert('应用正在启动中,请稍后再试。');
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
// --- JavaScript 动态渲染表格函数 ---
|
||
|
||
|
||
|
||
// 这个函数由 Python 调用,用于更新账号列表
|
||
|
||
// 现在它总是渲染"监听"和"删除"按钮,不再根据监听状态切换UI
|
||
|
||
function updateAccountTable(accounts) {
|
||
|
||
console.log('接收到账号数据,正在更新表格:', accounts);
|
||
|
||
const tbody = document.getElementById('account-table-body');
|
||
|
||
const countInfo = document.getElementById('account-count-info');
|
||
|
||
tbody.innerHTML = ''; // 清空当前表格内容
|
||
|
||
|
||
|
||
if (!accounts || accounts.length === 0) {
|
||
|
||
// 如果没有账号,显示提示信息或空状态
|
||
|
||
const noDataRow = `<tr><td colspan="5" class="py-3 px-4 text-sm text-center text-gray-500">暂无账号,请点击"新增"按钮添加。</td></tr>`;
|
||
|
||
tbody.innerHTML = noDataRow;
|
||
|
||
countInfo.textContent = '第 0-0 条/总共 0 条';
|
||
|
||
// currentlyMonitoringUrlPart = null; // 移除此行
|
||
|
||
// toggleOtherMonitorButtons(true, null); // 移除此行
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
|
||
// 遍历账号数据,创建并添加表格行
|
||
|
||
accounts.forEach((account, index) => {
|
||
|
||
const row = document.createElement('tr');
|
||
|
||
// 添加 data 属性以便通过 url_part 定位行
|
||
|
||
row.dataset.urlPart = account.url_part;
|
||
|
||
row.dataset.nickname = account.nickname || ''; // 存储昵称和抖音号,方便 deleteAccount 使用
|
||
|
||
row.dataset.douyinId = account.douyin_id || '';
|
||
|
||
row.classList.add('border-b', 'hover:bg-gray-50'); // 添加样式
|
||
|
||
|
||
|
||
// 序号
|
||
|
||
const indexCell = document.createElement('td');
|
||
|
||
indexCell.classList.add('py-3', 'px-4', 'text-sm');
|
||
|
||
indexCell.textContent = index + 1; // 从1开始计数
|
||
|
||
row.appendChild(indexCell);
|
||
|
||
|
||
|
||
// 抖音号
|
||
|
||
const douyinIdCell = document.createElement('td');
|
||
|
||
douyinIdCell.classList.add('py-3', 'px-4', 'text-sm');
|
||
|
||
douyinIdCell.textContent = account.douyin_id || '未知抖音号';
|
||
|
||
row.appendChild(douyinIdCell);
|
||
|
||
|
||
|
||
// 到期时间
|
||
|
||
const expiryCell = document.createElement('td');
|
||
|
||
expiryCell.classList.add('py-3', 'px-4', 'text-sm');
|
||
|
||
expiryCell.innerHTML = `${account.expiry_time || '未知到期时间'} <a href="#" class="text-primary hover:underline ml-2">充值</a>`;
|
||
|
||
row.appendChild(expiryCell);
|
||
|
||
|
||
|
||
// 主播昵称
|
||
|
||
const nicknameCell = document.createElement('td');
|
||
|
||
nicknameCell.classList.add('py-3', 'px-4', 'text-sm');
|
||
|
||
nicknameCell.textContent = account.nickname || '未知昵称';
|
||
|
||
row.appendChild(nicknameCell);
|
||
|
||
|
||
|
||
// 操作 (总是显示监听和删除按钮)
|
||
|
||
const actionsCell = document.createElement('td');
|
||
|
||
actionsCell.classList.add('py-3', 'px-4', 'text-sm', 'whitespace-nowrap'); // 防止按钮换行
|
||
|
||
|
||
|
||
// 总是显示监听和删除按钮
|
||
|
||
actionsCell.innerHTML = `
|
||
|
||
<button class="text-primary hover:underline mr-3" onclick="monitorAccount('${account.url_part}', '${account.nickname}')">
|
||
|
||
<i class="fa-solid fa-play mr-1"></i>监听
|
||
|
||
</button>
|
||
|
||
<button class="text-danger hover:underline mr-3" onclick="deleteAccount('${account.url_part}', '${account.nickname}', '${account.douyin_id}')">
|
||
|
||
<i class="fa-solid fa-trash-can mr-1"></i>删除
|
||
|
||
</button>
|
||
|
||
`;
|
||
|
||
// 移除根据状态切换按钮的逻辑
|
||
|
||
|
||
|
||
row.appendChild(actionsCell);
|
||
|
||
tbody.appendChild(row);
|
||
|
||
});
|
||
|
||
|
||
|
||
// 更新总数信息
|
||
|
||
countInfo.textContent = `第 1-${accounts.length} 条/总共 ${accounts.length} 条`;
|
||
|
||
|
||
|
||
// 移除根据是否有正在监听的账号,启用或禁用其他监听按钮的逻辑
|
||
|
||
// if (currentlyMonitoringUrlPart) { toggleOtherMonitorButtons(false, currentlyMonitoringUrlPart); } else { toggleOtherMonitorButtons(true, null); } // 移除此段
|
||
|
||
|
||
|
||
console.log('表格更新完成。');
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
// --- 页面加载完成后执行 ---
|
||
|
||
|
||
|
||
// 等待DOM完全加载
|
||
|
||
window.addEventListener('DOMContentLoaded', (event) => {
|
||
|
||
console.log('DOM完全加载完成');
|
||
|
||
|
||
|
||
// 创建同步的 XMLHttpRequest
|
||
|
||
const xhr = new XMLHttpRequest();
|
||
|
||
xhr.open('GET', 'user_Settings_Options.json', false); // 第三个参数设为 false 表示同步请求
|
||
|
||
xhr.send();
|
||
|
||
|
||
|
||
if (xhr.status === 200) {
|
||
|
||
// console.log('配置文件已加载');
|
||
|
||
// 解析 JSON 并写入 localStorage
|
||
|
||
const data = JSON.parse(xhr.responseText);
|
||
|
||
// console.log('配置数据:', data);
|
||
|
||
localStorage.setItem('smartBodyguardSettings', JSON.stringify(data));
|
||
|
||
console.log('配置已同步保存到 localStorage');
|
||
|
||
// showSuccessModal('配置已同步');
|
||
|
||
window.addEventListener('pywebviewready', function () {
|
||
|
||
console.log('window.addEventListener.pywebview 已就绪,重新发送设置。');
|
||
|
||
// hideEditModal();
|
||
|
||
|
||
|
||
// var container = document.getElementById('pywebview-status')
|
||
|
||
// container.innerHTML = '<i>pywebview</i> is ready'
|
||
|
||
// 在 pywebview 就绪后发送当前设置
|
||
|
||
pywebview.api.get_localStorage(localStorage.getItem('smartBodyguardSettings'))
|
||
|
||
|
||
|
||
});
|
||
|
||
|
||
|
||
// Notify pywebview (if available) - assuming this is how you communicate
|
||
if (window.pywebview) {
|
||
console.log('window.pywebview.pywebview 已就绪,重新发送设置。');
|
||
// var container = document.getElementById('pywebview-status')
|
||
// container.innerHTML = '<i>pywebview</i> is ready'
|
||
pywebview.api.get_localStorage(localStorage.getItem('smartBodyguardSettings'));
|
||
} else {
|
||
// Fallback for pywebview not being ready immediately (already handled by event listener above)
|
||
console.log('pywebview 未就绪,跳过 API 调用。');
|
||
}
|
||
|
||
} else {
|
||
|
||
// This throws an error but doesn't stop script execution immediately
|
||
|
||
console.error(`请求配置文件失败,状态码: ${xhr.status}`);
|
||
|
||
// You might want to handle this failure more gracefully
|
||
|
||
}
|
||
|
||
|
||
|
||
// 等待pywebview API 就绪
|
||
|
||
if (window.pywebview) {
|
||
|
||
console.log('pywebview API 已就绪。');
|
||
|
||
// 在API就绪后,立即调用Python方法获取初始账号列表并渲染
|
||
|
||
window.pywebview.api.get_initial_accounts()
|
||
|
||
.then(() => {
|
||
|
||
console.log('请求初始账号列表已发送到 Python。');
|
||
|
||
// updateAccountTable will be called by Python when data is ready
|
||
|
||
})
|
||
|
||
.catch(error => {
|
||
|
||
console.error('调用Python get_initial_accounts出错:', error);
|
||
|
||
// Optionally display an error in the UI
|
||
|
||
alert(`获取初始账号列表失败: ${error}`);
|
||
|
||
});
|
||
|
||
} else {
|
||
|
||
console.warn('pywebview API 尚未就绪,等待 pywebviewready 事件...');
|
||
|
||
// 如果API未就绪,监听 'pywebviewready' 事件
|
||
|
||
window.addEventListener('pywebviewready', function () {
|
||
|
||
console.log('pywebviewready 事件触发,API 已就绪。');
|
||
|
||
window.pywebview.api.get_initial_accounts()
|
||
|
||
.then(() => {
|
||
|
||
console.log('请求初始账号列表已发送到 Python (通过事件监听)。');
|
||
|
||
})
|
||
|
||
.catch(error => {
|
||
|
||
console.error('调用Python get_initial_accounts出错 (通过事件监听):', error);
|
||
|
||
alert(`获取初始账号列表失败 (通过事件监听): ${error}`);
|
||
|
||
});
|
||
|
||
});
|
||
|
||
}
|
||
|
||
|
||
|
||
// 添加一个事件监听,尝试在窗口关闭前停止监听(不保证总能触发)
|
||
|
||
// 这是一个尽力而为的尝试,更可靠的方式是在 Python 应用关闭时处理
|
||
|
||
window.addEventListener('beforeunload', (event) => {
|
||
|
||
console.log('窗口即将关闭,尝试停止监听...');
|
||
|
||
// 移除基于currentlyMonitoringUrlPart的判断
|
||
|
||
if (window.pywebview && window.pywebview.api && window.pywebview.api.stop_monitoring) {
|
||
|
||
// 使用 async/await 或 Promise 链,但 beforeunload 不支持异步
|
||
|
||
// 简单的同步调用可能会卡住关闭,或者不执行
|
||
|
||
// 更好的方法是在 Python 应用级别监听窗口关闭事件并停止线程
|
||
|
||
// 这里只是一个示例性的尝试,可能无效
|
||
|
||
// window.pywebview.api.stop_monitoring(); // 同步调用,不推荐在 beforeunload
|
||
|
||
}
|
||
|
||
// event.preventDefault(); // 取消关闭(用于调试)
|
||
|
||
// event.returnValue = ''; // 某些浏览器需要这个来显示提示
|
||
|
||
});
|
||
|
||
|
||
|
||
});
|
||
|
||
|
||
|
||
</script>
|
||
|
||
</body>
|
||
|
||
</html>
|