浏览代码

完成主页展示状态;优化前端存储的逻辑

mrh 9 月之前
父节点
当前提交
48909d8030

+ 4 - 3
ui/backend/config.yaml

@@ -2,20 +2,21 @@ chrome_exe: g:\code\upwork\zhang_crawl_bio\download\chrome-win\chrome.exe
 mimo_exe: g:\code\upwork\zhang_crawl_bio\download\proxy_pool\mihomo-windows-amd64-go120.exe
 redis_exe: g:\code\upwork\zhang_crawl_bio\download\Redis-x64-5.0.14.1\redis-server.exe
 redis_port: null
+sqluri: G:\code\upwork\zhang_crawl_bio\output\temp.db
 sub:
   file: g:\code\upwork\zhang_crawl_bio\download\proxy_pool\6137e542.yaml
   proxies:
     9660:
       file_path: g:\code\upwork\zhang_crawl_bio\download\proxy_pool\temp\9660.yaml
-      name: "\U0001F1F8\U0001F1EC\u4E9A\u9A6C\u900A\u65B0\u52A0\u57612"
+      name: "\U0001F1ED\U0001F1F0\u9999\u6E2F1\u53F7"
       port: 9660
     9662:
       file_path: g:\code\upwork\zhang_crawl_bio\download\proxy_pool\temp\9662.yaml
-      name: "\U0001F1F0\U0001F1F7\u97E9\u56FD\u4E09\u7F51\u4E13\u7EBF2"
+      name: "\U0001F1EF\U0001F1F5\u4E9A\u9A6C\u900A\u65E5\u672C2"
       port: 9662
     9664:
       file_path: g:\code\upwork\zhang_crawl_bio\download\proxy_pool\temp\9664.yaml
-      name: "\U0001F1EF\U0001F1F5\u4E9A\u9A6C\u900A\u65E5\u672C2"
+      name: "\U0001F1F3\U0001F1F1\u8377\u5170\u4E13\u7EBF"
       port: 9664
   start_port: 9660
   temp_dir: g:\code\upwork\zhang_crawl_bio\download\proxy_pool\temp

+ 1 - 1
ui/backend/main.py

@@ -41,5 +41,5 @@ app.include_router(worker_router, prefix="/api/worker", tags=["worker"])
 # 如果你需要运行这个应用,可以使用以下代码
 if __name__ == "__main__":
     import uvicorn
-    # uvicorn main:app --port 5835 --reload
+    # 注意: 由于涉及到多进程管理机制,因此不能使用 reload 。例如 uvicorn main:app --port 5835 --reload
     uvicorn.run(app, host="localhost", port=5835)

+ 12 - 0
ui/docs/gpt/home.md

@@ -0,0 +1,12 @@
+# 缓存改为全局
+能否将 store 改为全局变量,而不是使用本地缓存。我思路是这样的,每次启动后,全局变量都会初始请求有关的 URL 和数据,保存在全局会话中,除非重启或者刷新页面。
+也就是说,不要每次点击路由都请求一次,
+
+# 首页显示代理
+假如代理类型是 poll,那么应该显示名称为 代理池,代理池状态 2/3 。表示 reachable 有2个,total 有3个。
+假如代理类型是 system , 那么应该显示名称为 系统代理,启用状态 。
+```
+selectedProxy poll
+sys_proxy_cache {"timestamp":1740148480397,"data":{"sys_open":true,"proxy_server":"127.0.0.1:1881"}}
+proxies {"timestamp":1740148515594,"data":[{"name":"🇭🇰香港1号","port":9660,"reachable":false,"file_path":"g:\\code\\upwork\\zhang_crawl_bio\\download\\proxy_pool\\temp\\9660.yaml","mgr_url":"https://yacd.metacubex.one/?hostname=127.0.0.1&port=9661&secret=#/proxies","process_info":null,"ping":{}},{"name":"🇯🇵亚马逊日本2","port":9662,"reachable":true,"file_path":"g:\\code\\upwork\\zhang_crawl_bio\\download\\proxy_pool\\temp\\9662.yaml","mgr_url":"https://yacd.metacubex.one/?hostname=127.0.0.1&port=9663&secret=#/proxies","process_info":{"log_file":"G:\\code\\upwork\\zhang_crawl_bio\\ui\\backend\\output\\logs\\process_mgr\\mimo_9662.log","pid":32176,"start_time":1740148439.2248976},"ping":{}},{"name":"🇳🇱荷兰专线","port":9664,"reachable":true,"file_path":"g:\\code\\upwork\\zhang_crawl_bio\\download\\proxy_pool\\temp\\9664.yaml","mgr_url":"https://yacd.metacubex.one/?hostname=127.0.0.1&port=9665&secret=#/proxies","process_info":{"log_file":"G:\\code\\upwork\\zhang_crawl_bio\\ui\\backend\\output\\logs\\process_mgr\\mimo_9664.log","pid":41188,"start_time":1740148341.276417},"ping":{}}]}
+```

