From 569bc3c8ec8b55a5f3e16363885c748d4843cf8f Mon Sep 17 00:00:00 2001 From: jxxghp Date: Tue, 1 Jul 2025 11:38:00 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AB=99=E7=82=B9=E6=B7=BB=E5=8A=A0=E7=AD=9B?= =?UTF-8?q?=E9=80=89=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/cards/SiteCard.vue | 36 +++--- src/locales/en-US.ts | 9 ++ src/locales/zh-CN.ts | 9 ++ src/locales/zh-TW.ts | 9 ++ src/views/site/SiteCardListView.vue | 181 ++++++++++++++++++++++++++-- 5 files changed, 213 insertions(+), 31 deletions(-) diff --git a/src/components/cards/SiteCard.vue b/src/components/cards/SiteCard.vue index 4017dd7b..5333e37d 100644 --- a/src/components/cards/SiteCard.vue +++ b/src/components/cards/SiteCard.vue @@ -24,10 +24,11 @@ const { t } = useI18n() const cardProps = defineProps({ site: Object as PropType, data: Object as PropType, + stats: Object as PropType, }) // 定义触发的自定义事件 -const emit = defineEmits(['update', 'remove']) +const emit = defineEmits(['update', 'remove', 'refresh-stats']) // 确认框 const createConfirm = useConfirm() @@ -56,9 +57,6 @@ const resourceDialog = ref(false) // 用户数据弹窗 const siteUserDataDialog = ref(false) -// 站点使用统计 -const siteStats = ref({}) - // 查询站点图标 async function getSiteIcon() { try { @@ -84,16 +82,8 @@ async function testSite() { testButtonText.value = t('site.testConnectivity') testButtonDisable.value = false - getSiteStats() - } catch (error) { - console.error(error) - } -} - -// 查询站点使用统计 -async function getSiteStats() { - try { - siteStats.value = await api.get(`site/statistic/${cardProps.site?.domain}`) + // 测试完成后刷新统计数据 + emit('refresh-stats', cardProps.site?.domain) } catch (error) { console.error(error) } @@ -140,16 +130,17 @@ async function deleteSiteInfo() { // 根据站点状态显示不同的状态图标 const statColor = computed(() => { - if (isNullOrEmptyObject(siteStats.value)) { + if (!cardProps.stats || isNullOrEmptyObject(cardProps.stats)) { return 'secondary' } - if (siteStats.value?.lst_state == 1) { + if (cardProps.stats?.lst_state === 1) { return 'error' - } else if (siteStats.value?.lst_state == 0) { - if (!siteStats.value?.seconds) return 'secondary' - if (siteStats.value?.seconds >= 5) return 'warning' + } else if (cardProps.stats?.lst_state === 0) { + if (!cardProps.stats?.seconds) return 'secondary' + if (cardProps.stats?.seconds >= 5) return 'warning' return 'success' } + return 'secondary' }) // 数据百分比计算 @@ -185,19 +176,20 @@ function saveSite() { // 更新站点Cookie UA后的回调 function onSiteCookieUpdated() { siteCookieDialog.value = false - getSiteStats() + // Cookie更新后刷新统计数据 + emit('refresh-stats', cardProps.site?.domain) } // 资源浏览弹窗关闭后的回调 function onSiteResourceDone() { resourceDialog.value = false - getSiteStats() + // 资源操作完成后刷新统计数据 + emit('refresh-stats', cardProps.site?.domain) } // 装载时查询站点图标 onMounted(() => { getSiteIcon() - getSiteStats() }) diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index 9621fd26..f7902289 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -19,6 +19,11 @@ export default { noData: 'No data', noContent: 'No relevant content found', all: 'All', + active: 'Active', + inactive: 'Inactive', + filter: 'Filter', + noMatchingData: 'No matching data', + tryChangingFilters: 'Try changing filters', default: 'Default', name: 'Name', create: 'Create', @@ -884,6 +889,10 @@ export default { testing: 'Testing ...', testSuccess: '{name} connectivity test successful, ready to use!', testFailed: '{name} connectivity test failed: {message}', + connectionNormal: 'Connection Normal', + connectionSlow: 'Connection Slow', + connectionFailed: 'Connection Failed', + connectionUnknown: 'Connection Unknown', deleteConfirm: 'Are you sure you want to delete this site?', deleteSuccess: '{name} deleted successfully!', deleteFailed: '{name} deletion failed: {message}', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index abcb19d7..2ed66c24 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -19,6 +19,11 @@ export default { noData: '暂无数据', noContent: '没有找到相关内容', all: '全部', + active: '激活', + inactive: '未激活', + filter: '筛选', + noMatchingData: '没有符合条件的数据', + tryChangingFilters: '请尝试更改筛选条件', default: '默认', name: '名称', create: '新建', @@ -881,6 +886,10 @@ export default { testing: '测试中 ...', testSuccess: '{name} 连通性测试成功,可正常使用!', testFailed: '{name} 连通性测试失败:{message}', + connectionNormal: '连接正常', + connectionSlow: '连接缓慢', + connectionFailed: '连接失败', + connectionUnknown: '连接未知', deleteConfirm: '是否确认删除站点?', deleteSuccess: '{name} 删除成功!', deleteFailed: '{name} 删除失败:{message}', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 109b97a5..7fc64452 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -19,6 +19,11 @@ export default { noData: '暫無數據', noContent: '沒有找到相關內容', all: '全部', + active: '激活', + inactive: '未激活', + filter: '篩選', + noMatchingData: '沒有符合條件的數據', + tryChangingFilters: '請嘗試更改篩選條件', default: '默認', name: '名稱', create: '新建', @@ -880,6 +885,10 @@ export default { testing: '測試中 ...', testSuccess: '{name} 連通性測試成功,可正常使用!', testFailed: '{name} 連通性測試失敗:{message}', + connectionNormal: '連接正常', + connectionSlow: '連接緩慢', + connectionFailed: '連接失敗', + connectionUnknown: '連接未知', deleteConfirm: '是否確認刪除站點?', deleteSuccess: '{name} 刪除成功!', deleteFailed: '{name} 刪除失敗:{message}', diff --git a/src/views/site/SiteCardListView.vue b/src/views/site/SiteCardListView.vue index d5d7b03f..a8d1fd7b 100644 --- a/src/views/site/SiteCardListView.vue +++ b/src/views/site/SiteCardListView.vue @@ -22,6 +22,9 @@ const siteList = ref([]) // 站点数据列表 const userDataList = ref([]) +// 站点统计数据列表 +const siteStatsList = ref<{ [domain: string]: any }>({}) + // 是否刷新过 const isRefreshed = ref(false) @@ -31,6 +34,44 @@ const loading = ref(false) // 新增站点对话框 const siteAddDialog = ref(false) +// 筛选相关 +const filterMenu = ref(false) +const filterOption = ref('all') // all, active, inactive, connected, slow, failed, unknown + +// 筛选选项 +const filterOptions = computed(() => [ + { value: 'all', label: t('common.all'), icon: 'mdi-format-list-bulleted' }, + { value: 'active', label: t('common.active'), icon: 'mdi-check-circle', color: 'success' }, + { value: 'inactive', label: t('common.inactive'), icon: 'mdi-stop-circle', color: 'error' }, + { value: 'connected', label: t('site.connectionNormal'), icon: 'mdi-wifi', color: 'success' }, + { value: 'slow', label: t('site.connectionSlow'), icon: 'mdi-wifi-strength-2', color: 'warning' }, + { value: 'failed', label: t('site.connectionFailed'), icon: 'mdi-wifi-off', color: 'error' }, + { value: 'unknown', label: t('site.connectionUnknown'), icon: 'mdi-help-circle', color: 'secondary' }, +]) + +// 筛选后的站点列表 +const filteredSiteList = computed(() => { + if (filterOption.value === 'all') { + return siteList.value + } + return siteList.value.filter(site => { + if (filterOption.value === 'active') { + return site.is_active + } else if (filterOption.value === 'inactive') { + return !site.is_active + } else if (['connected', 'slow', 'failed', 'unknown'].includes(filterOption.value)) { + const connectionStatus = getConnectionStatus(site.domain) + return connectionStatus === filterOption.value + } + return true + }) +}) + +// 当前筛选选项的显示信息 +const currentFilter = computed(() => { + return filterOptions.value.find(option => option.value === filterOption.value) +}) + // 获取站点列表数据 async function fetchData() { try { @@ -38,6 +79,8 @@ async function fetchData() { siteList.value = await api.get('site/') loading.value = false isRefreshed.value = true + // 获取站点列表后,获取统计数据 + await fetchSiteStats() } catch (error) { console.error(error) } @@ -52,10 +95,54 @@ async function fetchUserData() { } } +// 获取站点统计数据 +async function fetchSiteStats() { + try { + // 使用批量接口一次性获取所有站点统计数据 + const response = await api.get('site/statistic') + const stats = response.data || response + + // 将数组转换为以domain为键的对象 + const statsMap: { [domain: string]: any } = {} + if (Array.isArray(stats)) { + stats.forEach((stat: any) => { + if (stat.domain) { + statsMap[stat.domain] = stat + } + }) + } + siteStatsList.value = statsMap + } catch (error) { + console.error('Failed to fetch site statistics:', error) + siteStatsList.value = {} + } +} + +// 根据站点统计数据判断连接状态 +function getConnectionStatus(domain: string) { + const stats = siteStatsList.value[domain] + if (!stats || Object.keys(stats).length === 0) { + return 'unknown' + } + if (stats.lst_state === 1) { + return 'failed' + } else if (stats.lst_state === 0) { + if (!stats.seconds) return 'unknown' + if (stats.seconds >= 5) return 'slow' + return 'connected' + } + return 'unknown' +} + // 保存站点排序 async function savaSitesPriority() { + // 只在显示全部站点时允许排序 + if (filterOption.value !== 'all') { + return + } + // 重新排序 - const priorities = siteList.value.map((site, index) => ({ id: site.id, pri: index + 1 })) + const priorities = filteredSiteList.value.map((site, index) => ({ id: site.id, pri: index + 1 })) try { const result: { [key: string]: any } = await api.post('site/priorities', priorities) if (result.success) { @@ -71,12 +158,39 @@ function getUserData(domain: string) { return userDataList.value.find(userData => userData.domain === domain) } +// 根据站点域名获取统计数据 +function getSiteStats(domain: string) { + return siteStatsList.value[domain] || {} +} + +// 处理站点统计数据刷新请求 +async function handleRefreshStats(domain?: string) { + if (domain) { + // 刷新特定站点的统计数据 + try { + const stats = await api.get(`site/statistic/${domain}`) + siteStatsList.value[domain] = stats + } catch (error) { + console.error(`Failed to refresh stats for ${domain}:`, error) + } + } else { + // 刷新所有站点统计数据 + await fetchSiteStats() + } +} + // 更新站点事件时 function onSiteSave() { siteAddDialog.value = false fetchData() } +// 选择筛选选项 +function selectFilter(value: string) { + filterOption.value = value + filterMenu.value = false +} + // 加载时获取数据 onBeforeMount(() => { fetchData() @@ -101,28 +215,77 @@ useDynamicButton({