新增上下限模式
This commit is contained in:
@@ -111,12 +111,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 接收上限 - 紧凑布局 -->
|
||||
<!-- 上下限模式 -->
|
||||
<label class="flex items-center gap-2 border-l border-gray-200 pl-4 ml-2 cursor-pointer">
|
||||
<input type="checkbox" v-model="gateEnabled" class="w-4 h-4" />
|
||||
<span class="text-gray-700 font-medium whitespace-nowrap">上下限模式</span>
|
||||
</label>
|
||||
|
||||
<!-- 接收上下限 - 紧凑布局 -->
|
||||
<div class="flex items-center gap-2 border-l border-gray-200 pl-4 ml-2">
|
||||
<span class="text-gray-700 font-medium whitespace-nowrap">接收上限</span>
|
||||
<input type="number" min="0" placeholder="无限制" v-model.number="maxCount"
|
||||
@change="updateMaxCount(maxCount)"
|
||||
class="w-20 px-2 py-1 border border-gray-300 rounded text-sm focus:border-blue-500 focus:outline-none" />
|
||||
<template v-if="gateEnabled">
|
||||
<span class="text-gray-700 font-medium whitespace-nowrap">主播下限</span>
|
||||
<input type="number" min="0" placeholder="5" v-model.number="minCount"
|
||||
class="w-20 px-2 py-1 border border-gray-300 rounded text-sm focus:border-blue-500 focus:outline-none" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -255,14 +266,21 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
||||
import { isElectron } from '../utils/electronBridge'
|
||||
import { getPermissions } from '@/utils/storage'
|
||||
import { usePythonBridge } from '@/utils/pythonBridge'
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean, required: true }
|
||||
})
|
||||
const emit = defineEmits(['close', 'save'])
|
||||
const { controlCheckTask } = usePythonBridge()
|
||||
|
||||
const HOST_LIST_MIN_COUNT_KEY = 'host_list_dialog_min_count'
|
||||
const HOST_LIST_GATE_ENABLED_KEY = 'host_list_dialog_gate_enabled'
|
||||
const HOST_LIST_GATE_STATE_KEY = 'host_list_dialog_gate_state'
|
||||
const HOST_LIST_MONITOR_INTERVAL_MS = 30000
|
||||
|
||||
// Level Options
|
||||
const LEVEL_OPTIONS = [
|
||||
@@ -315,9 +333,13 @@ const filters = ref({
|
||||
minOnlineFans: '',
|
||||
maxOnlineFans: '',
|
||||
})
|
||||
const gateEnabled = ref(false)
|
||||
const minCount = ref(5)
|
||||
const maxCount = ref()
|
||||
const checkTaskGateState = ref(null)
|
||||
const selectedLevels = ref(new Set())
|
||||
const showLevelDropdown = ref(false)
|
||||
let hostListMonitorTimer = null
|
||||
|
||||
const resolveRestrictedMaxAnchorCount = (fallbackValue = 9999999) => {
|
||||
const permissions = getPermissions()
|
||||
@@ -379,6 +401,7 @@ const parsedIds = computed(() => {
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (newVal) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
loadLocalGateConfig()
|
||||
loadHosts()
|
||||
loadConfig()
|
||||
} else {
|
||||
@@ -387,11 +410,19 @@ watch(() => props.visible, (newVal) => {
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
loadLocalGateConfig()
|
||||
loadConfig()
|
||||
|
||||
if (props.visible) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
loadHosts()
|
||||
loadConfig()
|
||||
}
|
||||
|
||||
startHostListMonitor()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopHostListMonitor()
|
||||
})
|
||||
|
||||
// 监听过滤器变化,同步到后端配置
|
||||
@@ -460,6 +491,117 @@ const loadConfig = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const startHostListMonitor = () => {
|
||||
stopHostListMonitor()
|
||||
|
||||
if (!isElectron()) return
|
||||
|
||||
hostListMonitorTimer = setInterval(async () => {
|
||||
if (!gateEnabled.value) return
|
||||
await loadHosts()
|
||||
}, HOST_LIST_MONITOR_INTERVAL_MS)
|
||||
}
|
||||
|
||||
const stopHostListMonitor = () => {
|
||||
if (!hostListMonitorTimer) return
|
||||
clearInterval(hostListMonitorTimer)
|
||||
hostListMonitorTimer = null
|
||||
}
|
||||
|
||||
const loadLocalGateConfig = () => {
|
||||
try {
|
||||
const savedGateEnabled = localStorage.getItem(HOST_LIST_GATE_ENABLED_KEY)
|
||||
gateEnabled.value = savedGateEnabled === 'true'
|
||||
|
||||
const savedMinCount = localStorage.getItem(HOST_LIST_MIN_COUNT_KEY)
|
||||
if (savedMinCount !== null && savedMinCount !== '') {
|
||||
const parsedMinCount = Number(savedMinCount)
|
||||
if (Number.isFinite(parsedMinCount) && parsedMinCount >= 0) {
|
||||
minCount.value = parsedMinCount
|
||||
}
|
||||
}
|
||||
|
||||
const savedGateState = localStorage.getItem(HOST_LIST_GATE_STATE_KEY)
|
||||
if (savedGateState === 'true') {
|
||||
checkTaskGateState.value = true
|
||||
} else if (savedGateState === 'false') {
|
||||
checkTaskGateState.value = false
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[HostListDialog] 读取本地门控配置失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
const persistGateEnabled = () => {
|
||||
try {
|
||||
localStorage.setItem(HOST_LIST_GATE_ENABLED_KEY, String(gateEnabled.value))
|
||||
} catch (e) {
|
||||
console.error('[HostListDialog] 保存上下限模式失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
const persistMinCount = () => {
|
||||
try {
|
||||
if (minCount.value === '' || minCount.value === null || minCount.value === undefined) {
|
||||
localStorage.removeItem(HOST_LIST_MIN_COUNT_KEY)
|
||||
return
|
||||
}
|
||||
|
||||
localStorage.setItem(HOST_LIST_MIN_COUNT_KEY, String(minCount.value))
|
||||
} catch (e) {
|
||||
console.error('[HostListDialog] 保存主播下限失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
const persistGateState = (value) => {
|
||||
try {
|
||||
if (value === null || value === undefined) {
|
||||
localStorage.removeItem(HOST_LIST_GATE_STATE_KEY)
|
||||
return
|
||||
}
|
||||
|
||||
localStorage.setItem(HOST_LIST_GATE_STATE_KEY, String(value))
|
||||
} catch (e) {
|
||||
console.error('[HostListDialog] 保存门控状态失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
const syncControlCheckTaskGate = async (forceSync = false) => {
|
||||
if (!isElectron()) return
|
||||
if (!gateEnabled.value) return
|
||||
|
||||
const hostCount = filteredHosts.value.length
|
||||
const upperLimit = Number(maxCount.value)
|
||||
const lowerLimit = Number(minCount.value)
|
||||
|
||||
let nextState = null
|
||||
|
||||
if (Number.isFinite(upperLimit) && upperLimit >= 0 && hostCount > upperLimit) {
|
||||
nextState = false
|
||||
} else if (Number.isFinite(lowerLimit) && lowerLimit >= 0 && hostCount < lowerLimit) {
|
||||
nextState = true
|
||||
} else if (forceSync && checkTaskGateState.value !== null) {
|
||||
nextState = checkTaskGateState.value
|
||||
}
|
||||
|
||||
if (nextState === null) return
|
||||
if (!forceSync && checkTaskGateState.value === nextState) return
|
||||
|
||||
try {
|
||||
const result = await controlCheckTask(nextState, true)
|
||||
if (result?.success === false) {
|
||||
console.error('[HostListDialog] controlCheckTask 调用失败:', result.error)
|
||||
return
|
||||
}
|
||||
|
||||
checkTaskGateState.value = nextState
|
||||
persistGateState(nextState)
|
||||
console.log('[HostListDialog] 主播库门控状态已更新:', nextState, '当前数量:', hostCount)
|
||||
} catch (e) {
|
||||
console.error('[HostListDialog] controlCheckTask 异常:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
const getAllChildLevels = (parentValue) => {
|
||||
const parent = LEVEL_OPTIONS.find(p => p.value === parentValue)
|
||||
@@ -548,6 +690,33 @@ const updateMaxCount = async (value) => {
|
||||
}
|
||||
}
|
||||
|
||||
watch(minCount, () => {
|
||||
persistMinCount()
|
||||
})
|
||||
|
||||
watch(gateEnabled, async (enabled) => {
|
||||
persistGateEnabled()
|
||||
|
||||
if (enabled) {
|
||||
void syncControlCheckTaskGate(true)
|
||||
return
|
||||
}
|
||||
|
||||
checkTaskGateState.value = null
|
||||
persistGateState(null)
|
||||
|
||||
if (!isElectron()) return
|
||||
|
||||
try {
|
||||
const result = await controlCheckTask(true, false)
|
||||
if (result?.success === false) {
|
||||
console.error('[HostListDialog] 关闭上下限模式时恢复放行失败:', result.error)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[HostListDialog] 关闭上下限模式时恢复放行异常:', e)
|
||||
}
|
||||
})
|
||||
|
||||
const filteredHosts = computed(() => {
|
||||
return hosts.value.filter(h => {
|
||||
if (!filters.value.gold && h.invitationType === 2) return false
|
||||
@@ -565,6 +734,13 @@ const filteredHosts = computed(() => {
|
||||
})
|
||||
})
|
||||
|
||||
watch(
|
||||
[() => filteredHosts.value.length, minCount, maxCount, gateEnabled],
|
||||
() => {
|
||||
void syncControlCheckTaskGate()
|
||||
}
|
||||
)
|
||||
|
||||
const selectedCount = computed(() => selected.value.size)
|
||||
|
||||
const toggleSelect = (id) => {
|
||||
|
||||
1
src/types/electron.d.ts
vendored
1
src/types/electron.d.ts
vendored
@@ -205,6 +205,7 @@ export interface ElectronAPI {
|
||||
visitAnchor: (id: string) => Promise<void>
|
||||
exportData: (data: string) => Promise<void>
|
||||
controlTask: (data: string) => Promise<{ success: boolean; message?: string; error?: string }>
|
||||
controlCheckTask: (payload: { isRunning: boolean; model: boolean }) => Promise<{ success: boolean; message?: string; error?: string }>
|
||||
getBrotherInfo: () => Promise<string>
|
||||
queryBrotherInfo: (data: string) => Promise<string>
|
||||
getAllBrotherInfo: () => Promise<string>
|
||||
|
||||
@@ -106,6 +106,14 @@ export function usePythonBridge() {
|
||||
await window.electronAPI.tk.controlTask(data);
|
||||
};
|
||||
|
||||
const controlCheckTask = async (isRunning, model) => {
|
||||
if (!inElectron) return { success: false, error: 'Not in Electron' };
|
||||
return await window.electronAPI.tk.controlCheckTask({
|
||||
isRunning: Boolean(isRunning),
|
||||
model: Boolean(model)
|
||||
});
|
||||
};
|
||||
|
||||
const getBrotherInfo = async () => {
|
||||
if (!inElectron) return { total: 0, valid: 0 };
|
||||
const res = await window.electronAPI.tk.getBrotherInfo();
|
||||
@@ -242,6 +250,7 @@ export function usePythonBridge() {
|
||||
getTkLoginStatus,
|
||||
// New Fan Workbench exports
|
||||
controlTask,
|
||||
controlCheckTask,
|
||||
getBrotherInfo,
|
||||
Specifystreaming,
|
||||
storageSetInfos,
|
||||
|
||||
Reference in New Issue
Block a user