From 8c87861d8e36312ac42c2b46f8fdaf75260d32b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A7=8B=E4=BA=8E=E5=88=9D=E8=A7=81?= <752204717@qq.com> Date: Fri, 3 Apr 2026 09:50:49 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=85=B3=E9=94=AE=E8=AF=8D?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/index.js | 4 +- src/components/common/langs/en.js | 43 +- src/components/common/langs/zh.js | 7 + src/components/page/crawlTaskMonitor.vue | 561 ++++++++--------------- 4 files changed, 231 insertions(+), 384 deletions(-) diff --git a/src/api/index.js b/src/api/index.js index 525753d..487fb66 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -19,8 +19,8 @@ const service = axios.create({ // baseURL: 'https://submission.tmrjournals.com/', //正式 记得切换 // baseURL: 'http://www.tougao.com/', //测试本地 记得切换 // baseURL: 'http://192.168.110.110/tougao/public/index.php/', - // baseURL: '/api', //本地 - baseURL: '/', //正式 + baseURL: '/api', //本地 + // baseURL: '/', //正式 }); diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js index 6867b42..68da425 100644 --- a/src/components/common/langs/en.js +++ b/src/components/common/langs/en.js @@ -588,15 +588,15 @@ colTitle: 'Template title', exportFail: 'Failed to export', }, crawlTask: { - pageTitle: 'Task Monitor', - pageDesc: 'View currently running and completed crawl tasks.', + pageTitle: 'Crawl Monitor', + pageDesc: 'Review crawl jobs and indexing progress for expert discovery.', exportData: 'Export Data', startCrawl: 'Start Crawl', - searchPlaceholder: 'Search by keyword...', + searchPlaceholder: 'Search by keyword or phrase…', searchBtn: 'Search', - allKeywords: 'All Keywords', - enabled: 'Enabled', - disabled: 'Disabled', + allKeywords: 'All keywords', + enabled: 'Active', + disabled: 'Paused', allStatus: 'All Status', statusCrawling: 'Crawling', statusDone: 'Done', @@ -641,28 +641,35 @@ colTitle: 'Template title', logsTitle: 'Run Logs', noLogs: 'No logs yet', noData: 'No tasks', - emptyResult: 'No matching task records found', - addKeyword: 'Add Keyword', + emptyResult: 'No tasks match your filters. Try adjusting keywords or status.', + addKeyword: 'Add keyword', keyword: 'Keyword', - keywordPlaceholder: 'Enter keyword, e.g.: Infectious Diseases', - runOnce: 'Single Crawl', - runOnceBtn: 'Single Crawl', + keywordPlaceholder: 'Enter a search term (e.g. infectious diseases)', + runOnce: 'Run once', + runOnceBtn: 'Run once', runOnceLoading: 'Crawling...', yes: 'Yes', no: 'No', cancel: 'Cancel', - confirm: 'OK', - enterKeyword: 'Please enter a keyword', - addKeywordSuccess: 'Keyword added successfully', - addKeywordFailed: 'Failed to add keyword', - runOnceSuccess: 'Single crawl triggered', - runOnceFailed: 'Failed to trigger single crawl', + confirm: 'Save', + enterKeyword: 'Please enter a keyword.', + addKeywordSuccess: 'Keyword added.', + addKeywordFailed: 'Could not add keyword.', + runOnceSuccess: 'One-off crawl requested.', + runOnceFailed: 'Could not start a one-off crawl.', disabledMsg: 'Disabled', enabledMsg: 'Enabled', pauseFailed: 'Failed to pause', resumeFailed: 'Failed to resume', - missingKeyword: 'Missing keyword, cannot perform single crawl', + missingKeyword: 'Enter a keyword before running a one-off crawl.', restartSuccess: 'Sync restarted (local mock)', + metricExperts: 'Experts indexed', + metricPages: 'Pages crawled', + stateRunning: 'Running', + stateStopped: 'Stopped', + taskRunningMsg: 'Crawl is now enabled.', + taskStoppedMsg: 'Crawl has been paused.', + runOnceQueued: 'A one-off crawl has been queued.', }, mailboxSend: { title: 'Write mail', diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js index a57f900..5b5d015 100644 --- a/src/components/common/langs/zh.js +++ b/src/components/common/langs/zh.js @@ -652,6 +652,13 @@ const zh = { resumeFailed: '恢复失败', missingKeyword: '缺少关键词,无法执行单次抓取', restartSuccess: '已重启同步(本地模拟)', + metricExperts: '入库专家', + metricPages: '采集页数', + stateRunning: '运行中', + stateStopped: '停止', + taskRunningMsg: '任务已启动', + taskStoppedMsg: '任务已禁用', + runOnceQueued: '单次采集指令已下发', }, mailboxSend: { title: '写邮件', diff --git a/src/components/page/crawlTaskMonitor.vue b/src/components/page/crawlTaskMonitor.vue index e6d12f6..7c5fe9d 100644 --- a/src/components/page/crawlTaskMonitor.vue +++ b/src/components/page/crawlTaskMonitor.vue @@ -1,7 +1,5 @@ @@ -177,212 +151,107 @@ export default { list: [], addDialogVisible: false, addLoading: false, - runOnceLoading: false, runOnceLoadingId: null, - addForm: { - field: '', - runNow: false - } + addForm: { field: '', runNow: false } }; }, created() { this.fetchList(); }, methods: { - calcProgress(lastPage, totalPages) { - const current = Number(lastPage || 0); - const total = Number(totalPages || 0); - if (total <= 0) return 0; - let percent = (current / total) * 100; - if (percent > 100) percent = 100; - return Number(percent.toFixed(2)); + /** 停止态用灰条,避免 exception 大红;运行中未满用蓝,满格用绿 */ + progressStrokeColor(item) { + if (item.state !== 'running') return '#c0c4cc'; + if (item.progress >= 100) return '#67c23a'; + return '#409eff'; }, + // 数据标准化逻辑 normalizeItem(item) { - const totalPages = Number(item.total_pages || 0); - const lastPage = Number(item.last_page || 0); - const percent = this.calcProgress(lastPage, totalPages); - const stateNum = Number(item.state); - const stateClass = stateNum === 0 ? 'running' : 'paused'; - const source = (item.source || '').toUpperCase() === 'PUBMED' ? 'PubMed' : (item.source || '-'); + const total = Number(item.total_pages || 0); + const current = Number(item.last_page || 0); + let progress = total > 0 ? (current / total) * 100 : 0; + return { - id: item.expert_fetch_id || item.id || 0, - field: item.field || '', + id: item.expert_fetch_id || item.id, + task_name: item.field || '-', + source: (item.source || 'PubMed'), expert_count: item.expert_count || 0, - task_name: item.field ? `${item.field}`:'-', - source, - state: stateClass, - stateClass, - progress: percent, - create_time: item.ctime_text || '-', - update_time: item.last_time_text || '-', - duration: '', - total_pages: Number(item.total_pages || 0), - last_page: Number(item.last_page || 0), - duplicates: 0, - failed: 0 + total_pages: total, + last_page: current, + progress: Number(progress.toFixed(1)), + state: Number(item.state) === 0 ? 'running' : 'paused', + create_time: item.ctime_text || '-' }; }, async fetchList() { this.loading = true; try { const params = { - keyword: this.searchText || '', + keyword: this.searchText, pageIndex: this.currentPage, - pageSize: this.pageSize + pageSize: this.pageSize, + state: this.filterStatus !== '' ? this.filterStatus : undefined }; - if (this.filterStatus !== '') { - params.state = this.filterStatus; - } const res = await this.$api.post('api/expert_manage/getFetchList', params); - if (res && res.code === 0 && res.data) { - const rows = res.data.list || []; - this.list = rows.map(this.normalizeItem); - this.total = Number(res.data.total || rows.length || 0); - } else { - this.list = []; - this.total = 0; + if (res.code === 0) { + this.list = res.data.list.map(this.normalizeItem); + this.total = res.data.total; } - } catch (e) { - this.list = []; - this.total = 0; } finally { this.loading = false; } }, - handleFilter() { - this.currentPage = 1; - this.fetchList(); - }, - handleSearchClick() { - this.handleFilter(); - }, - handleSizeChange(val) { - this.pageSize = val; - this.currentPage = 1; - this.fetchList(); - }, - handlePageChange(val) { - this.currentPage = val; - this.fetchList(); - window.scrollTo({ top: 0, behavior: 'smooth' }); - }, - resetQuery() { - this.searchText = ''; - this.filterStatus = ''; - this.currentPage = 1; - this.fetchList(); - }, - openAddDialog() { - this.addDialogVisible = true; - }, - resetAddForm() { - this.addLoading = false; - this.runOnceLoading = false; - this.addForm = { - field: '', - runNow: false - }; - }, - async submitAddKeyword() { - const field = (this.addForm.field || '').trim(); - if (!field) { - this.$message.warning(this.$t('crawlTask.enterKeyword')); - return; - } - const runNow = !!this.addForm.runNow; - this.addLoading = true; - try { - const addRes = await this.$api.post('api/expert_manage/addFetchField', { field }); - if (!addRes || addRes.code !== 0) { - this.$message.error((addRes && addRes.msg) || this.$t('crawlTask.addKeywordFailed')); - return; - } - - this.$message.success(this.$t('crawlTask.addKeywordSuccess')); - this.addDialogVisible = false; - this.currentPage = 1; - await this.fetchList(); - - if (runNow) { - // 勾选“单次抓取”时,在列表对应行按钮上显示 loading - const lowerField = field.toLowerCase(); - const target = this.list.find( - (row) => ((row.field || '').trim().toLowerCase() === lowerField) - ); - this.runOnceLoading = true; - this.runOnceLoadingId = target ? target.id : null; - const runRes = await this.$api.post('api/expert_finder/fetchOneField', { field }); - if (!runRes || runRes.code !== 0) { - this.$message.warning((runRes && runRes.msg) || this.$t('crawlTask.runOnceFailed')); - } else { - this.$message.success(this.$t('crawlTask.runOnceSuccess')); - } - await this.fetchList(); - } - } catch (e) { - this.$message.error(this.$t('crawlTask.operationRetry')); - } finally { - this.addLoading = false; - this.runOnceLoading = false; - this.runOnceLoadingId = null; - this.loading = false; - } - }, async handleToggleTask(item) { - const isRunning = item.state === 'running'; - const newState = isRunning ? '1' : '0'; + const isNowRunning = item.state === 'running'; + const newState = isNowRunning ? '0' : '1'; try { - this.loading = true; const res = await this.$api.post('api/expert_manage/editFetchField', { expert_fetch_id: item.id, state: newState }); - if (res && res.code === 0) { - item.state = isRunning ? 'paused' : 'running'; - item.stateClass = item.state; - this.$message.success(isRunning ? this.$t('crawlTask.disabledMsg') : this.$t('crawlTask.enabledMsg')); + if (res.code === 0) { + this.$message.success(isNowRunning ? this.$t('crawlTask.taskRunningMsg') : this.$t('crawlTask.taskStoppedMsg')); } else { - this.$message.error((res && res.msg) || (isRunning ? this.$t('crawlTask.pauseFailed') : this.$t('crawlTask.resumeFailed'))); + item.state = isNowRunning ? 'paused' : 'running'; + this.$message.error(res.msg || this.$t('crawlTask.operationFail')); } } catch (e) { - this.$message.error(isRunning ? this.$t('crawlTask.pauseFailed') : this.$t('crawlTask.resumeFailed')); - } finally { - this.loading = false; + item.state = isNowRunning ? 'paused' : 'running'; } }, - handleRestart(item) { - item.state = 'running'; - item.stateClass = 'running'; - item.progress = 0; - this.$message.success(this.$t('crawlTask.restartSuccess')); - }, async handleRunOnce(item) { - const field = (item.field || item.task_name || '').trim(); - if (!field) { - this.$message.warning(this.$t('crawlTask.missingKeyword')); - return; - } this.runOnceLoadingId = item.id; try { - const res = await this.$api.post('/api/expert_finder/fetchOneField', { field }); - if (res && res.code === 0) { - this.$message.success(this.$t('crawlTask.runOnceSuccess')); - await this.fetchList(); - } else { - this.$message.error((res && res.msg) || this.$t('crawlTask.runOnceFailed')); + const res = await this.$api.post('/api/expert_finder/fetchOneField', { field: item.task_name }); + if (res.code === 0) { + this.$message.success(this.$t('crawlTask.runOnceQueued')); + this.fetchList(); } - } catch (e) { - this.$message.error(this.$t('crawlTask.runOnceFailed')); } finally { this.runOnceLoadingId = null; } }, - handleAction(msg, item) { - if (msg === 'logs') { - this.$message.info(`${this.$t('crawlTask.viewLogs')}: #${item.id}`); - return; - } - this.$message.info(msg); + // 其余分页/弹窗逻辑 + handleFilter() { this.currentPage = 1; this.fetchList(); }, + handleSearchClick() { this.handleFilter(); }, + handleSizeChange(val) { this.pageSize = val; this.fetchList(); }, + handlePageChange(val) { this.currentPage = val; this.fetchList(); }, + openAddDialog() { this.addDialogVisible = true; }, + resetAddForm() { this.addForm = { field: '', runNow: false }; this.addLoading = false; }, + async submitAddKeyword() { + if (!this.addForm.field.trim()) return this.$message.warning(this.$t('crawlTask.enterKeyword')); + this.addLoading = true; + try { + const res = await this.$api.post('api/expert_manage/addFetchField', { field: this.addForm.field }); + if (res.code === 0) { + this.$message.success(this.$t('crawlTask.addKeywordSuccess')); + if (this.addForm.runNow) { + await this.$api.post('api/expert_finder/fetchOneField', { field: this.addForm.field }); + } + this.addDialogVisible = false; + this.fetchList(); + } + } finally { this.addLoading = false; } } } }; @@ -390,122 +259,86 @@ export default { \ No newline at end of file