diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index d181a81..577fc6a 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -79,7 +79,7 @@ import { getToken, setToken, setUser, setUserPass, getUserPass } from '@/stores/ import { ElLoading, ElMessage } from 'element-plus'; import { passToken } from '@/api/ios'; -let version = ref('3.3.0'); +let version = ref('3.6.0'); onMounted(() => { diff --git a/src/views/VideoStream.vue b/src/views/VideoStream.vue index 420c57d..7b64ba4 100644 --- a/src/views/VideoStream.vue +++ b/src/views/VideoStream.vue @@ -21,12 +21,16 @@ 上传日志
- 监听爬虫 + + 设置 + 剩余 {{ sseCrawlerRemaining }}/{{ sseCrawlerLimit }} 条 + 设置 + 剩余 {{ sseBossRemaining }}/{{ sseBossLimit }} 条
@@ -118,7 +122,7 @@
- 分钟换一次 + 小时换一次
联盟号
@@ -287,18 +291,122 @@ const isLocked = (type) => !!runType.value && runType.value !== type // —— SSE 开关(分别控制爬虫队列 & 大哥队列)—— const sseCrawlerEnabled = ref(JSON.parse(localStorage.getItem('SSE_CRAWLER_ENABLED') ?? 'true')) // q.tenant.* const sseBossEnabled = ref(JSON.parse(localStorage.getItem('SSE_BOSS_ENABLED') ?? 'false')) // b.tenant.* +const sseCrawlerLimit = ref(Number(localStorage.getItem('SSE_CRAWLER_LIMIT') ?? '500')) +const sseBossLimit = ref(Number(localStorage.getItem('SSE_BOSS_LIMIT') ?? '500')) +const sseCrawlerRemaining = ref(Number(localStorage.getItem('SSE_CRAWLER_REMAIN') ?? sseCrawlerLimit.value)) +const sseBossRemaining = ref(Number(localStorage.getItem('SSE_BOSS_REMAIN') ?? sseBossLimit.value)) + +const normalizeSseLimit = (val, fallback) => { + const n = Number(val) + if (!Number.isFinite(n) || n <= 0) return fallback + return Math.floor(n) +} watch(sseCrawlerEnabled, v => { localStorage.setItem('SSE_CRAWLER_ENABLED', JSON.stringify(v)) + if (v && sseCrawlerRemaining.value <= 0) { + sseCrawlerEnabled.value = false + ElMessage.warning('请先设置监听爬虫条数') + } }) watch(sseBossEnabled, v => { localStorage.setItem('SSE_BOSS_ENABLED', JSON.stringify(v)) + if (v && sseBossRemaining.value <= 0) { + sseBossEnabled.value = false + ElMessage.warning('请先设置监听大哥条数') + } +}) + +watch(sseCrawlerLimit, v => { + const next = normalizeSseLimit(v, 500) + if (next !== v) { + sseCrawlerLimit.value = next + return + } + localStorage.setItem('SSE_CRAWLER_LIMIT', String(next)) +}) + +watch(sseBossLimit, v => { + const next = normalizeSseLimit(v, 500) + if (next !== v) { + sseBossLimit.value = next + return + } + localStorage.setItem('SSE_BOSS_LIMIT', String(next)) +}) + +watch(sseCrawlerRemaining, v => { + const next = normalizeSseLimit(v, 0) + if (next !== v) { + sseCrawlerRemaining.value = next + return + } + localStorage.setItem('SSE_CRAWLER_REMAIN', String(next)) +}) + +watch(sseBossRemaining, v => { + const next = normalizeSseLimit(v, 0) + if (next !== v) { + sseBossRemaining.value = next + return + } + localStorage.setItem('SSE_BOSS_REMAIN', String(next)) }) // 至少有一个开着才算启用 SSE const sseAnyEnabled = computed(() => sseCrawlerEnabled.value || sseBossEnabled.value) +const bumpSseCount = (type) => { + const isCrawler = type === 'crawler' + const remainingRef = isCrawler ? sseCrawlerRemaining : sseBossRemaining + if (remainingRef.value <= 0) { + if (isCrawler) { + sseCrawlerEnabled.value = false + } else { + sseBossEnabled.value = false + } + return false + } + + remainingRef.value -= 1 + if (remainingRef.value <= 0) { + if (isCrawler) { + sseCrawlerEnabled.value = false + } else { + sseBossEnabled.value = false + } + ElMessage.info(`监听${isCrawler ? '爬虫' : '大哥'}条数已用完,已自动关闭`) + } + return true +} + +const setSseLimit = async (type) => { + const isCrawler = type === 'crawler' + const label = isCrawler ? '爬虫' : '大哥' + const limitRef = isCrawler ? sseCrawlerLimit : sseBossLimit + const remainingRef = isCrawler ? sseCrawlerRemaining : sseBossRemaining + + try { + const { value } = await ElMessageBox.prompt( + `请输入监听${label}条数`, + '设置监听条数', + { + inputValue: String(limitRef.value || 0), + inputPattern: /^[1-9]\d*$/, + inputErrorMessage: '请输入正整数', + confirmButtonText: '确定', + cancelButtonText: '取消', + } + ) + const next = normalizeSseLimit(value, limitRef.value || 500) + limitRef.value = next + remainingRef.value = next + } catch (e) { + // 用户取消 + } +} + // 互斥按钮的样式:激活=红,锁定=半透明且禁点 const ctrlStyle = (type) => { const lockedByMode = isLocked(type) @@ -797,6 +905,11 @@ onMounted(async () => { // 2️⃣ 按开关过滤 if (fromCrawler && !sseCrawlerEnabled.value) return if (fromBoss && !sseBossEnabled.value) return + if (fromCrawler) { + if (!bumpSseCount('crawler')) return + } else if (fromBoss) { + if (!bumpSseCount('boss')) return + } if (isUnknown && !sseCrawlerEnabled.value) return // 老数据当爬虫看 // 3️⃣ 按来源取不同字段 @@ -1226,8 +1339,8 @@ function startScheduleLoop() { const now = Date.now() if (!lastInterruptTs) lastInterruptTs = now // 首次初始化 - // const due = now - lastInterruptTs >= interruptEveryMin.value * 60_000 * 60 - const due = now - lastInterruptTs >= interruptEveryMin.value * 60_000 + const due = now - lastInterruptTs >= interruptEveryMin.value * 60_000 * 60 + // const due = now - lastInterruptTs >= interruptEveryMin.value * 60_000 console.log( 'due=', due, @@ -1658,11 +1771,13 @@ const markPendingForOffline = (ids) => { }; const onToggleCrawler = (val) => { - if (val) sseBossEnabled.value = false -} + if (val) sseBossEnabled.value = false; +}; + const onToggleBoss = (val) => { - if (val) sseCrawlerEnabled.value = false -} + if (val) sseCrawlerEnabled.value = false; +}; + function timestampToTime(timestamp_ms) { const date = new Date(timestamp_ms);