|
|
|
|
@ -6,17 +6,19 @@
|
|
|
|
|
<div class="toolbar">
|
|
|
|
|
<el-button type="primary" :loading="starting" :disabled="running || !batchLoaded || !!configurationError" @click="start()">获取更新</el-button>
|
|
|
|
|
<el-button @click="openSettings">分类留存设置</el-button>
|
|
|
|
|
<el-button @click="openAutoSettings">自动获取更新设置</el-button>
|
|
|
|
|
<el-button @click="openHistory">历史批次</el-button>
|
|
|
|
|
<el-button v-if="batch" @click="openTasks(batch.id, 'failed')">失败明细</el-button>
|
|
|
|
|
<el-button v-if="batch && batch.failed && !running" :loading="starting" :disabled="!!configurationError" @click="start(batch.id)">仅重试失败企业</el-button>
|
|
|
|
|
<span class="hint">获取全部候选和已占额企业新闻,不受下方筛选影响;不更改企业户状态。</span>
|
|
|
|
|
<span class="hint">获取全部候选和已占额企业新闻,不受下方筛选影响;候选企业成功返回有效业务数据后转为已占额。</span>
|
|
|
|
|
</div>
|
|
|
|
|
<template v-if="batch">
|
|
|
|
|
<p>批次 #{{ batch.id }} · {{ running ? '采集中' : '已结束' }} · 开始时间 {{ batch.created_at }}</p>
|
|
|
|
|
<p>批次 #{{ batch.id }}({{ batch.scheduled_date ? '自动' : '手动' }}) · {{ running ? '采集中' : '已结束' }} · 开始时间 {{ batch.created_at }}</p>
|
|
|
|
|
<div class="metrics">
|
|
|
|
|
<span>企业总数 <b>{{ batch.total }}</b></span><span>已处理 <b>{{ batch.processed }}</b></span>
|
|
|
|
|
<span>成功 <b>{{ batch.success }}</b></span><span>失败 <b>{{ batch.failed }}</b></span><span>新增新闻 <b>{{ batch.inserted }}</b></span>
|
|
|
|
|
</div>
|
|
|
|
|
<p v-if="batch.group_size" class="hint">每组 {{ batch.group_size }} 家 · 共 {{ batch.group_total }} 组 · 已完成 {{ batch.group_processed }} 组</p>
|
|
|
|
|
<el-progress :percentage="batch.progress" :status="running ? undefined : (batch.failed ? 'warning' : 'success')" />
|
|
|
|
|
<p v-if="running" class="hint">{{ batch.current_company ? '当前处理:' + batch.current_company : '等待后台调度继续处理' }}。关闭页面不影响采集。</p>
|
|
|
|
|
<p v-if="batch.invalid" class="hint">有 {{ batch.invalid }} 条新闻格式异常,详见失败明细;正常新闻已保存。</p>
|
|
|
|
|
@ -67,6 +69,18 @@
|
|
|
|
|
</el-table>
|
|
|
|
|
<el-pagination :current-page="query.page" :page-size="20" :total="total" layout="total, prev, pager, next" @current-change="pageNews" />
|
|
|
|
|
|
|
|
|
|
<el-dialog title="自动获取更新设置" :visible.sync="autoVisible" width="600px" :close-on-click-modal="false">
|
|
|
|
|
<el-form label-width="130px">
|
|
|
|
|
<el-form-item label="启用每日采集"><el-switch v-model="autoForm.enabled" /></el-form-item>
|
|
|
|
|
<el-form-item label="每天开始时间"><el-time-picker v-model="autoForm.start_time" value-format="HH:mm" format="HH:mm" :clearable="false" placeholder="选择开始时间" /></el-form-item>
|
|
|
|
|
<el-form-item label="每组企业数"><el-input-number v-model="autoForm.group_size" :min="1" :max="100" :precision="0" /></el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<p class="hint">时区:{{ autoTimezone }}。建议先使用每组 10 家;每家企业返回的全部新闻处理完后,再处理下一家,单家失败不影响后续企业。</p>
|
|
|
|
|
<p class="hint">常驻进程每组结束后等待 5 秒继续;使用计划任务时按调度继续。请确保服务器采集进程或 Laravel 调度正在运行。</p>
|
|
|
|
|
<p class="hint">每天自动创建一个批次,已有批次未完成则等待。关闭开关只停止新建自动批次,当前批次继续完成。每组数量及分类留存设置在新建批次时固定。</p>
|
|
|
|
|
<p class="hint">如果当天已过开始时间且尚未自动采集,启用后会在下次调度开始。重复新闻不会再次入库,沿用分类留存设置。</p>
|
|
|
|
|
<span slot="footer"><el-button @click="autoVisible = false">取消</el-button><el-button type="primary" :loading="autoSaving" @click="saveAutomation">保存设置</el-button></span>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
<el-dialog title="分类留存设置" custom-class="category-settings-dialog" :visible.sync="settingsVisible" width="650px" top="8vh" :close-on-click-modal="false">
|
|
|
|
|
<p>命中任意一个已选分类即自动留存;不选则全部不留存。数量为全部已入库新闻数量,一条新闻可属于多个分类。</p>
|
|
|
|
|
<div class="category-actions">
|
|
|
|
|
@ -130,12 +144,13 @@
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import { newsCategories, saveCategorySettings, listNews, companies, showNews, saveNews, reviewNews, reviewMany, newsLogs, startBatch, latestBatch, listBatches, listTasks } from '@/api/companyNews'
|
|
|
|
|
import { getAutoSettings, saveAutoSettings, newsCategories, saveCategorySettings, listNews, companies, showNews, saveNews, reviewNews, reviewMany, newsLogs, startBatch, latestBatch, listBatches, listTasks } from '@/api/companyNews'
|
|
|
|
|
export default {
|
|
|
|
|
name: 'CompanyNews',
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
query: { category_codes: [], company_id: '', keyword: '', review_status: 'retained', page: 1 }, dates: [], rows: [], selection: [], total: 0, loading: false,
|
|
|
|
|
autoVisible: false, autoSaving: false, autoTimezone: 'Asia/Shanghai', autoForm: { enabled: false, start_time: '22:00', group_size: 10 },
|
|
|
|
|
categoryOptions: [], savedRetainedCategories: [], retainedCategories: [], categorySequence: 0, settingsVisible: false, settingsSaving: false,
|
|
|
|
|
companyOptions: [], companyLoading: false, companySequence: 0, companyKeyword: '', companyPage: 0, companyTotal: 0, companyHasMore: false, companyScroll: null, listSequence: 0,
|
|
|
|
|
configurationError: '', batch: null, batchLoaded: false, starting: false, pollError: false, timer: null, active: false, pollEpoch: 0,
|
|
|
|
|
@ -157,6 +172,24 @@ export default {
|
|
|
|
|
deactivated() { this.stop(); this.unbindCompanyScroll() },
|
|
|
|
|
beforeDestroy() { this.stop(); this.unbindCompanyScroll(); this.companySequence++ },
|
|
|
|
|
methods: {
|
|
|
|
|
async openAutoSettings() {
|
|
|
|
|
try {
|
|
|
|
|
const result = await getAutoSettings()
|
|
|
|
|
this.autoForm = { enabled: result.enabled, start_time: result.start_time, group_size: result.group_size }
|
|
|
|
|
this.autoTimezone = result.timezone
|
|
|
|
|
this.autoVisible = true
|
|
|
|
|
} catch (_) { /* Request interceptor displays errors. */ }
|
|
|
|
|
},
|
|
|
|
|
async saveAutomation() {
|
|
|
|
|
if (this.autoSaving) return
|
|
|
|
|
if (!this.autoForm.start_time || !Number.isInteger(this.autoForm.group_size) || this.autoForm.group_size < 1 || this.autoForm.group_size > 100) return this.$message.warning('请填写开始时间及 1~100 的每组企业数')
|
|
|
|
|
this.autoSaving = true
|
|
|
|
|
try {
|
|
|
|
|
await saveAutoSettings({ ...this.autoForm })
|
|
|
|
|
this.$message.success('自动获取更新设置已保存')
|
|
|
|
|
this.autoVisible = false
|
|
|
|
|
} catch (_) { /* Request interceptor displays errors. */ } finally { this.autoSaving = false }
|
|
|
|
|
},
|
|
|
|
|
async loadCategories() {
|
|
|
|
|
const sequence = ++this.categorySequence
|
|
|
|
|
try {
|
|
|
|
|
|