| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- {% extends "base.html" %}
- {% block content %}
- <div class="flex h-screen overflow-hidden" id="deep-view">
- <!-- Sidebar -->
- {% include 'partials/sidebar.html' %}
- <!-- Main Content -->
- <div class="flex-1 flex flex-col min-w-0 overflow-hidden bg-gray-900/80 relative">
- <!-- Top Header -->
- <header class="h-16 flex items-center justify-between px-6 z-20 bg-gray-900/80 backdrop-blur-md border-b border-blue-900/30">
- <button class="md:hidden text-gray-300 focus:outline-none" id="open-sidebar">
- <i class="fas fa-bars text-xl"></i>
- </button>
- <h1 class="text-lg md:text-2xl font-bold tech-title truncate ml-2">AI深度采集管理</h1>
- <div class="flex items-center space-x-4">
- <div class="flex items-center space-x-2">
- <div class="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white font-bold border border-cyan-400 shadow-md">A</div>
- <span class="hidden md:inline text-sm text-gray-300">{{ current_user.username }}</span>
- </div>
- </div>
- </header>
- <!-- Main Scrollable Area -->
- <main class="flex-1 overflow-y-auto p-4 md:p-6 scroll-smooth">
- <div class="tech-panel p-6 rounded-xl mb-6 min-h-[calc(100vh-140px)] flex flex-col">
-
- <!-- 1. Search Area -->
- <div class="flex flex-col md:flex-row gap-4 mb-6">
- <div class="flex-1 flex gap-2">
- <input type="text" id="search-keyword" class="flex-1 bg-gray-700 text-white rounded p-2 border border-gray-600 focus:outline-none focus:border-blue-500" placeholder="请输入URL或内容关键字...">
- <button onclick="searchData()" class="bg-blue-600 hover:bg-blue-700 text-white px-6 rounded font-medium transition-colors">
- <i class="fas fa-search"></i> 搜索
- </button>
- </div>
- </div>
- <!-- 2. Toolbar -->
- <div class="flex gap-3 mb-4 p-3 bg-gray-800/50 rounded-lg border border-gray-700">
- <button onclick="batchDelete()" class="bg-red-600/80 hover:bg-red-600 text-white px-4 py-2 rounded text-sm transition-colors flex items-center gap-2">
- <i class="fas fa-trash-alt"></i> 批量删除
- </button>
- </div>
- <!-- 3. Data Table -->
- <div class="flex-1 overflow-x-auto">
- <table class="w-full text-left border-collapse">
- <thead>
- <tr class="text-gray-400 border-b border-gray-700 bg-gray-800/30">
- <th class="p-4 w-12 text-center">
- <input type="checkbox" id="select-all" onclick="toggleSelectAll()" class="rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-offset-gray-900">
- </th>
- <th class="p-4 w-20">ID</th>
- <th class="p-4">标题 / URL</th>
- <th class="p-4 w-32">状态</th>
- <th class="p-4 w-64">AI摘要</th>
- <th class="p-4 w-48">更新时间</th>
- <th class="p-4 w-48 text-center">操作</th>
- </tr>
- </thead>
- <tbody id="data-table-body" class="text-gray-300 divide-y divide-gray-700/50">
- <!-- Rows will be injected here -->
- </tbody>
- </table>
- </div>
- <!-- 4. Pagination -->
- <div class="mt-6 flex justify-between items-center border-t border-gray-700 pt-4">
- <div class="text-sm text-gray-500">
- 共 <span id="total-count" class="text-white font-bold">0</span> 条数据
- </div>
- <div class="flex gap-2" id="pagination-controls">
- <!-- Pagination buttons -->
- </div>
- </div>
- </div>
- </main>
- </div>
- </div>
- <!-- View Modal -->
- <div id="view-modal" class="fixed inset-0 z-50 hidden">
- <div class="absolute inset-0 bg-black/80 backdrop-blur-sm" onclick="closeViewModal()"></div>
- <div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-gray-900 rounded-xl shadow-2xl border border-gray-700 w-[800px] h-[90vh] flex flex-col animate-fade-in-up">
- <div class="p-4 border-b border-gray-700 flex justify-between items-center">
- <h3 class="text-xl font-bold text-white truncate max-w-[600px]" id="view-url">URL</h3>
- <button onclick="closeViewModal()" class="text-gray-400 hover:text-white"><i class="fas fa-times"></i></button>
- </div>
- <div class="flex-1 overflow-y-auto p-6">
- <div class="mb-6">
- <h4 class="text-sm font-bold text-purple-400 mb-2 uppercase tracking-wider">AI 摘要</h4>
- <div class="bg-gray-800 p-4 rounded-lg text-gray-300 leading-relaxed" id="view-summary"></div>
- </div>
- <div>
- <h4 class="text-sm font-bold text-blue-400 mb-2 uppercase tracking-wider">采集内容 (Markdown)</h4>
- <pre class="bg-gray-800 p-4 rounded-lg text-gray-300 overflow-x-auto whitespace-pre-wrap font-mono text-sm" id="view-content"></pre>
- </div>
- </div>
- </div>
- </div>
- <!-- Toast Notification -->
- <div id="toast" class="fixed top-5 left-1/2 transform -translate-x-1/2 z-50 hidden transition-all duration-300">
- <div class="bg-gray-800 border-l-4 border-blue-500 text-white px-6 py-3 rounded shadow-lg flex items-center gap-3">
- <i class="fas fa-info-circle text-blue-400" id="toast-icon"></i>
- <span id="toast-message">Notification</span>
- </div>
- </div>
- <!-- Confirm Modal -->
- <div id="confirm-modal" class="fixed inset-0 z-50 hidden">
- <div class="absolute inset-0 bg-black/60 backdrop-blur-sm" onclick="closeConfirm()"></div>
- <div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-gray-800 rounded-xl shadow-2xl border border-gray-700 w-96 p-6 animate-fade-in-up">
- <h3 class="text-xl font-bold text-white mb-2">确认操作</h3>
- <p class="text-gray-400 mb-6" id="confirm-message">确定要执行此操作吗?</p>
- <div class="flex justify-end gap-3">
- <button onclick="closeConfirm()" class="px-4 py-2 rounded text-gray-400 hover:text-white hover:bg-gray-700 transition-colors">取消</button>
- <button id="confirm-btn" class="px-4 py-2 rounded bg-red-600 text-white hover:bg-red-700 transition-colors shadow-lg">确定</button>
- </div>
- </div>
- </div>
- <script>
- let currentPage = 1;
- let currentKeyword = '';
- const pageSize = 10;
-
- // Check URL params for initial query
- const urlParams = new URLSearchParams(window.location.search);
- const initialQuery = urlParams.get('query');
- $(document).ready(function() {
- if (initialQuery) {
- $('#search-keyword').val(initialQuery);
- currentKeyword = initialQuery;
- }
-
- loadData();
- $('#search-keyword').keypress(function(e) {
- if(e.which == 13) {
- searchData();
- }
- });
- $('#open-sidebar').click(function() {
- $('#sidebar').toggleClass('-translate-x-full');
- });
- });
- function searchData() {
- currentKeyword = $('#search-keyword').val().trim();
- currentPage = 1;
- loadData();
- }
- function loadData() {
- const tbody = $('#data-table-body');
- tbody.html('<tr><td colspan="7" class="text-center py-10 text-gray-500"><i class="fas fa-spinner fa-spin mr-2"></i>加载中...</td></tr>');
- $.get('/deep/api/list', {
- page: currentPage,
- per_page: pageSize,
- query: currentKeyword
- }, function(response) {
- renderTable(response.items);
- renderPagination(response);
- $('#total-count').text(response.total);
- $('#select-all').prop('checked', false);
- }).fail(function() {
- tbody.html('<tr><td colspan="7" class="text-center py-10 text-red-500">加载失败,请重试</td></tr>');
- });
- }
- function renderTable(items) {
- const tbody = $('#data-table-body');
- tbody.empty();
- if (items.length === 0) {
- tbody.html('<tr><td colspan="7" class="text-center py-10 text-gray-500">暂无数据</td></tr>');
- return;
- }
- items.forEach(item => {
- const html = `
- <tr class="hover:bg-gray-800/50 transition-colors group">
- <td class="p-4 text-center">
- <input type="checkbox" class="row-checkbox rounded bg-gray-700 border-gray-600 text-blue-500 focus:ring-offset-gray-900" value="${item.id}">
- </td>
- <td class="p-4 text-gray-500 text-sm">#${item.id}</td>
- <td class="p-4">
- <div class="flex flex-col">
- <a href="${item.url}" target="_blank" class="text-white font-medium hover:text-blue-400 line-clamp-1 mb-1" title="${item.title || item.url}">
- ${item.title || item.url}
- </a>
- <div class="text-xs text-gray-500 line-clamp-1 font-mono">${item.url}</div>
- </div>
- </td>
- <td class="p-4">
- <span class="px-2 py-1 rounded text-xs ${item.status === 'completed' ? 'bg-green-900/50 text-green-400 border border-green-800' : 'bg-red-900/50 text-red-400 border border-red-800'}">
- ${item.status === 'completed' ? '已完成' : '失败'}
- </span>
- </td>
- <td class="p-4 text-gray-400 text-sm line-clamp-2" title="${item.summary || ''}">
- ${item.summary || '无摘要'}
- </td>
- <td class="p-4 text-sm text-gray-400 font-mono">${item.updated_at}</td>
- <td class="p-4">
- <div class="flex justify-center gap-2">
- <button onclick="viewDeepItem(${item.id})" class="text-blue-400 hover:text-blue-300 p-1" title="查看详情">
- <i class="fas fa-eye"></i>
- </button>
- <button onclick="deleteItem(${item.id})" class="text-red-400 hover:text-red-300 p-1" title="删除">
- <i class="fas fa-trash-alt"></i>
- </button>
- </div>
- </td>
- </tr>
- `;
- tbody.append(html);
- });
- }
- function viewDeepItem(id) {
- $.get('/deep/api/get/' + id, function(data) {
- $('#view-url').text(data.url);
- $('#view-summary').text(data.summary || '暂无摘要');
- $('#view-content').text(data.content || '暂无内容');
- $('#view-modal').removeClass('hidden');
- }).fail(function() {
- showToast('获取详情失败', 'error');
- });
- }
- function closeViewModal() {
- $('#view-modal').addClass('hidden');
- }
- function deleteItem(id) {
- showConfirm('确定要删除这条深度采集数据吗?', function() {
- $.ajax({
- url: '/deep/api/delete',
- type: 'POST',
- contentType: 'application/json',
- data: JSON.stringify({ ids: [id] }),
- success: function() {
- showToast('删除成功', 'success');
- loadData();
- },
- error: function() {
- showToast('删除失败', 'error');
- }
- });
- });
- }
- function batchDelete() {
- const ids = [];
- $('.row-checkbox:checked').each(function() {
- ids.push($(this).val());
- });
- if (ids.length === 0) {
- showToast('请先选择要删除的数据', 'info');
- return;
- }
- showConfirm(`确定要删除选中的 ${ids.length} 条数据吗?`, function() {
- $.ajax({
- url: '/deep/api/delete',
- type: 'POST',
- contentType: 'application/json',
- data: JSON.stringify({ ids: ids }),
- success: function() {
- showToast('批量删除成功', 'success');
- loadData();
- },
- error: function() {
- showToast('批量删除失败', 'error');
- }
- });
- });
- }
- // Pagination (Reuse from other pages or simple impl)
- function renderPagination(data) {
- const container = $('#pagination-controls');
- container.empty();
- if (data.pages <= 1) return;
- const prevBtn = $(`<button class="px-3 py-1 rounded border border-gray-600 text-gray-300 hover:bg-gray-700 disabled:opacity-50" ${data.current_page === 1 ? 'disabled' : ''}><i class="fas fa-chevron-left"></i></button>`);
- prevBtn.click(() => {
- if (currentPage > 1) {
- currentPage--;
- loadData();
- }
- });
- container.append(prevBtn);
- const nextBtn = $(`<button class="px-3 py-1 rounded border border-gray-600 text-gray-300 hover:bg-gray-700 disabled:opacity-50" ${data.current_page === data.pages ? 'disabled' : ''}><i class="fas fa-chevron-right"></i></button>`);
- nextBtn.click(() => {
- if (currentPage < data.pages) {
- currentPage++;
- loadData();
- }
- });
- container.append(nextBtn);
- }
- function toggleSelectAll() {
- const isChecked = $('#select-all').prop('checked');
- $('.row-checkbox').prop('checked', isChecked);
- }
-
- // Shared functions (showToast, showConfirm) - assumed to be global or we copy them
- // Copying simple versions here to be safe
- function showToast(message, type = 'info') {
- const toast = $('#toast');
- const icon = $('#toast-icon');
- const msg = $('#toast-message');
-
- msg.text(message);
- if (type === 'success') icon.attr('class', 'fas fa-check-circle text-green-400');
- else if (type === 'error') icon.attr('class', 'fas fa-times-circle text-red-400');
- else icon.attr('class', 'fas fa-info-circle text-blue-400');
-
- toast.removeClass('hidden').css('opacity', 0).animate({ opacity: 1 }, 300);
- setTimeout(() => {
- toast.animate({ opacity: 0 }, 300, function() {
- $(this).addClass('hidden');
- });
- }, 3000);
- }
- let confirmCallback = null;
- function showConfirm(message, callback) {
- $('#confirm-message').text(message);
- $('#confirm-modal').removeClass('hidden');
- confirmCallback = callback;
- }
- function closeConfirm() {
- $('#confirm-modal').addClass('hidden');
- confirmCallback = null;
- }
- $('#confirm-btn').click(function() {
- if (confirmCallback) confirmCallback();
- closeConfirm();
- });
- </script>
- {% endblock %}
|