+ 32 - 5
ui/fontend/src/components/Home.vue

@@ -23,7 +23,7 @@
       <el-descriptions-item label="文件大小">{{ (uploadResult.size / 1024).toFixed(2) }} KB</el-descriptions-item>
       <el-descriptions-item label="上传时间">{{ uploadResult.uploadTime }}</el-descriptions-item>
       <el-descriptions-item label="关键词总数">{{ uploadResult.total_keywords }}</el-descriptions-item>
-      <el-descriptions-item label="成功插入数">{{ uploadResult.inserted_count }}</el-descriptions-item>
+      <el-descriptions-item label="导入关键词个数">{{ uploadResult.inserted_count }} (如果存在则跳过)</el-descriptions-item>
       <el-descriptions-item label="状态">
         <el-tag :type="uploadResult.success ? 'success' : 'danger'">
           {{ uploadResult.success ? '导入成功' : '导入失败' }}
@@ -37,12 +37,22 @@
     <h2>系统状态总览</h2>
     <el-descriptions :column="3" border>
       <!-- 代理信息 -->
-      <el-descriptions-item label="代理类型">{{ store.selectedProxy }}</el-descriptions-item>
+      <el-descriptions-item label="代理类型">
+        <template v-if="store.selectedProxy === 'poll'">
+          代理池状态 {{ store.proxiesCache?.data?.filter((p: any) => p.reachable).length || 0 }}/{{ store.proxiesCache?.data?.length || 0 }}
+        </template>
+        <template v-else-if="store.selectedProxy === 'system'">
+          系统代理 {{ store.sysProxyCache?.data?.sys_open ? '已启用' : '未启用' }}
+        </template>
+        <template v-else>
+          {{ store.selectedProxy }}
+        </template>
+      </el-descriptions-item>
       
       <!-- 数据库任务 -->
-      <el-descriptions-item label="总任务数">{{ taskStats.total_tasks ?? '-' }}</el-descriptions-item>
-      <el-descriptions-item label="已完成任务">{{ taskStats.completed_tasks ?? '-' }}</el-descriptions-item>
-      <el-descriptions-item label="未完成任务">{{ taskStats.pending_tasks ?? '-' }}</el-descriptions-item>
+      <el-descriptions-item label="总关键词数">{{ taskStats.total_tasks ?? '-' }}</el-descriptions-item>
+      <el-descriptions-item label="已完成关键词">{{ taskStats.completed_tasks ?? '-' }}</el-descriptions-item>
+      <el-descriptions-item label="未完成关键词">{{ taskStats.pending_tasks ?? '-' }}</el-descriptions-item>
       
       <!-- 执行状态 -->
       <el-descriptions-item label="工作服务状态">
