diff --git a/src/components/NoDataFound.vue b/src/components/NoDataFound.vue index 47e5cc82..1b4dfbcc 100644 --- a/src/components/NoDataFound.vue +++ b/src/components/NoDataFound.vue @@ -7,25 +7,62 @@ interface Props { errorCode?: string errorTitle?: string errorDescription?: string + size?: number } + + + diff --git a/src/pages/resource.vue b/src/pages/resource.vue index 1d2c99db..b4221bea 100644 --- a/src/pages/resource.vue +++ b/src/pages/resource.vue @@ -64,20 +64,49 @@ const errorDescription = ref('未搜索到任何资源') // 使用SSE监听加载进度 function startLoadingProgress() { progressText.value = '正在搜索,请稍候...' + progressValue.value = 10 // 初始进度设为10%,确保进度条显示 progressEventSource.value = new EventSource(`${import.meta.env.VITE_API_BASE_URL}system/progress/search`) progressEventSource.value.onmessage = event => { const progress = JSON.parse(event.data) if (progress) { progressText.value = progress.text progressValue.value = progress.value + + // 搜索完成条件调整:只有明确完成时才关闭 + if (progress.text.includes('完成') && progress.value >= 99) { + setTimeout(() => { + stopLoadingProgress() + }, 1000) // 延迟1秒关闭,确保用户能看到100% + } } } + + // 添加错误处理 + progressEventSource.value.onerror = () => { + setTimeout(() => { + stopLoadingProgress() + }, 1000) + } + + // 添加安全超时,确保不会永远卡住 + setTimeout(() => { + if (progressEventSource.value && progressValue.value < 100) { + stopLoadingProgress() + } + }, 60000) // 60秒超时 } // 停止监听加载进度 function stopLoadingProgress() { - if (progressEventSource.value) progressEventSource.value?.close() - progressValue.value = 0 + if (progressEventSource.value) { + progressEventSource.value.close() + progressEventSource.value = undefined + } + // 确保进度显示100%,然后再渐进清零 + progressValue.value = 100 + setTimeout(() => { + progressValue.value = 0 + }, 1500) // 延长到1.5秒,让用户有足够时间看到完成状态 } // 设置视图类型 @@ -125,7 +154,7 @@ async function fetchData() { }) } if (result && result.success) { - dataList.value = result.data + dataList.value = result.data || [] } else if (result && result.message) { errorDescription.value = result.message } @@ -137,6 +166,8 @@ async function fetchData() { isRefreshed.value = true } catch (error) { console.error(error) + stopLoadingProgress() + isRefreshed.value = true return Promise.reject(error) } } @@ -156,7 +187,7 @@ onUnmounted(() => {
-
+
@@ -231,9 +262,8 @@ onUnmounted(() => {
返回首页
diff --git a/src/views/system/SearchBarView.vue b/src/views/system/SearchBarView.vue index 69425f12..6589e992 100644 --- a/src/views/system/SearchBarView.vue +++ b/src/views/system/SearchBarView.vue @@ -4,6 +4,16 @@ import type { Plugin, Site, Subscribe } from '@/api/types' import { SystemNavMenus, SettingTabs } from '@/router/menu' import { NavMenu } from '@/@layouts/types' import { useUserStore } from '@/stores' +import { useTheme } from 'vuetify' +import { computed, onMounted, ref } from 'vue' + +// 定义站点信息接口 +interface SiteInfo { + id: number + name: string + is_active: boolean + [key: string]: any +} // 路由 const router = useRouter() @@ -17,8 +27,19 @@ const superUser = userStore.superUser // 当前用户名 const userName = userStore.userName +// 定义props,接收modelValue +const props = defineProps<{ + modelValue: boolean +}>() + // 定义事件 -const emit = defineEmits(['close']) +const emit = defineEmits(['close', 'update:modelValue']) + +// 对话框状态的本地计算属性 +const dialog = computed({ + get: () => props.modelValue, + set: val => emit('update:modelValue', val), +}) // 搜索词 const searchWord = ref(null) @@ -30,14 +51,16 @@ const searchWordInput = ref(null) const recentSearches = ref([]) // 全选/全不选按钮文字 -const checkAllText = computed(() => (selectedSites.value.length === allSites.value.length ? '全不选' : '全选')) +const checkAllText = computed(() => { + return selectedSites.value.length < allSites.value.length ? '选择全部' : '取消全选' +}) // 全选/全不选 -function checkAllSitesorNot() { - if (selectedSites.value.length === allSites.value.length) { - selectedSites.value = [] +const checkAllSitesorNot = () => { + if (selectedSites.value.length < allSites.value.length) { + selectedSites.value = allSites.value.map((item: SiteInfo) => item.id) } else { - selectedSites.value = allSites.value.map(item => item.id) + selectedSites.value = [] } } @@ -138,11 +161,11 @@ const matchedPluginItems = computed(() => { // 所有订阅数据 const SubscribeItems = ref([]) -// 所有站点 -const allSites = ref([]) - -// 选中的站点 +// 站点选择对话框 +const showSiteDialog = ref(false) +const siteFilter = ref('') const selectedSites = ref([]) +const allSites = ref([]) // 获取订阅列表数据 async function fetchSubscribes() { @@ -153,29 +176,79 @@ async function fetchSubscribes() { } } -// 查询所有站点 -async function querySites() { - try { - const data: Site[] = await api.get('site/') +// 根据筛选条件过滤站点 +const filteredSites = computed(() => { + if (!siteFilter.value) return allSites.value + const filter = siteFilter.value.toLowerCase() + return allSites.value.filter((site: SiteInfo) => + site.name.toLowerCase().includes(filter) + ) +}) - // 过滤站点,只有启用的站点才显示 - allSites.value = data.filter(item => item.is_active) - } catch (error) { - console.log(error) +// 保存用户站点选择到本地 +const saveUserSitePreferences = () => { + try { + localStorage.setItem('MP_SelectedSites', JSON.stringify(selectedSites.value)) + } catch (err) { + console.error('保存站点选择失败:', err) } } -// 查询用户选中的站点 -async function querySelectedSites() { +// 从本地或接口加载用户站点偏好设置 +const loadUserSitePreferences = async () => { try { - const result: { [key: string]: any } = await api.get('system/setting/IndexerSites') - - selectedSites.value = result.data?.value ?? [] - } catch (error) { - console.log(error) + // 先尝试从本地存储获取 + const storedSites = localStorage.getItem('MP_SelectedSites') + if (storedSites) { + selectedSites.value = JSON.parse(storedSites) + console.log('从本地加载站点选择:', selectedSites.value) + return + } + + // 如果本地没有,尝试从接口获取系统预设 + const result = await api.get('system/setting/IndexerSites') + if (result && result.data && result.data.value) { + selectedSites.value = result.data.value + console.log('从系统预设加载站点选择:', selectedSites.value) + return + } + } catch (err) { + console.error('加载站点选择失败:', err) } } +// 获取站点分类信息 +const getSiteCategories = () => { + api.get('site/').then(async (res: any) => { + if (res && Array.isArray(res)) { + allSites.value = res.filter((site: any) => site.is_active) || [] + // 加载用户站点选择 + await loadUserSitePreferences() + // 如果没有选择任何站点并且有可用站点,才默认选择全部 + if (selectedSites.value.length === 0 && allSites.value.length > 0) { + selectedSites.value = allSites.value.map((site: SiteInfo) => site.id) + } + } else if (res.data && Array.isArray(res.data)) { + allSites.value = res.data.filter((site: any) => site.is_active) || [] + // 加载用户站点选择 + await loadUserSitePreferences() + // 如果没有选择任何站点并且有可用站点,才默认选择全部 + if (selectedSites.value.length === 0 && allSites.value.length > 0) { + selectedSites.value = allSites.value.map((site: SiteInfo) => site.id) + } + } + console.log('站点数据:', allSites.value) + console.log('已选站点:', selectedSites.value) + }).catch(err => { + console.error('获取站点数据失败:', err) + }) +} + +// 打开站点选择对话框 +const openSiteDialog = () => { + showSiteDialog.value = true +} + // 匹配的订阅列表 const matchedSubscribeItems = computed(() => { if (!searchWord.value) return [] @@ -185,6 +258,27 @@ const matchedSubscribeItems = computed(() => { }) }) +// 搜索站点资源 +const searchTorrent = () => { + if (!searchWord.value) return + // 记录搜索词 + saveRecentSearches(searchWord.value) + // 保存用户站点选择 + saveUserSitePreferences() + // 跳转到搜索页面 + router.push({ + path: '/resource', + query: { + keyword: searchWord.value, + area: 'title', + sites: selectedSites.value.join(','), + }, + }) + // 关闭搜索对话框 + dialog.value = false + emit('close') +} + // 跳转媒体搜索页面 function searchMedia(searchType: string) { // 搜索类型 media/person @@ -200,21 +294,6 @@ function searchMedia(searchType: string) { emit('close') } -// 跳转到种子搜索页面 -function searchTorrent() { - if (!searchWord.value) return - saveRecentSearches(searchWord.value) - router.push({ - path: '/resource', - query: { - keyword: searchWord.value, - area: 'title', - sites: selectedSites.value.join(','), - }, - }) - emit('close') -} - // 跳转到历史记录页面 function searchHistory() { if (!searchWord.value) return @@ -273,217 +352,788 @@ onMounted(() => { fetchInstalledPlugins() fetchSubscribes() loadRecentSearches() - querySites() - querySelectedSites() + getSiteCategories() }) + +