563 lines
27 KiB
HTML
563 lines
27 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',
|
|||
|
},
|
|||
|
fontFamily: {
|
|||
|
inter: ['Inter', 'system-ui', 'sans-serif'],
|
|||
|
},
|
|||
|
spacing: { // 添加或修改 gap 的定义,确保 gap-4 存在
|
|||
|
'4': '1rem', // 16px - 列表项和表头的主要列间距
|
|||
|
'3': '1rem', // 12px - 保留原有的gap-3 (侧边栏等使用)
|
|||
|
'8': '3rem', // 32px - 预留给按钮占位 (操作按钮区域宽度)
|
|||
|
'10': '3rem', // 40px - 预留给头像占位 (头像宽度)
|
|||
|
// 根据需要添加更多间距值
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</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;
|
|||
|
}
|
|||
|
|
|||
|
.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 {
|
|||
|
position: sticky;
|
|||
|
top: 20px;
|
|||
|
z-index: 10;
|
|||
|
}
|
|||
|
}
|
|||
|
</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="Logging.html" class="sidebar-item"><i class="fa-solid fa-file-lines"></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>
|
|||
|
<!-- 全局停止监听按钮 (保留,但逻辑可能依赖后端判断是否有监听任务) -->
|
|||
|
</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');
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// --- 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的删除账号方法
|
|||
|
// 接收 urlPart, nickname, douyinId 作为参数
|
|||
|
function deleteAccount(urlPart, nickname, douyinId) {
|
|||
|
console.log('前端请求删除账号:', urlPart, '昵称:', nickname, '抖音号:', douyinId);
|
|||
|
|
|||
|
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端在无法删除正在监听的账号时可能已经弹出了提示,这里可以不重复弹
|
|||
|
}
|
|||
|
})
|
|||
|
.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 (window.pywebview) {
|
|||
|
// 找到对应的监听按钮并禁用
|
|||
|
const monitorButton = document.querySelector(`tr[data-url-part="${urlPart}"] button.text-primary`);
|
|||
|
if (monitorButton) {
|
|||
|
monitorButton.disabled = true;
|
|||
|
monitorButton.classList.add('opacity-50', 'cursor-not-allowed');
|
|||
|
}
|
|||
|
|
|||
|
window.pywebview.api.monitor_account(urlPart, nickname)
|
|||
|
.then(success => {
|
|||
|
if (success) {
|
|||
|
console.log(`Python 成功启动账号 ${urlPart} 的监听流程。`);
|
|||
|
// UI更新将通过 updateButtonState 由Python触发
|
|||
|
} else {
|
|||
|
console.warn(`Python 启动账号 ${urlPart} 监听失败。`);
|
|||
|
// Python 在失败时可能已经弹窗提示,这里可以不处理或加一个通用提示
|
|||
|
// alert('启动监听失败,请检查日志或稍后再试。');
|
|||
|
}
|
|||
|
})
|
|||
|
.catch(error => {
|
|||
|
console.error(`调用Python monitor_account 出错,url_part: ${urlPart}:`, error);
|
|||
|
alert(`启动监听时发生错误: ${error}`);
|
|||
|
})
|
|||
|
.finally(() => {
|
|||
|
// 无论成功或失败,重新启用按钮
|
|||
|
if (monitorButton) {
|
|||
|
monitorButton.disabled = false;
|
|||
|
monitorButton.classList.remove('opacity-50', 'cursor-not-allowed');
|
|||
|
}
|
|||
|
});
|
|||
|
} else {
|
|||
|
console.error('pywebview API 未就绪.');
|
|||
|
alert('应用正在启动中,请稍后再试。');
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// 调用Python的停止监听方法
|
|||
|
function stopMonitoring(urlPart) {
|
|||
|
console.log('前端请求停止监听账号:', urlPart);
|
|||
|
if (window.pywebview) {
|
|||
|
window.pywebview.api.stop_monitoring(urlPart)
|
|||
|
.then(success => {
|
|||
|
if (success) {
|
|||
|
console.log(`Python 成功停止账号 ${urlPart} 的监听流程。`);
|
|||
|
// UI更新将通过 updateButtonState 由Python触发
|
|||
|
} else {
|
|||
|
console.warn(`Python 停止账号 ${urlPart} 监听失败。`);
|
|||
|
// Python 在失败时可能已经弹窗提示,这里可以不处理或加一个通用提示
|
|||
|
// alert('停止监听失败,请检查日志或稍后再试。');
|
|||
|
}
|
|||
|
})
|
|||
|
.catch(error => {
|
|||
|
console.error(`调用Python stop_monitoring 出错,url_part: ${urlPart}:`, error);
|
|||
|
alert(`停止监听时发生错误: ${error}`);
|
|||
|
});
|
|||
|
} else {
|
|||
|
console.error('pywebview API 未就绪.');
|
|||
|
alert('应用正在启动中,请稍后再试。');
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 添加 JavaScript 函数来更新按钮状态
|
|||
|
function updateButtonState(urlPart, isMonitoring) {
|
|||
|
console.log(`更新按钮状态: url_part=${urlPart}, isMonitoring=${isMonitoring}`);
|
|||
|
const row = document.querySelector(`tr[data-url-part="${urlPart}"]`);
|
|||
|
if (row) {
|
|||
|
const actionsCell = row.querySelector('td:last-child');
|
|||
|
if (actionsCell) {
|
|||
|
let buttonHTML = '';
|
|||
|
if (isMonitoring) {
|
|||
|
// 如果正在监听,显示停止按钮
|
|||
|
buttonHTML = `
|
|||
|
<button class="text-warning hover:underline mr-3" onclick="stopMonitoring('${urlPart}')">
|
|||
|
<i class="fa-solid fa-stop mr-1"></i>停止
|
|||
|
</button>
|
|||
|
<button class="text-danger hover:underline mr-3" onclick="deleteAccount('${urlPart}', '${row.dataset.nickname}', '${row.dataset.douyinId}')">
|
|||
|
<i class="fa-solid fa-trash-can mr-1"></i>删除
|
|||
|
</button>
|
|||
|
`;
|
|||
|
} else {
|
|||
|
// 如果没有在监听,显示监听按钮
|
|||
|
buttonHTML = `
|
|||
|
<button class="text-primary hover:underline mr-3" onclick="monitorAccount('${urlPart}', '${row.dataset.nickname}')">
|
|||
|
<i class="fa-solid fa-play mr-1"></i>监听
|
|||
|
</button>
|
|||
|
<button class="text-danger hover:underline mr-3" onclick="deleteAccount('${urlPart}', '${row.dataset.nickname}', '${row.dataset.douyinId}')">
|
|||
|
<i class="fa-solid fa-trash-can mr-1"></i>删除
|
|||
|
</button>
|
|||
|
`;
|
|||
|
}
|
|||
|
actionsCell.innerHTML = buttonHTML;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 调用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 调用,用于更新账号列表
|
|||
|
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 条';
|
|||
|
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} 条`;
|
|||
|
|
|||
|
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) {
|
|||
|
// 解析 JSON 并写入 localStorage
|
|||
|
const data = JSON.parse(xhr.responseText);
|
|||
|
localStorage.setItem('smartBodyguardSettings', JSON.stringify(data));
|
|||
|
console.log('配置已同步保存到 localStorage');
|
|||
|
|
|||
|
// 等待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}`);
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
} else {
|
|||
|
console.error(`请求配置文件失败,状态码: ${xhr.status}`);
|
|||
|
}
|
|||
|
|
|||
|
// 添加一个事件监听,尝试在窗口关闭前停止监听(不保证总能触发)
|
|||
|
window.addEventListener('beforeunload', (event) => {
|
|||
|
console.log('窗口即将关闭,尝试停止监听...');
|
|||
|
if (window.pywebview && window.pywebview.api && window.pywebview.api.stop_monitoring) {
|
|||
|
// 这里只是一个示例性的尝试,可能无效
|
|||
|
// window.pywebview.api.stop_monitoring(); // 同步调用,不推荐在 beforeunload
|
|||
|
}
|
|||
|
// event.preventDefault(); // 取消关闭(用于调试)
|
|||
|
// event.returnValue = ''; // 某些浏览器需要这个来显示提示
|
|||
|
});
|
|||
|
});
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
|
|||
|
</html>
|