@@ -127,6 +137,23 @@ const fetchWorkerEndpoint = async () => {
 // 在组件挂载时获取 worker 地址
 onMounted(async () => {
   await fetchWorkerEndpoint()
+  // 初始化代理数据
+  store.startBackgroundPolling()
+  
+  // 强制初始化代理数据
+  const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || ''
+  await Promise.all([
+    store.cachedFetch(`${apiBaseUrl}/proxy/sys`, 'sys_proxy_cache'),
+    store.cachedFetch(`${apiBaseUrl}/proxy/proxies`, 'proxies')
+  ])
+  console.log('proxies:', store.proxiesCache.data);
+  console.log('sys_proxy_cache:', store.sysProxyCache);
+  console.log('selectedProxy:', store.selectedProxy);
+  
+  
+  
+  // 响应式访问数据
+
   if (isWorkerAvailable.value) {
     await fetchTaskStatistics()
   }

+ 1 - 1
ui/fontend/src/components/Menu.vue

@@ -5,7 +5,7 @@
       <el-radio-button :value="true">collapse</el-radio-button>
     </el-radio-group>
     <el-menu
-      default-active="2"
+      default-active="1"
       :router="true"
       class="el-menu-vertical-demo"
       :collapse="isCollapse"

+ 41 - 66
ui/fontend/src/components/Proxy.vue

@@ -1,97 +1,72 @@
 <template>
-    <div>
-      <el-row justify="center">
-        <el-radio-group @change="proxy_change" v-model="proxy_choice">
-          <el-radio  value="system" @change="get_sys_proxy" border>系统</el-radio>
-          <el-radio  value="poll" @change="get_sub_url" border>代理池</el-radio>
-        </el-radio-group>
-      </el-row>
-        
-      <el-row v-if="proxy_choice=='system'" justify="center" class="mt-4">
-        <el-descriptions title="系统代理状态" direction="vertical" :column="1" border>
-          <el-descriptions-item label="代理状态">
-            {{ sys_proxy.sys_open ? '已启用' : '未启用' }}
-          </el-descriptions-item>
-          <el-descriptions-item label="代理地址">
-            {{ sys_proxy.proxy_server || '未设置' }}
-          </el-descriptions-item>
-          <el-descriptions-item label="说明">
-            如果选择系统代理,程序会自动跟随系统默认。
-          </el-descriptions-item>
-        </el-descriptions>
-      </el-row>
-      <el-row v-else justify="center" class="mt-4">
-        <ProxyPool />
-      </el-row>
-    </div>
+  <div>
+    <el-row justify="center">
+      <el-radio-group @change="proxy_change" v-model="proxy_choice">
+        <el-radio value="system" @change="get_sys_proxy" border>系统</el-radio>
+        <el-radio value="poll" @change="get_sub_url" border>代理池</el-radio>
+      </el-radio-group>
+    </el-row>
+
+    <el-row v-if="proxy_choice === 'system'" justify="center" class="mt-4">
+      <el-descriptions title="系统代理状态" direction="vertical" :column="1" border>
+        <el-descriptions-item label="代理状态">
+          {{ store.sysProxyCache?.data?.sys_open ? '已启用' : '未启用' }}
+        </el-descriptions-item>
+        <el-descriptions-item label="代理地址">
+          {{ store.sysProxyCache?.data?.proxy_server || '未设置' }}
+        </el-descriptions-item>
+        <el-descriptions-item label="说明">
+          如果选择系统代理,程序会自动跟随系统默认。
+        </el-descriptions-item>
+      </el-descriptions>
+    </el-row>
+    <el-row v-else justify="center" class="mt-4">
+      <ProxyPool />
+    </el-row>
+  </div>
 </template>
-    
+
 <script setup lang="ts">
-import { ref } from 'vue'
-import { onMounted } from 'vue'
+import { ref, onMounted } from 'vue' // 确保导入onMounted
 import ProxyPool from './ProxyPool.vue'
-
 import { useProxyStore } from '../stores/proxyStore'
+import { ElMessage } from 'element-plus'
+
 const store = useProxyStore()
 const proxy_choice = ref<string>(store.selectedProxy)
 const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || ''
-const sys_proxy = ref<{sys_open: boolean, proxy_server: string}>({
-  sys_open: false,
-  proxy_server: ''
+
+// 在组件挂载时获取初始数据
+onMounted(() => {
+  Promise.all([get_sys_proxy(), get_sub_url()]).catch((error) =>
+    console.error('初始化失败:', error)
+  )
 })
-const subUrl = ref('')
 
-// 获取订阅链接(带缓存)
 const get_sub_url = async () => {
   try {
-    const cacheKey = 'sub_url_cache'
-    const data = await store.cachedFetch(`${apiBaseUrl}/proxy/subs`, cacheKey)
-    subUrl.value = data.msg?.url || ''
+    await store.cachedFetch(`${apiBaseUrl}/proxy/subs`, store.CACHE_KEYS.SUB_URL)
   } catch (error) {
     console.error('获取订阅链接失败:', error)
     ElMessage.error('获取订阅信息失败')
   }
 }
 
-// 获取系统代理信息(带缓存)
 const get_sys_proxy = async () => {
   try {
-    const cacheKey = 'sys_proxy_cache'
-    const data = await store.cachedFetch(`${apiBaseUrl}/proxy/sys`, cacheKey)
-    sys_proxy.value = data
-    store.setProxyData(data) // 保持store数据同步
+    const data= await store.cachedFetch(`${apiBaseUrl}/proxy/sys`, store.CACHE_KEYS.SYS_PROXY)
+    console.log('get_sys_proxy data:', data)
   } catch (error) {
     console.error('获取代理信息失败:', error)
     ElMessage.error('获取代理信息失败')
   }
 }
 
-// 初始化时自动获取并启动后台轮询
-onMounted(async () => {
-  store.startBackgroundPolling()
-  try {
-    await Promise.all([get_sys_proxy(), get_sub_url()])
-  } catch (error) {
-    console.error('初始化失败:', error)
-  }
-})
-
-// 切换代理类型时强制刷新
-const proxy_change = async (value: any) => {
+const proxy_change = (value: string) => {
   store.setSelectedProxy(value)
-  if (value === 'system') {
-    await store.cachedFetch(`${apiBaseUrl}/proxy/sys`, 'sys_proxy_cache', true)
-    store.setProxyData(sys_proxy.value)
-  } else if (value === 'poll') {
-    await store.cachedFetch(`${apiBaseUrl}/proxy/subs`, 'sub_url_cache', true)
-  }
-  // 立即更新显示数据
-  sys_proxy.value = { ...store.sysProxyData }
 }
-
 </script>
 
 <style scoped>
-
-</style>
-  
+/* 样式保持不变 */
+</style>

+ 8 - 14
ui/fontend/src/components/ProxyPool.vue

@@ -87,19 +87,10 @@ const selectedProxy = ref<ProxyResponse | null>(null)
 const fetchProxyList = async (useCatch=true) => {
 try {
     loading.value = true
-    // let response:Response
-    let data: ProxyResponse[]
-    const cacheKey = 'fetchProxyList'
-    if (!useCatch) {
-      localStorage.removeItem(cacheKey) 
-    }
-      data = await store.cachedFetch(`${apiBaseUrl}/proxy/proxies`, cacheKey)
-      console.log(`useCatch ${cacheKey}: `, data)
-      // response = await fetch(`${apiBaseUrl}/proxy/proxies`)
-      // if (!response.ok) throw new Error('获取代理列表失败')
-      // data = await response.json()
-    
-    proxyList.value = data.map((proxy: ProxyResponse) => ({
+      const data = await store.cachedFetch(`${apiBaseUrl}/proxy/proxies`, 'proxies', !useCatch)
+    // 处理两种可能的响应格式:直接数组或包含data属性的对象
+    const proxyData = Array.isArray(data) ? data : data.data
+    proxyList.value = proxyData.map((proxy: ProxyResponse) => ({
       ...proxy,
       isLoading: proxy.isLoading || false
     }))
@@ -132,6 +123,8 @@ try {
     const result: ProxyPostResponse = await response.json()
     proxy.reachable = true
     proxy.mgr_url = result.data.mgr_url
+    await store.cachedFetch(`${apiBaseUrl}/proxy/proxies`, 'proxies', true)
+
 } catch (error) {
     console.error(error)
 } finally {
@@ -146,7 +139,8 @@ const closeProxy = async (proxy: ProxyResponse) => {
         })
         if (!response.ok) throw new Error('关闭代理失败')
         proxy.reachable = false
-    } catch (error) {
+        await store.cachedFetch(`${apiBaseUrl}/proxy/proxies`, 'proxies', true)
+      } catch (error) {
         console.error(error)
     } finally {
         proxy.isLoading = false

+ 23 - 20
ui/fontend/src/stores/proxyStore.ts

@@ -1,9 +1,14 @@
 import { defineStore } from 'pinia'
 import { ref } from 'vue'
 
-// 缓存配置
+// 缓存配置和允许的缓存键
 const CACHE_TTL = 300000 // 5分钟缓存有效期(后台会自动刷新)
 const POLL_INTERVAL = 60000 // 1分钟轮询间隔
+const CACHE_KEYS = {
+  SYS_PROXY: 'sys_proxy_cache',
+  SUB_URL: 'sub_url_cache',
+  PROXIES_POOL: 'proxies'
+} as const
 
 interface CacheItem {
   timestamp: number
@@ -13,7 +18,6 @@ interface CacheItem {
 
 export const useProxyStore = defineStore('proxy', () => {
   const selectedProxy = ref(localStorage.getItem('selectedProxy') || 'system')
-  const sysProxyData = ref(JSON.parse(localStorage.getItem('sysProxyData') || '{}'))
   const backgroundPollTimer = ref<number>()
 
   // 带缓存的请求方法
@@ -93,9 +97,9 @@ export const useProxyStore = defineStore('proxy', () => {
       try {
         const apiBaseUrl = import.meta.env.VITE_API_BASE_URL || ''
         await Promise.allSettled([
-          cachedFetch(`${apiBaseUrl}/proxy/sys`, 'sys_proxy_cache', true),
-          cachedFetch(`${apiBaseUrl}/proxy/subs`, 'sub_url_cache', true),
-          cachedFetch(`${apiBaseUrl}/proxy/proxies`,'proxies', true),
+          cachedFetch(`${apiBaseUrl}/proxy/sys`, CACHE_KEYS.SYS_PROXY, true),
+          cachedFetch(`${apiBaseUrl}/proxy/subs`, CACHE_KEYS.SUB_URL, true),
+          cachedFetch(`${apiBaseUrl}/proxy/proxies`, CACHE_KEYS.PROXIES_POOL, true),
         ])
         console.log('[Background Poll] 缓存自动刷新完成')
       } catch (error) {
@@ -111,23 +115,22 @@ export const useProxyStore = defineStore('proxy', () => {
     }
   }
 
-  function setProxyData(data: any) {
-    sysProxyData.value = data
-    localStorage.setItem('sysProxyData', JSON.stringify(data))
-    // 同时更新系统代理缓存
-    localStorage.setItem('sys_proxy_cache', JSON.stringify({
-      timestamp: Date.now(),
-      data
-    }))
-  }
 
-  return { 
-    selectedProxy, 
-    sysProxyData, 
-    setSelectedProxy, 
-    setProxyData,
+  // 暴露缓存数据
+  const sysProxyCache = computed(() => JSON.parse(localStorage.getItem(CACHE_KEYS.SYS_PROXY) || 'null'))
+  const subUrlCache = computed(() => JSON.parse(localStorage.getItem(CACHE_KEYS.SUB_URL) || 'null'))
+  const proxiesCache = computed(() => JSON.parse(localStorage.getItem(CACHE_KEYS.PROXIES_POOL) || 'null'))
+
+  return {
+    setSelectedProxy,
     cachedFetch,
     startBackgroundPolling,
-    stopBackgroundPolling
+    stopBackgroundPolling,
+    // 暴露缓存数据
+    CACHE_KEYS,
+    selectedProxy,
+    sysProxyCache,
+    subUrlCache,
+    proxiesCache
   }
 })