diff --git a/static/js/app-modular.js b/static/js/app-modular.js
deleted file mode 100644
index 3234da5..0000000
--- a/static/js/app-modular.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * 闲鱼自动回复系统 - 模块化主入口文件
- *
- * 此文件负责引入所有功能模块,替代原来的单一app.js文件
- *
- * 模块结构:
- * - globals.js: 全局变量
- * - utils.js: 通用工具函数
- * - navigation.js: 页面导航功能
- * - dashboard.js: 仪表盘管理
- * - keyword-cache.js: 关键词缓存管理
- * - account-list.js: 账号列表管理
- * - keywords.js: 关键词管理
- * - cookies.js: Cookie管理
- * - main-features.js: 主要功能
- * - all-other-features.js: 所有其他功能
- * - init.js: 应用初始化
- */
-
-// 注意:在HTML中需要按以下顺序引入所有模块文件:
-/*
-
-
-
-
-
-
-
-
-
-
-
-*/
-
-// 模块加载完成后的初始化检查
-document.addEventListener('DOMContentLoaded', function() {
- console.log('闲鱼自动回复系统 - 模块化版本已加载');
-
- // 检查关键函数是否存在
- const requiredFunctions = [
- 'showSection',
- 'loadDashboard',
- 'loadCookies',
- 'refreshAccountList',
- 'loadAccountKeywords',
- 'showToast',
- 'toggleLoading'
- ];
-
- const missingFunctions = requiredFunctions.filter(func => typeof window[func] !== 'function');
-
- if (missingFunctions.length > 0) {
- console.error('缺少必要的函数:', missingFunctions);
- console.error('请检查模块文件是否正确加载');
- } else {
- console.log('所有必要的函数已加载完成');
- }
-});
-
-// 导出模块信息(用于调试)
-window.moduleInfo = {
- version: '1.0.0',
- modules: [
- 'globals',
- 'utils',
- 'navigation',
- 'dashboard',
- 'keyword-cache',
- 'account-list',
- 'keywords',
- 'cookies',
- 'main-features',
- 'all-other-features',
- 'init'
- ],
- loadedAt: new Date().toISOString()
-};
diff --git a/static/js/modules/account-list.js b/static/js/modules/account-list.js
deleted file mode 100644
index 2888fbf..0000000
--- a/static/js/modules/account-list.js
+++ /dev/null
@@ -1,132 +0,0 @@
-// ==================== 账号列表管理 ====================
-
-// 刷新账号列表(用于自动回复页面)
-async function refreshAccountList() {
- try {
- toggleLoading(true);
-
- // 获取账号列表
- const response = await fetch(`${apiBase}/cookies/details`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- if (response.ok) {
- const accounts = await response.json();
- const select = document.getElementById('accountSelect');
- select.innerHTML = '';
-
- // 为每个账号获取关键词数量
- const accountsWithKeywords = await Promise.all(
- accounts.map(async (account) => {
- try {
- const keywordsResponse = await fetch(`${apiBase}/keywords/${account.id}`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- if (keywordsResponse.ok) {
- const keywordsData = await keywordsResponse.json();
- return {
- ...account,
- keywords: keywordsData,
- keywordCount: keywordsData.length
- };
- } else {
- return {
- ...account,
- keywordCount: 0
- };
- }
- } catch (error) {
- console.error(`获取账号 ${account.id} 关键词失败:`, error);
- return {
- ...account,
- keywordCount: 0
- };
- }
- })
- );
-
- // 渲染账号选项(显示所有账号,但标识禁用状态)
- if (accountsWithKeywords.length === 0) {
- select.innerHTML = '';
- return;
- }
-
- // 分组显示:先显示启用的账号,再显示禁用的账号
- const enabledAccounts = accountsWithKeywords.filter(account => {
- const enabled = account.enabled === undefined ? true : account.enabled;
- console.log(`账号 ${account.id} 过滤状态: enabled=${account.enabled}, 判断为启用=${enabled}`);
- return enabled;
- });
- const disabledAccounts = accountsWithKeywords.filter(account => {
- const enabled = account.enabled === undefined ? true : account.enabled;
- return !enabled;
- });
-
- // 渲染启用的账号
- enabledAccounts.forEach(account => {
- const option = document.createElement('option');
- option.value = account.id;
-
- // 根据关键词数量显示不同的图标和样式
- let icon = '📝';
- let status = '';
- if (account.keywordCount === 0) {
- icon = '⚪';
- status = ' (未配置)';
- } else if (account.keywordCount >= 5) {
- icon = '🟢';
- status = ` (${account.keywordCount} 个关键词)`;
- } else {
- icon = '🟡';
- status = ` (${account.keywordCount} 个关键词)`;
- }
-
- option.textContent = `${icon} ${account.id}${status}`;
- select.appendChild(option);
- });
-
- // 如果有禁用的账号,添加分隔线和禁用账号
- if (disabledAccounts.length > 0) {
- // 添加分隔线
- const separatorOption = document.createElement('option');
- separatorOption.disabled = true;
- separatorOption.textContent = `--- 禁用账号 (${disabledAccounts.length} 个) ---`;
- select.appendChild(separatorOption);
-
- // 渲染禁用的账号
- disabledAccounts.forEach(account => {
- const option = document.createElement('option');
- option.value = account.id;
-
- // 禁用账号使用特殊图标和样式
- let icon = '🔴';
- let status = '';
- if (account.keywordCount === 0) {
- status = ' (未配置) [已禁用]';
- } else {
- status = ` (${account.keywordCount} 个关键词) [已禁用]`;
- }
-
- option.textContent = `${icon} ${account.id}${status}`;
- option.style.color = '#6b7280';
- option.style.fontStyle = 'italic';
- select.appendChild(option);
- });
- }
-
- console.log('账号列表刷新完成,关键词统计:', accountsWithKeywords.map(a => ({ id: a.id, keywords: a.keywordCount })));
- } else {
- showToast('获取账号列表失败', 'danger');
- }
- } catch (error) {
- console.error('刷新账号列表失败:', error);
- showToast('刷新账号列表失败', 'danger');
- } finally {
- toggleLoading(false);
- }
-}
diff --git a/static/js/modules/all-other-features.js b/static/js/modules/all-other-features.js
deleted file mode 100644
index e0be265..0000000
--- a/static/js/modules/all-other-features.js
+++ /dev/null
@@ -1,197 +0,0 @@
-// ==================== 所有其他功能模块 ====================
-// 此文件包含原app.js中除已拆分模块外的所有其他功能
-
-// ==================== 默认回复管理功能 ====================
-
-// 打开默认回复管理器
-async function openDefaultReplyManager() {
- try {
- await loadDefaultReplies();
- const modal = new bootstrap.Modal(document.getElementById('defaultReplyModal'));
- modal.show();
- } catch (error) {
- console.error('打开默认回复管理器失败:', error);
- showToast('打开默认回复管理器失败', 'danger');
- }
-}
-
-// 加载默认回复列表
-async function loadDefaultReplies() {
- try {
- // 获取所有账号
- const accountsResponse = await fetch(`${apiBase}/cookies`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- if (!accountsResponse.ok) {
- throw new Error('获取账号列表失败');
- }
-
- const accounts = await accountsResponse.json();
-
- // 获取所有默认回复设置
- const repliesResponse = await fetch(`${apiBase}/default-replies`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- let defaultReplies = {};
- if (repliesResponse.ok) {
- defaultReplies = await repliesResponse.json();
- }
-
- renderDefaultRepliesList(accounts, defaultReplies);
- } catch (error) {
- console.error('加载默认回复列表失败:', error);
- showToast('加载默认回复列表失败', 'danger');
- }
-}
-
-// 渲染默认回复列表
-function renderDefaultRepliesList(accounts, defaultReplies) {
- const tbody = document.getElementById('defaultReplyTableBody');
- tbody.innerHTML = '';
-
- if (accounts.length === 0) {
- tbody.innerHTML = `
-
-
-
- 暂无账号数据
- 请先添加账号
- |
-
- `;
- return;
- }
-
- accounts.forEach(accountId => {
- const replySettings = defaultReplies[accountId] || { enabled: false, reply_content: '' };
- const tr = document.createElement('tr');
-
- // 状态标签
- const statusBadge = replySettings.enabled ?
- '启用' :
- '禁用';
-
- // 回复内容预览
- let contentPreview = replySettings.reply_content || '未设置';
- if (contentPreview.length > 50) {
- contentPreview = contentPreview.substring(0, 50) + '...';
- }
-
- tr.innerHTML = `
-
- ${accountId}
- |
- ${statusBadge} |
-
-
- ${contentPreview}
-
- |
-
-
-
-
-
- |
- `;
-
- tbody.appendChild(tr);
- });
-}
-
-// 编辑默认回复
-async function editDefaultReply(accountId) {
- try {
- // 获取当前设置
- const response = await fetch(`${apiBase}/default-replies/${accountId}`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- let settings = { enabled: false, reply_content: '' };
- if (response.ok) {
- settings = await response.json();
- }
-
- // 填充编辑表单
- document.getElementById('editAccountId').value = accountId;
- document.getElementById('editAccountIdDisplay').value = accountId;
- document.getElementById('editDefaultReplyEnabled').checked = settings.enabled;
- document.getElementById('editReplyContent').value = settings.reply_content || '';
-
- // 根据启用状态显示/隐藏内容输入框
- toggleReplyContentVisibility();
-
- // 显示编辑模态框
- const modal = new bootstrap.Modal(document.getElementById('editDefaultReplyModal'));
- modal.show();
- } catch (error) {
- console.error('获取默认回复设置失败:', error);
- showToast('获取默认回复设置失败', 'danger');
- }
-}
-
-// 切换回复内容输入框的显示/隐藏
-function toggleReplyContentVisibility() {
- const enabled = document.getElementById('editDefaultReplyEnabled').checked;
- const contentGroup = document.getElementById('editReplyContentGroup');
- contentGroup.style.display = enabled ? 'block' : 'none';
-}
-
-// 保存默认回复设置
-async function saveDefaultReply() {
- try {
- const accountId = document.getElementById('editAccountId').value;
- const enabled = document.getElementById('editDefaultReplyEnabled').checked;
- const replyContent = document.getElementById('editReplyContent').value;
-
- if (enabled && !replyContent.trim()) {
- showToast('启用默认回复时必须设置回复内容', 'warning');
- return;
- }
-
- const data = {
- enabled: enabled,
- reply_content: enabled ? replyContent : null
- };
-
- const response = await fetch(`${apiBase}/default-replies/${accountId}`, {
- method: 'PUT',
- headers: {
- 'Authorization': `Bearer ${authToken}`,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(data)
- });
-
- if (response.ok) {
- showToast('默认回复设置保存成功', 'success');
- bootstrap.Modal.getInstance(document.getElementById('editDefaultReplyModal')).hide();
- loadDefaultReplies(); // 刷新列表
- loadCookies(); // 刷新账号列表以更新默认回复状态显示
- } else {
- const error = await response.text();
- showToast(`保存失败: ${error}`, 'danger');
- }
- } catch (error) {
- console.error('保存默认回复设置失败:', error);
- showToast('保存默认回复设置失败', 'danger');
- }
-}
-
-// 测试默认回复
-function testDefaultReply(accountId) {
- console.log('测试默认回复功能,账号ID:', accountId);
- showToast('测试功能开发中...', 'info');
-}
diff --git a/static/js/modules/cookies.js b/static/js/modules/cookies.js
deleted file mode 100644
index 60920fb..0000000
--- a/static/js/modules/cookies.js
+++ /dev/null
@@ -1,347 +0,0 @@
-// ==================== Cookie管理 ====================
-
-// 加载Cookie列表
-async function loadCookies() {
- try {
- toggleLoading(true);
- const tbody = document.querySelector('#cookieTable tbody');
- tbody.innerHTML = '';
-
- const cookieDetails = await fetchJSON(apiBase + '/cookies/details');
-
- if (cookieDetails.length === 0) {
- tbody.innerHTML = `
-
-
-
- 暂无账号
- 请添加新的闲鱼账号开始使用
- |
-
- `;
- return;
- }
-
- // 为每个账号获取关键词数量和默认回复设置并渲染
- const accountsWithKeywords = await Promise.all(
- cookieDetails.map(async (cookie) => {
- try {
- // 获取关键词数量
- const keywordsResponse = await fetch(`${apiBase}/keywords/${cookie.id}`, {
- headers: { 'Authorization': `Bearer ${authToken}` }
- });
-
- let keywordCount = 0;
- if (keywordsResponse.ok) {
- const keywordsData = await keywordsResponse.json();
- keywordCount = keywordsData.length;
- }
-
- // 获取默认回复设置
- const defaultReplyResponse = await fetch(`${apiBase}/default-replies/${cookie.id}`, {
- headers: { 'Authorization': `Bearer ${authToken}` }
- });
-
- let defaultReply = { enabled: false, reply_content: '' };
- if (defaultReplyResponse.ok) {
- defaultReply = await defaultReplyResponse.json();
- }
-
- // 获取AI回复设置
- const aiReplyResponse = await fetch(`${apiBase}/ai-reply-settings/${cookie.id}`, {
- headers: { 'Authorization': `Bearer ${authToken}` }
- });
-
- let aiReply = { ai_enabled: false, model_name: 'qwen-plus' };
- if (aiReplyResponse.ok) {
- aiReply = await aiReplyResponse.json();
- }
-
- return {
- ...cookie,
- keywordCount: keywordCount,
- defaultReply: defaultReply,
- aiReply: aiReply
- };
- } catch (error) {
- return {
- ...cookie,
- keywordCount: 0,
- defaultReply: { enabled: false, reply_content: '' },
- aiReply: { ai_enabled: false, model_name: 'qwen-plus' }
- };
- }
- })
- );
-
- accountsWithKeywords.forEach(cookie => {
- // 使用数据库中的实际状态,默认为启用
- const isEnabled = cookie.enabled === undefined ? true : cookie.enabled;
-
- console.log(`账号 ${cookie.id} 状态: enabled=${cookie.enabled}, isEnabled=${isEnabled}`); // 调试信息
-
- const tr = document.createElement('tr');
- tr.className = `account-row ${isEnabled ? 'enabled' : 'disabled'}`;
- // 默认回复状态标签
- const defaultReplyBadge = cookie.defaultReply.enabled ?
- '启用' :
- '禁用';
-
- // AI回复状态标签
- const aiReplyBadge = cookie.aiReply.ai_enabled ?
- 'AI启用' :
- 'AI禁用';
-
- // 自动确认发货状态(默认开启)
- const autoConfirm = cookie.auto_confirm === undefined ? true : cookie.auto_confirm;
-
- tr.innerHTML = `
-
-
- ${cookie.id}
-
- |
-
-
- ${cookie.value || '未设置'}
-
- |
-
-
- ${cookie.keywordCount} 个关键词
-
- |
-
-
-
-
-
-
-
- |
-
- ${defaultReplyBadge}
- |
-
- ${aiReplyBadge}
- |
-
-
-
-
-
-
-
- |
-
-
-
-
-
-
-
-
- |
- `;
- tbody.appendChild(tr);
- });
-
- // 为Cookie值添加点击复制功能
- document.querySelectorAll('.cookie-value').forEach(element => {
- element.style.cursor = 'pointer';
- element.addEventListener('click', function() {
- const cookieValue = this.textContent;
- if (cookieValue && cookieValue !== '未设置') {
- navigator.clipboard.writeText(cookieValue).then(() => {
- showToast('Cookie已复制到剪贴板', 'success');
- }).catch(() => {
- showToast('复制失败,请手动复制', 'error');
- });
- }
- });
- });
-
- } catch (err) {
- // 错误已在fetchJSON中处理
- } finally {
- toggleLoading(false);
- }
-}
-
-// 复制Cookie
-function copyCookie(id, value) {
- if (!value || value === '未设置') {
- showToast('该账号暂无Cookie值', 'warning');
- return;
- }
-
- navigator.clipboard.writeText(value).then(() => {
- showToast(`账号 "${id}" 的Cookie已复制到剪贴板`, 'success');
- }).catch(() => {
- // 降级方案:创建临时文本框
- const textArea = document.createElement('textarea');
- textArea.value = value;
- document.body.appendChild(textArea);
- textArea.select();
- try {
- document.execCommand('copy');
- showToast(`账号 "${id}" 的Cookie已复制到剪贴板`, 'success');
- } catch (err) {
- showToast('复制失败,请手动复制', 'error');
- }
- document.body.removeChild(textArea);
- });
-}
-
-// 删除Cookie
-async function delCookie(id) {
- if (!confirm(`确定要删除账号 "${id}" 吗?此操作不可恢复。`)) return;
-
- try {
- await fetchJSON(apiBase + `/cookies/${id}`, { method: 'DELETE' });
- showToast(`账号 "${id}" 已删除`, 'success');
- loadCookies();
- } catch (err) {
- // 错误已在fetchJSON中处理
- }
-}
-
-// 内联编辑Cookie
-function editCookieInline(id, currentValue) {
- const row = event.target.closest('tr');
- const cookieValueCell = row.querySelector('.cookie-value');
- const originalContent = cookieValueCell.innerHTML;
-
- // 存储原始数据到全局变量,避免HTML注入问题
- window.editingCookieData = {
- id: id,
- originalContent: originalContent,
- originalValue: currentValue || ''
- };
-
- // 创建编辑界面容器
- const editContainer = document.createElement('div');
- editContainer.className = 'd-flex gap-2';
-
- // 创建输入框
- const input = document.createElement('input');
- input.type = 'text';
- input.className = 'form-control form-control-sm';
- input.id = `edit-${id}`;
- input.value = currentValue || '';
- input.placeholder = '输入新的Cookie值';
-
- // 创建保存按钮
- const saveBtn = document.createElement('button');
- saveBtn.className = 'btn btn-sm btn-success';
- saveBtn.title = '保存';
- saveBtn.innerHTML = '';
- saveBtn.onclick = () => saveCookieInline(id);
-
- // 创建取消按钮
- const cancelBtn = document.createElement('button');
- cancelBtn.className = 'btn btn-sm btn-secondary';
- cancelBtn.title = '取消';
- cancelBtn.innerHTML = '';
- cancelBtn.onclick = () => cancelCookieEdit(id);
-
- // 组装编辑界面
- editContainer.appendChild(input);
- editContainer.appendChild(saveBtn);
- editContainer.appendChild(cancelBtn);
-
- // 替换原内容
- cookieValueCell.innerHTML = '';
- cookieValueCell.appendChild(editContainer);
-
- // 聚焦输入框
- input.focus();
- input.select();
-
- // 添加键盘事件监听
- input.addEventListener('keydown', function (e) {
- if (e.key === 'Enter') {
- e.preventDefault();
- saveCookieInline(id);
- } else if (e.key === 'Escape') {
- e.preventDefault();
- cancelCookieEdit(id);
- }
- });
-
- // 禁用该行的其他按钮
- const actionButtons = row.querySelectorAll('.btn-group button');
- actionButtons.forEach(btn => btn.disabled = true);
-}
-
-// 保存内联编辑的Cookie
-async function saveCookieInline(id) {
- const input = document.getElementById(`edit-${id}`);
- const newValue = input.value.trim();
-
- if (!newValue) {
- showToast('Cookie值不能为空', 'warning');
- return;
- }
-
- try {
- toggleLoading(true);
-
- await fetchJSON(apiBase + `/cookies/${id}`, {
- method: 'PUT',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- id: id,
- value: newValue
- })
- });
-
- showToast(`账号 "${id}" Cookie已更新`, 'success');
- loadCookies(); // 重新加载列表
-
- } catch (err) {
- console.error('Cookie更新失败:', err);
- showToast(`Cookie更新失败: ${err.message || '未知错误'}`, 'danger');
- // 恢复原内容
- cancelCookieEdit(id);
- } finally {
- toggleLoading(false);
- }
-}
-
-// 取消Cookie编辑
-function cancelCookieEdit(id) {
- if (!window.editingCookieData || window.editingCookieData.id !== id) {
- console.error('编辑数据不存在');
- return;
- }
-
- const row = document.querySelector(`#edit-${id}`).closest('tr');
- const cookieValueCell = row.querySelector('.cookie-value');
-
- // 恢复原内容
- cookieValueCell.innerHTML = window.editingCookieData.originalContent;
-
- // 恢复按钮状态
- const actionButtons = row.querySelectorAll('.btn-group button');
- actionButtons.forEach(btn => btn.disabled = false);
-
- // 清理全局数据
- delete window.editingCookieData;
-}
diff --git a/static/js/modules/dashboard.js b/static/js/modules/dashboard.js
deleted file mode 100644
index e163fae..0000000
--- a/static/js/modules/dashboard.js
+++ /dev/null
@@ -1,141 +0,0 @@
-// ==================== 仪表盘管理 ====================
-
-// 加载仪表盘数据
-async function loadDashboard() {
- try {
- toggleLoading(true);
-
- // 获取账号列表
- const cookiesResponse = await fetch(`${apiBase}/cookies/details`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- if (cookiesResponse.ok) {
- const cookiesData = await cookiesResponse.json();
-
- // 为每个账号获取关键词信息
- const accountsWithKeywords = await Promise.all(
- cookiesData.map(async (account) => {
- try {
- const keywordsResponse = await fetch(`${apiBase}/keywords/${account.id}`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- if (keywordsResponse.ok) {
- const keywordsData = await keywordsResponse.json();
- return {
- ...account,
- keywords: keywordsData,
- keywordCount: keywordsData.length
- };
- } else {
- return {
- ...account,
- keywords: [],
- keywordCount: 0
- };
- }
- } catch (error) {
- console.error(`获取账号 ${account.id} 关键词失败:`, error);
- return {
- ...account,
- keywords: [],
- keywordCount: 0
- };
- }
- })
- );
-
- dashboardData.accounts = accountsWithKeywords;
-
- // 计算统计数据
- let totalKeywords = 0;
- let activeAccounts = 0;
- let enabledAccounts = 0;
-
- accountsWithKeywords.forEach(account => {
- const keywordCount = account.keywordCount || 0;
- const isEnabled = account.enabled === undefined ? true : account.enabled;
-
- if (isEnabled) {
- enabledAccounts++;
- totalKeywords += keywordCount;
- if (keywordCount > 0) {
- activeAccounts++;
- }
- }
- });
-
- dashboardData.totalKeywords = totalKeywords;
-
- // 更新仪表盘显示
- updateDashboardStats(accountsWithKeywords.length, totalKeywords, enabledAccounts);
- updateDashboardAccountsList(accountsWithKeywords);
- }
- } catch (error) {
- console.error('加载仪表盘数据失败:', error);
- showToast('加载仪表盘数据失败', 'danger');
- } finally {
- toggleLoading(false);
- }
-}
-
-// 更新仪表盘统计数据
-function updateDashboardStats(totalAccounts, totalKeywords, enabledAccounts) {
- document.getElementById('totalAccounts').textContent = totalAccounts;
- document.getElementById('totalKeywords').textContent = totalKeywords;
- document.getElementById('activeAccounts').textContent = enabledAccounts;
-}
-
-// 更新仪表盘账号列表
-function updateDashboardAccountsList(accounts) {
- const tbody = document.getElementById('dashboardAccountsList');
- tbody.innerHTML = '';
-
- if (accounts.length === 0) {
- tbody.innerHTML = `
-
-
-
- 暂无账号数据
- |
-
- `;
- return;
- }
-
- accounts.forEach(account => {
- const keywordCount = account.keywordCount || 0;
- const isEnabled = account.enabled === undefined ? true : account.enabled;
-
- let status = '';
- if (!isEnabled) {
- status = '已禁用';
- } else if (keywordCount > 0) {
- status = '活跃';
- } else {
- status = '未配置';
- }
-
- const row = document.createElement('tr');
- row.className = isEnabled ? '' : 'table-secondary';
- row.innerHTML = `
-
- ${account.id}
- ${!isEnabled ? '' : ''}
- |
-
- ${keywordCount} 个关键词
- |
- ${status} |
-
- ${new Date().toLocaleString()}
- |
- `;
- tbody.appendChild(row);
- });
-}
diff --git a/static/js/modules/example-usage.html b/static/js/modules/example-usage.html
deleted file mode 100644
index e951e24..0000000
--- a/static/js/modules/example-usage.html
+++ /dev/null
@@ -1,102 +0,0 @@
-
-
-
-
-
- 闲鱼自动回复系统 - 模块化版本
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/static/js/modules/globals.js b/static/js/modules/globals.js
deleted file mode 100644
index 9eecdc0..0000000
--- a/static/js/modules/globals.js
+++ /dev/null
@@ -1,15 +0,0 @@
-// ==================== 全局变量 ====================
-const apiBase = location.origin;
-let keywordsData = {};
-let currentCookieId = '';
-let editCookieId = '';
-let authToken = localStorage.getItem('auth_token');
-let dashboardData = {
- accounts: [],
- totalKeywords: 0
-};
-
-// 账号关键词缓存
-let accountKeywordCache = {};
-let cacheTimestamp = 0;
-const CACHE_DURATION = 30000; // 30秒缓存
diff --git a/static/js/modules/init.js b/static/js/modules/init.js
deleted file mode 100644
index 629e38c..0000000
--- a/static/js/modules/init.js
+++ /dev/null
@@ -1,171 +0,0 @@
-// ==================== 应用初始化 ====================
-
-// 登出功能
-async function logout() {
- try {
- if (authToken) {
- await fetch('/logout', {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
- }
- localStorage.removeItem('auth_token');
- window.location.href = '/';
- } catch (err) {
- console.error('登出失败:', err);
- localStorage.removeItem('auth_token');
- window.location.href = '/';
- }
-}
-
-// 检查认证状态
-async function checkAuth() {
- if (!authToken) {
- window.location.href = '/';
- return false;
- }
-
- try {
- const response = await fetch('/verify', {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
- const result = await response.json();
-
- if (!result.authenticated) {
- localStorage.removeItem('auth_token');
- window.location.href = '/';
- return false;
- }
-
- // 检查是否为管理员,显示管理员菜单和功能
- if (result.username === 'admin') {
- const adminMenuSection = document.getElementById('adminMenuSection');
- if (adminMenuSection) {
- adminMenuSection.style.display = 'block';
- }
-
- // 显示备份管理功能
- const backupManagement = document.getElementById('backup-management');
- if (backupManagement) {
- backupManagement.style.display = 'block';
- }
- }
-
- return true;
- } catch (err) {
- localStorage.removeItem('auth_token');
- window.location.href = '/';
- return false;
- }
-}
-
-// 初始化事件监听
-document.addEventListener('DOMContentLoaded', async () => {
- // 首先检查认证状态
- const isAuthenticated = await checkAuth();
- if (!isAuthenticated) return;
-
- // 添加Cookie表单提交
- document.getElementById('addForm').addEventListener('submit', async (e) => {
- e.preventDefault();
- const id = document.getElementById('cookieId').value.trim();
- const value = document.getElementById('cookieValue').value.trim();
-
- if (!id || !value) return;
-
- try {
- await fetchJSON(apiBase + '/cookies', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ id, value })
- });
-
- document.getElementById('cookieId').value = '';
- document.getElementById('cookieValue').value = '';
- showToast(`账号 "${id}" 添加成功`);
- loadCookies();
- } catch (err) {
- // 错误已在fetchJSON中处理
- }
- });
-
- // 增强的键盘快捷键和用户体验
- document.getElementById('newKeyword')?.addEventListener('keypress', function (e) {
- if (e.key === 'Enter') {
- e.preventDefault();
- document.getElementById('newReply').focus();
- }
- });
-
- document.getElementById('newReply')?.addEventListener('keypress', function (e) {
- if (e.key === 'Enter') {
- e.preventDefault();
- addKeyword();
- }
- });
-
- // ESC键取消编辑
- document.addEventListener('keydown', function (e) {
- if (e.key === 'Escape' && typeof window.editingIndex !== 'undefined') {
- e.preventDefault();
- cancelEdit();
- }
- });
-
- // 输入框实时验证和提示
- document.getElementById('newKeyword')?.addEventListener('input', function (e) {
- const value = e.target.value.trim();
- const addBtn = document.querySelector('.add-btn');
- const replyInput = document.getElementById('newReply');
-
- if (value.length > 0) {
- e.target.style.borderColor = '#10b981';
- if (replyInput.value.trim().length > 0) {
- addBtn.style.opacity = '1';
- addBtn.style.transform = 'scale(1)';
- }
- } else {
- e.target.style.borderColor = '#e5e7eb';
- addBtn.style.opacity = '0.7';
- addBtn.style.transform = 'scale(0.95)';
- }
- });
-
- document.getElementById('newReply')?.addEventListener('input', function (e) {
- const value = e.target.value.trim();
- const addBtn = document.querySelector('.add-btn');
- const keywordInput = document.getElementById('newKeyword');
-
- if (value.length > 0) {
- e.target.style.borderColor = '#10b981';
- if (keywordInput.value.trim().length > 0) {
- addBtn.style.opacity = '1';
- addBtn.style.transform = 'scale(1)';
- }
- } else {
- e.target.style.borderColor = '#e5e7eb';
- addBtn.style.opacity = '0.7';
- addBtn.style.transform = 'scale(0.95)';
- }
- });
-
- // 初始加载仪表盘
- loadDashboard();
-
- // 点击侧边栏外部关闭移动端菜单
- document.addEventListener('click', function (e) {
- const sidebar = document.getElementById('sidebar');
- const toggle = document.querySelector('.mobile-toggle');
-
- if (window.innerWidth <= 768 &&
- !sidebar.contains(e.target) &&
- !toggle.contains(e.target) &&
- sidebar.classList.contains('show')) {
- sidebar.classList.remove('show');
- }
- });
-});
diff --git a/static/js/modules/keyword-cache.js b/static/js/modules/keyword-cache.js
deleted file mode 100644
index 685c04b..0000000
--- a/static/js/modules/keyword-cache.js
+++ /dev/null
@@ -1,42 +0,0 @@
-// ==================== 关键词缓存管理 ====================
-
-// 获取账号关键词数量(带缓存)- 包含普通关键词和商品关键词
-async function getAccountKeywordCount(accountId) {
- const now = Date.now();
-
- // 检查缓存
- if (accountKeywordCache[accountId] && (now - cacheTimestamp) < CACHE_DURATION) {
- return accountKeywordCache[accountId];
- }
-
- try {
- const response = await fetch(`${apiBase}/keywords/${accountId}`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- if (response.ok) {
- const keywordsData = await response.json();
- // 现在API返回的是包含普通关键词和商品关键词的完整列表
- const count = keywordsData.length;
-
- // 更新缓存
- accountKeywordCache[accountId] = count;
- cacheTimestamp = now;
-
- return count;
- } else {
- return 0;
- }
- } catch (error) {
- console.error(`获取账号 ${accountId} 关键词失败:`, error);
- return 0;
- }
-}
-
-// 清除关键词缓存
-function clearKeywordCache() {
- accountKeywordCache = {};
- cacheTimestamp = 0;
-}
diff --git a/static/js/modules/keywords.js b/static/js/modules/keywords.js
deleted file mode 100644
index ea06808..0000000
--- a/static/js/modules/keywords.js
+++ /dev/null
@@ -1,463 +0,0 @@
-// ==================== 关键词管理 ====================
-
-// 加载账号关键词
-async function loadAccountKeywords() {
- const accountId = document.getElementById('accountSelect').value;
- const keywordManagement = document.getElementById('keywordManagement');
-
- if (!accountId) {
- keywordManagement.style.display = 'none';
- return;
- }
-
- try {
- toggleLoading(true);
- currentCookieId = accountId;
-
- // 获取账号详情以检查状态
- const accountResponse = await fetch(`${apiBase}/cookies/details`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- let accountStatus = true; // 默认启用
- if (accountResponse.ok) {
- const accounts = await accountResponse.json();
- const currentAccount = accounts.find(acc => acc.id === accountId);
- accountStatus = currentAccount ? (currentAccount.enabled === undefined ? true : currentAccount.enabled) : true;
- console.log(`加载关键词时账号 ${accountId} 状态: enabled=${currentAccount?.enabled}, accountStatus=${accountStatus}`); // 调试信息
- }
-
- const response = await fetch(`${apiBase}/keywords-with-item-id/${accountId}`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- if (response.ok) {
- const data = await response.json();
- console.log('从服务器获取的关键词数据:', data); // 调试信息
-
- // 后端返回的是 [{keyword, reply, item_id}, ...] 格式,直接使用
- const formattedData = data;
-
- console.log('格式化后的关键词数据:', formattedData); // 调试信息
- keywordsData[accountId] = formattedData;
- renderKeywordsList(formattedData);
-
- // 加载商品列表
- await loadItemsList(accountId);
-
- // 更新账号徽章显示
- updateAccountBadge(accountId, accountStatus);
-
- keywordManagement.style.display = 'block';
- } else {
- showToast('加载关键词失败', 'danger');
- }
- } catch (error) {
- console.error('加载关键词失败:', error);
- showToast('加载关键词失败', 'danger');
- } finally {
- toggleLoading(false);
- }
-}
-
-// 更新账号徽章显示
-function updateAccountBadge(accountId, isEnabled) {
- const badge = document.getElementById('currentAccountBadge');
- if (!badge) return;
-
- const statusIcon = isEnabled ? '🟢' : '🔴';
- const statusText = isEnabled ? '启用' : '禁用';
- const statusClass = isEnabled ? 'bg-success' : 'bg-warning';
-
- badge.innerHTML = `
-
- ${statusIcon} ${accountId}
-
-
- 状态: ${statusText}
- ${!isEnabled ? ' (配置的关键词不会参与自动回复)' : ''}
-
- `;
-}
-
-// 显示添加关键词表单
-function showAddKeywordForm() {
- const form = document.getElementById('addKeywordForm');
- form.style.display = form.style.display === 'none' ? 'block' : 'none';
-
- if (form.style.display === 'block') {
- document.getElementById('newKeyword').focus();
- }
-}
-
-// 加载商品列表
-async function loadItemsList(accountId) {
- try {
- const response = await fetch(`${apiBase}/items/${accountId}`, {
- headers: {
- 'Authorization': `Bearer ${authToken}`
- }
- });
-
- if (response.ok) {
- const data = await response.json();
- const items = data.items || [];
-
- // 更新商品选择下拉框
- const selectElement = document.getElementById('newItemIdSelect');
- if (selectElement) {
- // 清空现有选项(保留第一个默认选项)
- selectElement.innerHTML = '';
-
- // 添加商品选项
- items.forEach(item => {
- const option = document.createElement('option');
- option.value = item.item_id;
- option.textContent = `${item.item_id} - ${item.item_title}`;
- selectElement.appendChild(option);
- });
- }
-
- console.log(`加载了 ${items.length} 个商品到选择列表`);
- } else {
- console.warn('加载商品列表失败:', response.status);
- }
- } catch (error) {
- console.error('加载商品列表时发生错误:', error);
- }
-}
-
-// 添加或更新关键词
-async function addKeyword() {
- const keyword = document.getElementById('newKeyword').value.trim();
- const reply = document.getElementById('newReply').value.trim();
- const itemId = document.getElementById('newItemIdSelect').value.trim();
-
- if (!keyword || !reply) {
- showToast('请填写关键词和回复内容', 'warning');
- return;
- }
-
- if (!currentCookieId) {
- showToast('请先选择账号', 'warning');
- return;
- }
-
- // 检查是否为编辑模式
- const isEditMode = typeof window.editingIndex !== 'undefined';
- const actionText = isEditMode ? '更新' : '添加';
-
- try {
- toggleLoading(true);
-
- // 获取当前关键词列表
- let currentKeywords = [...(keywordsData[currentCookieId] || [])];
-
- // 如果是编辑模式,先移除原关键词
- if (isEditMode) {
- currentKeywords.splice(window.editingIndex, 1);
- }
-
- // 准备要保存的关键词列表
- let keywordsToSave = [...currentKeywords];
-
- // 如果是编辑模式,先移除原关键词
- if (isEditMode && typeof window.editingIndex !== 'undefined') {
- keywordsToSave.splice(window.editingIndex, 1);
- }
-
- // 检查关键词是否已存在(考虑商品ID)
- const existingKeyword = keywordsToSave.find(item =>
- item.keyword === keyword &&
- (item.item_id || '') === (itemId || '')
- );
- if (existingKeyword) {
- const itemIdText = itemId ? `(商品ID: ${itemId})` : '(通用关键词)';
- showToast(`关键词 "${keyword}" ${itemIdText} 已存在,请使用其他关键词或商品ID`, 'warning');
- toggleLoading(false);
- return;
- }
-
- // 添加新关键词或更新的关键词
- const newKeyword = {
- keyword: keyword,
- reply: reply,
- item_id: itemId || ''
- };
- keywordsToSave.push(newKeyword);
-
- const response = await fetch(`${apiBase}/keywords-with-item-id/${currentCookieId}`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${authToken}`
- },
- body: JSON.stringify({
- keywords: keywordsToSave
- })
- });
-
- if (response.ok) {
- showToast(`✨ 关键词 "${keyword}" ${actionText}成功!`, 'success');
-
- // 清空输入框并重置样式
- const keywordInput = document.getElementById('newKeyword');
- const replyInput = document.getElementById('newReply');
- const selectElement = document.getElementById('newItemIdSelect');
- const addBtn = document.querySelector('.add-btn');
-
- keywordInput.value = '';
- replyInput.value = '';
- if (selectElement) {
- selectElement.value = '';
- }
- keywordInput.style.borderColor = '#e5e7eb';
- replyInput.style.borderColor = '#e5e7eb';
- addBtn.style.opacity = '0.7';
- addBtn.style.transform = 'scale(0.95)';
-
- // 如果是编辑模式,重置编辑状态
- if (isEditMode) {
- delete window.editingIndex;
- delete window.originalKeyword;
-
- // 恢复添加按钮
- addBtn.innerHTML = '添加';
- addBtn.style.background = 'linear-gradient(135deg, #10b981 0%, #059669 100%)';
-
- // 移除取消按钮
- const cancelBtn = document.getElementById('cancelEditBtn');
- if (cancelBtn) {
- cancelBtn.remove();
- }
- }
-
- // 聚焦到关键词输入框,方便连续添加
- setTimeout(() => {
- keywordInput.focus();
- }, 100);
-
- loadAccountKeywords(); // 重新加载关键词列表
- clearKeywordCache(); // 清除缓存
- } else {
- const errorText = await response.text();
- console.error('关键词添加失败:', errorText);
- showToast('关键词添加失败', 'danger');
- }
- } catch (error) {
- console.error('添加关键词失败:', error);
- showToast('添加关键词失败', 'danger');
- } finally {
- toggleLoading(false);
- }
-}
-
-// 渲染现代化关键词列表
-function renderKeywordsList(keywords) {
- console.log('渲染关键词列表:', keywords); // 调试信息
- const container = document.getElementById('keywordsList');
-
- if (!container) {
- console.error('找不到关键词列表容器元素');
- return;
- }
-
- container.innerHTML = '';
-
- if (!keywords || keywords.length === 0) {
- console.log('关键词列表为空,显示空状态');
- container.innerHTML = `
-
-
-
还没有关键词
-
添加第一个关键词,让您的闲鱼店铺自动回复客户消息
-
-
- `;
- return;
- }
-
- console.log(`开始渲染 ${keywords.length} 个关键词`);
-
- keywords.forEach((item, index) => {
- console.log(`渲染关键词 ${index + 1}:`, item); // 调试信息
-
- const keywordItem = document.createElement('div');
- keywordItem.className = 'keyword-item';
- // 商品ID显示
- const itemIdDisplay = item.item_id ?
- ` 商品ID: ${item.item_id}` :
- ' 通用关键词';
-
- keywordItem.innerHTML = `
-
-
- `;
- container.appendChild(keywordItem);
- });
-
- console.log('关键词列表渲染完成');
-}
-
-// 聚焦到关键词输入框
-function focusKeywordInput() {
- document.getElementById('newKeyword').focus();
-}
-
-// 编辑关键词 - 改进版本
-function editKeyword(index) {
- const keywords = keywordsData[currentCookieId] || [];
- const keyword = keywords[index];
-
- if (!keyword) {
- showToast('关键词不存在', 'warning');
- return;
- }
-
- // 将关键词信息填入输入框
- document.getElementById('newKeyword').value = keyword.keyword;
- document.getElementById('newReply').value = keyword.reply;
-
- // 设置商品ID选择框
- const selectElement = document.getElementById('newItemIdSelect');
- if (selectElement) {
- selectElement.value = keyword.item_id || '';
- }
-
- // 设置编辑模式标识
- window.editingIndex = index;
- window.originalKeyword = keyword.keyword;
- window.originalItemId = keyword.item_id || '';
-
- // 更新按钮文本和样式
- const addBtn = document.querySelector('.add-btn');
- addBtn.innerHTML = '更新';
- addBtn.style.background = 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)';
-
- // 显示取消按钮
- showCancelEditButton();
-
- // 聚焦到关键词输入框并选中文本
- setTimeout(() => {
- const keywordInput = document.getElementById('newKeyword');
- keywordInput.focus();
- keywordInput.select();
- }, 100);
-
- showToast('📝 编辑模式:修改后点击"更新"按钮保存', 'info');
-}
-
-// 显示取消编辑按钮
-function showCancelEditButton() {
- // 检查是否已存在取消按钮
- if (document.getElementById('cancelEditBtn')) {
- return;
- }
-
- const addBtn = document.querySelector('.add-btn');
- const cancelBtn = document.createElement('button');
- cancelBtn.id = 'cancelEditBtn';
- cancelBtn.className = 'btn btn-outline-secondary';
- cancelBtn.style.marginLeft = '0.5rem';
- cancelBtn.innerHTML = '取消';
- cancelBtn.onclick = cancelEdit;
-
- addBtn.parentNode.appendChild(cancelBtn);
-}
-
-// 取消编辑
-function cancelEdit() {
- // 清空输入框
- document.getElementById('newKeyword').value = '';
- document.getElementById('newReply').value = '';
-
- // 清空商品ID选择框
- const selectElement = document.getElementById('newItemIdSelect');
- if (selectElement) {
- selectElement.value = '';
- }
-
- // 重置编辑状态
- delete window.editingIndex;
- delete window.originalKeyword;
- delete window.originalItemId;
-
- // 恢复添加按钮
- const addBtn = document.querySelector('.add-btn');
- addBtn.innerHTML = '添加';
- addBtn.style.background = 'linear-gradient(135deg, #10b981 0%, #059669 100%)';
-
- // 移除取消按钮
- const cancelBtn = document.getElementById('cancelEditBtn');
- if (cancelBtn) {
- cancelBtn.remove();
- }
-
- showToast('已取消编辑', 'info');
-}
-
-// 删除关键词
-async function deleteKeyword(cookieId, index) {
- if (!confirm('确定要删除这个关键词吗?')) {
- return;
- }
-
- try {
- toggleLoading(true);
-
- // 获取当前关键词列表
- const currentKeywords = keywordsData[cookieId] || [];
- // 移除指定索引的关键词
- currentKeywords.splice(index, 1);
-
- // 更新服务器
- const response = await fetch(`${apiBase}/keywords-with-item-id/${cookieId}`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${authToken}`
- },
- body: JSON.stringify({
- keywords: currentKeywords
- })
- });
-
- if (response.ok) {
- showToast('关键词删除成功', 'success');
- keywordsData[cookieId] = currentKeywords;
- renderKeywordsList(currentKeywords);
- clearKeywordCache(); // 清除缓存
- } else {
- const errorText = await response.text();
- console.error('关键词删除失败:', errorText);
- showToast('关键词删除失败', 'danger');
- }
- } catch (error) {
- console.error('删除关键词失败:', error);
- showToast('删除关键词删除失败', 'danger');
- } finally {
- toggleLoading(false);
- }
-}
diff --git a/static/js/modules/main-features.js b/static/js/modules/main-features.js
deleted file mode 100644
index c2d6879..0000000
--- a/static/js/modules/main-features.js
+++ /dev/null
@@ -1,189 +0,0 @@
-// ==================== 主要功能模块 ====================
-
-// 切换账号启用/禁用状态
-async function toggleAccountStatus(accountId, enabled) {
- try {
- toggleLoading(true);
-
- // 这里需要调用后端API来更新账号状态
- // 由于当前后端可能没有enabled字段,我们先在前端模拟
- // 实际项目中需要后端支持
-
- const response = await fetch(`${apiBase}/cookies/${accountId}/status`, {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${authToken}`
- },
- body: JSON.stringify({ enabled: enabled })
- });
-
- if (response.ok) {
- showToast(`账号 "${accountId}" 已${enabled ? '启用' : '禁用'}`, 'success');
-
- // 清除相关缓存,确保数据一致性
- clearKeywordCache();
-
- // 更新界面显示
- updateAccountRowStatus(accountId, enabled);
-
- // 刷新自动回复页面的账号列表
- refreshAccountList();
-
- // 如果禁用的账号在自动回复页面被选中,更新显示
- const accountSelect = document.getElementById('accountSelect');
- if (accountSelect && accountSelect.value === accountId) {
- if (!enabled) {
- // 更新徽章显示禁用状态
- updateAccountBadge(accountId, false);
- showToast('账号已禁用,配置的关键词不会参与自动回复', 'warning');
- } else {
- // 更新徽章显示启用状态
- updateAccountBadge(accountId, true);
- showToast('账号已启用,配置的关键词将参与自动回复', 'success');
- }
- }
-
- } else {
- // 如果后端不支持,先在前端模拟
- console.warn('后端暂不支持账号状态切换,使用前端模拟');
- showToast(`账号 "${accountId}" 已${enabled ? '启用' : '禁用'} (前端模拟)`, enabled ? 'success' : 'warning');
- updateAccountRowStatus(accountId, enabled);
- }
-
- } catch (error) {
- console.error('切换账号状态失败:', error);
-
- // 后端不支持时的降级处理
- showToast(`账号 "${accountId}" 已${enabled ? '启用' : '禁用'} (本地模拟)`, enabled ? 'success' : 'warning');
- updateAccountRowStatus(accountId, enabled);
-
- // 恢复切换按钮状态
- const toggle = document.querySelector(`input[onchange*="${accountId}"]`);
- if (toggle) {
- toggle.checked = enabled;
- }
- } finally {
- toggleLoading(false);
- }
-}
-
-// 更新账号行的状态显示
-function updateAccountRowStatus(accountId, enabled) {
- const toggle = document.querySelector(`input[onchange*="${accountId}"]`);
- if (!toggle) return;
-
- const row = toggle.closest('tr');
- const statusBadge = row.querySelector('.status-badge');
- const actionButtons = row.querySelectorAll('.btn-group .btn:not(.btn-outline-info):not(.btn-outline-danger)');
-
- // 更新行样式
- row.className = `account-row ${enabled ? 'enabled' : 'disabled'}`;
-
- // 更新状态徽章
- statusBadge.className = `status-badge ${enabled ? 'enabled' : 'disabled'}`;
- statusBadge.title = enabled ? '账号已启用' : '账号已禁用';
- statusBadge.innerHTML = `
-
- `;
-
- // 更新按钮状态(只禁用编辑Cookie按钮,其他按钮保持可用)
- actionButtons.forEach(btn => {
- if (btn.onclick && btn.onclick.toString().includes('editCookieInline')) {
- btn.disabled = !enabled;
- }
- // 设置自动回复按钮始终可用,但更新提示文本
- if (btn.onclick && btn.onclick.toString().includes('goToAutoReply')) {
- btn.title = enabled ? '设置自动回复' : '配置关键词 (账号已禁用)';
- }
- });
-
- // 更新切换按钮的提示
- const label = toggle.closest('.status-toggle');
- label.title = enabled ? '点击禁用' : '点击启用';
-}
-
-// 切换自动确认发货状态
-async function toggleAutoConfirm(accountId, enabled) {
- try {
- toggleLoading(true);
-
- const response = await fetch(`${apiBase}/cookies/${accountId}/auto-confirm`, {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${authToken}`
- },
- body: JSON.stringify({ auto_confirm: enabled })
- });
-
- if (response.ok) {
- const result = await response.json();
- showToast(result.message, 'success');
-
- // 更新界面显示
- updateAutoConfirmRowStatus(accountId, enabled);
- } else {
- const error = await response.json();
- showToast(error.detail || '更新自动确认发货设置失败', 'error');
-
- // 恢复切换按钮状态
- const toggle = document.querySelector(`input[onchange*="toggleAutoConfirm('${accountId}'"]`);
- if (toggle) {
- toggle.checked = !enabled;
- }
- }
-
- } catch (error) {
- console.error('切换自动确认发货状态失败:', error);
- showToast('网络错误,请稍后重试', 'error');
-
- // 恢复切换按钮状态
- const toggle = document.querySelector(`input[onchange*="toggleAutoConfirm('${accountId}'"]`);
- if (toggle) {
- toggle.checked = !enabled;
- }
- } finally {
- toggleLoading(false);
- }
-}
-
-// 更新自动确认发货行状态
-function updateAutoConfirmRowStatus(accountId, enabled) {
- const row = document.querySelector(`tr:has(input[onchange*="toggleAutoConfirm('${accountId}'"])`);
- if (!row) return;
-
- const statusBadge = row.querySelector('.status-badge:has(i.bi-truck, i.bi-truck-flatbed)');
- const toggle = row.querySelector(`input[onchange*="toggleAutoConfirm('${accountId}'"]`);
-
- if (statusBadge && toggle) {
- // 更新状态徽章
- statusBadge.className = `status-badge ${enabled ? 'enabled' : 'disabled'}`;
- statusBadge.title = enabled ? '自动确认发货已开启' : '自动确认发货已关闭';
- statusBadge.innerHTML = `
-
- `;
-
- // 更新切换按钮的提示
- const label = toggle.closest('.status-toggle');
- label.title = enabled ? '点击关闭自动确认发货' : '点击开启自动确认发货';
- }
-}
-
-// 跳转到自动回复页面并选择指定账号
-function goToAutoReply(accountId) {
- // 切换到自动回复页面
- showSection('auto-reply');
-
- // 设置账号选择器的值
- setTimeout(() => {
- const accountSelect = document.getElementById('accountSelect');
- if (accountSelect) {
- accountSelect.value = accountId;
- // 触发change事件来加载关键词
- loadAccountKeywords();
- }
- }, 100);
-
- showToast(`已切换到自动回复页面,账号 "${accountId}" 已选中`, 'info');
-}
diff --git a/static/js/modules/navigation.js b/static/js/modules/navigation.js
deleted file mode 100644
index 63ac0cc..0000000
--- a/static/js/modules/navigation.js
+++ /dev/null
@@ -1,86 +0,0 @@
-// ==================== 页面导航功能 ====================
-
-// 菜单切换功能
-function showSection(sectionName) {
- console.log('切换到页面:', sectionName); // 调试信息
-
- // 隐藏所有内容区域
- document.querySelectorAll('.content-section').forEach(section => {
- section.classList.remove('active');
- });
-
- // 移除所有菜单项的active状态
- document.querySelectorAll('.nav-link').forEach(link => {
- link.classList.remove('active');
- });
-
- // 显示选中的内容区域
- const targetSection = document.getElementById(sectionName + '-section');
- if (targetSection) {
- targetSection.classList.add('active');
- console.log('页面已激活:', sectionName + '-section'); // 调试信息
- } else {
- console.error('找不到页面元素:', sectionName + '-section'); // 调试信息
- }
-
- // 设置对应菜单项为active(修复event.target问题)
- const menuLinks = document.querySelectorAll('.nav-link');
- menuLinks.forEach(link => {
- if (link.onclick && link.onclick.toString().includes(`showSection('${sectionName}')`)) {
- link.classList.add('active');
- }
- });
-
- // 根据不同section加载对应数据
- switch (sectionName) {
- case 'dashboard':
- loadDashboard();
- break;
- case 'accounts':
- loadCookies();
- break;
- case 'items':
- loadItems();
- break;
- case 'auto-reply':
- refreshAccountList();
- break;
- case 'cards':
- loadCards();
- break;
- case 'auto-delivery':
- loadDeliveryRules();
- break;
- case 'notification-channels':
- loadNotificationChannels();
- break;
- case 'message-notifications':
- loadMessageNotifications();
- break;
- case 'logs':
- // 如果没有日志数据,则加载
- setTimeout(() => {
- if (!window.allLogs || window.allLogs.length === 0) {
- refreshLogs();
- }
- }, 100);
- break;
- }
-
- // 如果切换到非日志页面,停止自动刷新
- if (sectionName !== 'logs' && window.autoRefreshInterval) {
- clearInterval(window.autoRefreshInterval);
- window.autoRefreshInterval = null;
- const button = document.querySelector('#autoRefreshText');
- const icon = button?.previousElementSibling;
- if (button) {
- button.textContent = '开启自动刷新';
- if (icon) icon.className = 'bi bi-play-circle me-1';
- }
- }
-}
-
-// 移动端侧边栏切换
-function toggleSidebar() {
- document.getElementById('sidebar').classList.toggle('show');
-}
diff --git a/static/js/modules/utils.js b/static/js/modules/utils.js
deleted file mode 100644
index 9ac3988..0000000
--- a/static/js/modules/utils.js
+++ /dev/null
@@ -1,85 +0,0 @@
-// ==================== 通用工具函数 ====================
-
-// 显示/隐藏加载动画
-function toggleLoading(show) {
- document.getElementById('loading').classList.toggle('d-none', !show);
-}
-
-// 显示提示消息
-function showToast(message, type = 'success') {
- const toastContainer = document.querySelector('.toast-container');
- const toast = document.createElement('div');
- toast.className = `toast align-items-center text-white bg-${type} border-0`;
- toast.setAttribute('role', 'alert');
- toast.setAttribute('aria-live', 'assertive');
- toast.setAttribute('aria-atomic', 'true');
-
- toast.innerHTML = `
-
- `;
-
- toastContainer.appendChild(toast);
- const bsToast = new bootstrap.Toast(toast, { delay: 3000 });
- bsToast.show();
-
- // 自动移除
- toast.addEventListener('hidden.bs.toast', () => {
- toast.remove();
- });
-}
-
-// 错误处理
-async function handleApiError(err) {
- console.error(err);
- showToast(err.message || '操作失败', 'danger');
- toggleLoading(false);
-}
-
-// API请求包装
-async function fetchJSON(url, opts = {}) {
- toggleLoading(true);
- try {
- // 添加认证头
- if (authToken) {
- opts.headers = opts.headers || {};
- opts.headers['Authorization'] = `Bearer ${authToken}`;
- }
-
- const res = await fetch(url, opts);
- if (res.status === 401) {
- // 未授权,跳转到登录页面
- localStorage.removeItem('auth_token');
- window.location.href = '/';
- return;
- }
- if (!res.ok) {
- let errorMessage = `HTTP ${res.status}`;
- try {
- const errorText = await res.text();
- if (errorText) {
- // 尝试解析JSON错误信息
- try {
- const errorJson = JSON.parse(errorText);
- errorMessage = errorJson.detail || errorJson.message || errorText;
- } catch {
- errorMessage = errorText;
- }
- }
- } catch {
- errorMessage = `HTTP ${res.status} ${res.statusText}`;
- }
- throw new Error(errorMessage);
- }
- const data = await res.json();
- toggleLoading(false);
- return data;
- } catch (err) {
- handleApiError(err);
- throw err;
- }
-}