diff --git a/src/layout/WorkbenchLayout.vue b/src/layout/WorkbenchLayout.vue index 0906851..bf79e4c 100644 --- a/src/layout/WorkbenchLayout.vue +++ b/src/layout/WorkbenchLayout.vue @@ -49,7 +49,7 @@ 大哥工作台 - @@ -193,6 +192,9 @@ import placeholderHosts from '@/assets/placeholder-hosts.png' import placeholderWebAi from '@/assets/placeholder-webai.png' import placeholderBigBrother from '@/assets/placeholder-bigbrother.png' +import { getUser } from '@/utils/storage'; +const tenantId = ref(''); + const emit = defineEmits(['logout', 'go-back', 'stop-all']) const currentView = ref('tk') @@ -239,6 +241,9 @@ const notifySidebarWidth = (width) => { onMounted(() => { loadServiceContacts() + + tenantId.value = String(getUser()?.tenantId || ''); + if (!isElectron()) return resizeObserver = new ResizeObserver((entries) => { const width = entries[0]?.contentRect.width diff --git a/src/types/electron.d.ts b/src/types/electron.d.ts index d6a7489..71e9499 100644 --- a/src/types/electron.d.ts +++ b/src/types/electron.d.ts @@ -209,6 +209,7 @@ export interface ElectronAPI { visitAnchor: (id: string) => Promise exportData: (data: string) => Promise controlTask: (data: string) => Promise<{ success: boolean; message?: string; error?: string }> + controlExpertCapture: (data: string) => Promise<{ success: boolean; message?: string; error?: string }> controlCheckTask: (payload: { isRunning: boolean; model: boolean }) => Promise<{ success: boolean; message?: string; error?: string }> getBrotherInfo: () => Promise queryBrotherInfo: (data: string) => Promise diff --git a/src/utils/pythonBridge.js b/src/utils/pythonBridge.js index 96dc1c6..8fabad9 100644 --- a/src/utils/pythonBridge.js +++ b/src/utils/pythonBridge.js @@ -106,6 +106,11 @@ export function usePythonBridge() { await window.electronAPI.tk.controlTask(data); }; + const controlExpertCapture = async (data) => { + if (!inElectron) return { success: false, error: 'Not in Electron' }; + return await window.electronAPI.tk.controlExpertCapture(data); + }; + const controlCheckTask = async (isRunning, model) => { if (!inElectron) return { success: false, error: 'Not in Electron' }; return await window.electronAPI.tk.controlCheckTask({ @@ -250,6 +255,7 @@ export function usePythonBridge() { getTkLoginStatus, // New Fan Workbench exports controlTask, + controlExpertCapture, controlCheckTask, getBrotherInfo, Specifystreaming, diff --git a/src/views/tk/ExpertWorkbench.vue b/src/views/tk/ExpertWorkbench.vue index 43c6abe..24a159e 100644 --- a/src/views/tk/ExpertWorkbench.vue +++ b/src/views/tk/ExpertWorkbench.vue @@ -21,6 +21,22 @@
+
+ +
+ {{ countryData || '未识别' }} + {{ regionCode || '--' }} + + +
+
-
- 当前仓库里还没有达人爬虫的后端启动接口,这里先保留前端配置和任务态,等后端接口接入后再把参数真正传下去。 -
@@ -119,6 +132,9 @@ import { computed, onBeforeUnmount, onMounted, ref } from 'vue'; import { ElMessage } from 'element-plus'; import { getUser } from '@/utils/storage'; import { expertInfoPage } from '@/api/account'; +import { useCountryInfo } from '@/composables/useCountryInfo'; +import { CountryCode } from '@/utils/countryUtil'; +import { usePythonBridge } from '@/utils/pythonBridge'; const userInfo = ref({}); const tenantId = ref(''); @@ -135,11 +151,14 @@ const tableData = ref([]); const isRunning = ref(false); const elapsedSeconds = ref(0); const elapsedTimerId = ref(null); +const { countryData, isLoading: isRefreshingCountry, initCountryInfo, refreshCountry, showEditCountryDialog } = useCountryInfo(); +const { controlExpertCapture } = usePythonBridge(); const stats = computed(() => ({ total: total.value, pageCount: tableData.value.length, })); +const regionCode = computed(() => resolveRegionCode(countryData.value)); const formattedElapsed = computed(() => { const hours = Math.floor(elapsedSeconds.value / 3600); @@ -152,6 +171,7 @@ onMounted(() => { userInfo.value = getUser() || {}; tenantId.value = String(userInfo.value?.tenantId || ''); queryDate.value = buildTodayText(); + void initCountryInfo((key) => key); void loadList(); }); @@ -176,6 +196,15 @@ function normalizeNumber(value) { return Number.isFinite(parsed) ? parsed : undefined; } +function resolveRegionCode(countryName) { + if (!countryName) { + return ''; + } + + const matched = Object.entries(CountryCode).find(([code, name]) => code.length === 2 && name === countryName); + return matched?.[0] || ''; +} + function formatDateTime(value) { if (!value) { return ''; @@ -229,9 +258,23 @@ function validateCrawlRange() { return false; } + if (!regionCode.value) { + ElMessage.error('当前国家无法转换为地区代码,请先修改国家'); + return false; + } + return true; } +function buildCapturePayload(isStart) { + return { + region: regionCode.value, + MinFollowerCount: normalizeNumber(crawlForm.value.fansMin) ?? 0, + MaxFollowerCount: normalizeNumber(crawlForm.value.fansMax) ?? 0, + isStart, + }; +} + async function loadList() { if (!tenantId.value) { tableData.value = []; @@ -270,7 +313,7 @@ function stopElapsedTimer() { } } -function handleStart() { +async function handleStart() { if (!tenantId.value) { ElMessage.error('缺少 tenantId,无法启动达人工作台'); return; @@ -280,17 +323,41 @@ function handleStart() { return; } + const result = await controlExpertCapture(JSON.stringify(buildCapturePayload(true))); + if (!result?.success) { + ElMessage.error(result?.error || '启动达人爬虫失败'); + return; + } + isRunning.value = true; startElapsedTimer(); - ElMessage.success('达人工作台已进入运行态'); + ElMessage.success('达人工作台已开始运行'); } -function handleStop() { +async function handleStop() { + const result = await controlExpertCapture(JSON.stringify(buildCapturePayload(false))); + if (!result?.success) { + ElMessage.error(result?.error || '停止达人爬虫失败'); + return; + } + isRunning.value = false; stopElapsedTimer(); ElMessage.success('达人工作台已停止'); } +async function refreshCountryFn() { + await refreshCountry((key) => key); +} + +async function editCountry() { + try { + await showEditCountryDialog((key) => key); + } catch { + // 用户取消 + } +} + async function handleSearch() { page.value = 1; await loadList();