diff --git a/README.md b/README.md index 369ac87..100825a 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ Pixiv 下载浏览管理器是一个基于 Web 的应用程序,提供以下功 [![视频演示](https://img.shields.io/badge/📹-演示视频-blue?style=for-the-badge)](https://sywb.top/Staticfiles/p%E6%95%99%E7%A8%8B.mp4) +观看更新内容(关注于新功能演示,上面的视频主要是登录教学): + +[![功能更新](https://img.shields.io/badge/📹-功能更新-blue?style=for-the-badge)](https://sywb.top/Staticfiles/p%E6%9B%B4%E6%96%B0.mp4) + ## 🚀 快速开始 ### 便携版下载(如果不想自义定或者是懒) diff --git a/backend/routes/artist.js b/backend/routes/artist.js index 3fc2d68..c7952ac 100644 --- a/backend/routes/artist.js +++ b/backend/routes/artist.js @@ -31,9 +31,6 @@ router.get('/search', async (req, res) => { name: user.user.name, account: user.user.account, profile_image_urls: user.user.profile_image_urls, - total_illusts: 0, - total_manga: 0, - total_followers: 0, is_followed: user.user.is_followed || false })); diff --git a/backend/services/artist.js b/backend/services/artist.js index a21bfaa..568c90b 100644 --- a/backend/services/artist.js +++ b/backend/services/artist.js @@ -8,25 +8,50 @@ class ArtistService { } /** - * 获取作者信息 + * 获取作者详细信息(用于作者详情页面) */ async getArtistInfo(artistId) { try { - const response = await this.makeRequest( - 'GET', - '/v1/user/detail', - { user_id: artistId } - ); + const response = await this.makeRequest('GET', '/v1/user/detail', { user_id: artistId }); return { + success: true, - data: response.user + data: response.user, }; - } catch (error) { return { success: false, - error: error.message + error: error.message, + }; + } + } + + /** + * 获取作者简单信息(用于列表页面) + */ + async getArtistBasicInfo(artistId) { + try { + const response = await this.makeRequest('GET', '/v1/user/detail', { user_id: artistId }); + + // 只返回基本信息,不包含统计信息 + const basicInfo = { + id: response.user.id, + name: response.user.name, + account: response.user.account, + profile_image_urls: response.user.profile_image_urls, + comment: response.user.comment, + is_followed: response.user.is_followed || false + }; + + return { + success: true, + data: basicInfo, + }; + } catch (error) { + return { + success: false, + error: error.message, }; } } @@ -36,24 +61,16 @@ class ArtistService { */ async getArtistArtworks(artistId, options = {}) { try { - const { - type = 'art', - filter = 'for_ios', - offset = 0, - limit = 30 - } = options; + const { type = 'art', filter = 'for_ios', offset = 0, limit = 30 } = options; const params = { user_id: artistId, type, filter, - offset + offset, }; - const response = await this.makeRequest( - 'GET', - `/v1/user/illusts?${stringify(params)}` - ); + const response = await this.makeRequest('GET', `/v1/user/illusts?${stringify(params)}`); console.log('Artworks response keys:', Object.keys(response)); console.log('Artworks count:', response.illusts?.length || 0); @@ -63,14 +80,13 @@ class ArtistService { success: true, data: { artworks: response.illusts, - next_url: response.next_url - } + next_url: response.next_url, + }, }; - } catch (error) { return { success: false, - error: error.message + error: error.message, }; } } @@ -80,36 +96,28 @@ class ArtistService { */ async getArtistFollowing(artistId, options = {}) { try { - const { - restrict = 'public', - offset = 0, - limit = 30 - } = options; + const { restrict = 'public', offset = 0, limit = 30 } = options; const params = { user_id: artistId, restrict, - offset + offset, }; - const response = await this.makeRequest( - 'GET', - `/v1/user/following?${stringify(params)}` - ); + const response = await this.makeRequest('GET', `/v1/user/following?${stringify(params)}`); return { success: true, data: { users: response.user_previews, next_url: response.next_url, - total: response.user_previews.length - } + total: response.user_previews.length, + }, }; - } catch (error) { return { success: false, - error: error.message + error: error.message, }; } } @@ -119,34 +127,27 @@ class ArtistService { */ async getArtistFollowers(artistId, options = {}) { try { - const { - offset = 0, - limit = 30 - } = options; + const { offset = 0, limit = 30 } = options; const params = { user_id: artistId, - offset + offset, }; - const response = await this.makeRequest( - 'GET', - `/v1/user/follower?${stringify(params)}` - ); + const response = await this.makeRequest('GET', `/v1/user/follower?${stringify(params)}`); return { success: true, data: { users: response.user_previews, next_url: response.next_url, - total: response.user_previews.length - } + total: response.user_previews.length, + }, }; - } catch (error) { return { success: false, - error: error.message + error: error.message, }; } } @@ -156,32 +157,29 @@ class ArtistService { */ async getFollowingArtists(options = {}) { try { - const { - offset = 0, - limit = 30 - } = options; + const { offset = 0, limit = 30 } = options; // 检查认证状态 if (!this.auth || !this.auth.accessToken) { return { success: false, - error: '未登录或认证已过期' + error: '未登录或认证已过期', }; } // 尝试从认证实例获取当前用户ID let currentUserId = this.auth.user?.id; - + // 如果认证实例中没有用户信息,尝试从状态中获取 if (!currentUserId) { const status = this.auth.getStatus(); currentUserId = status.user?.id; } - + if (!currentUserId) { return { success: false, - error: '无法获取当前用户信息,请重新登录' + error: '无法获取当前用户信息,请重新登录', }; } @@ -189,15 +187,10 @@ class ArtistService { user_id: currentUserId, restrict: 'public', offset, - limit + limit, }; - console.log('获取关注作者列表,参数:', params); - - const response = await this.makeRequest( - 'GET', - `/v1/user/following?${stringify(params)}` - ); + const response = await this.makeRequest('GET', `/v1/user/following?${stringify(params)}`); // 转换数据格式以匹配前端期望 const artists = (response.user_previews || []).map(user => ({ @@ -205,52 +198,21 @@ class ArtistService { name: user.user.name, account: user.user.account, profile_image_urls: user.user.profile_image_urls, - total_illusts: 0, // 这些信息需要通过 /v1/user/detail 获取 - total_manga: 0, - total_followers: 0, - is_followed: user.user.is_followed || false + is_followed: user.user.is_followed || false, })); - // 为前5个用户获取详细信息(避免API调用过多) - const artistsToFetch = artists.slice(0, 5); - const detailedArtists = await Promise.all( - artistsToFetch.map(async (artist) => { - try { - const detailResponse = await this.getArtistInfo(artist.id); - if (detailResponse.success) { - return { - ...artist, - total_illusts: detailResponse.data.total_illusts || 0, - total_manga: detailResponse.data.total_manga || 0, - total_followers: detailResponse.data.total_followers || 0 - }; - } - } catch (error) { - console.error(`获取用户 ${artist.id} 详细信息失败:`, error.message); - } - return artist; - }) - ); - - // 合并详细信息和基本信息 - const finalArtists = [ - ...detailedArtists, - ...artists.slice(5) // 其余用户保持基本信息 - ]; - return { success: true, data: { - artists: finalArtists, - total: finalArtists.length - } + artists: artists, + total: artists.length, + }, }; - } catch (error) { console.error('获取关注作者列表失败:', error.message); return { success: false, - error: error.message + error: error.message, }; } } @@ -262,26 +224,21 @@ class ArtistService { try { const data = { user_id: artistId, - restrict: 'public' + restrict: 'public', }; const endpoint = action === 'follow' ? '/v1/user/follow/add' : '/v1/user/follow/delete'; - - const response = await this.makeRequest( - 'POST', - endpoint, - data - ); + + const response = await this.makeRequest('POST', endpoint, data); return { success: true, - data: response + data: response, }; - } catch (error) { return { success: false, - error: error.message + error: error.message, }; } } @@ -291,26 +248,17 @@ class ArtistService { */ async searchArtists(searchOptions) { try { - const { - keyword, - sort = 'date_desc', - duration = 'all', - offset = 0, - limit = 30 - } = searchOptions; + const { keyword, sort = 'date_desc', duration = 'all', offset = 0, limit = 30 } = searchOptions; const params = { word: keyword, sort, duration, offset, - filter: 'for_ios' + filter: 'for_ios', }; - const response = await this.makeRequest( - 'GET', - `/v1/search/user?${stringify(params)}` - ); + const response = await this.makeRequest('GET', `/v1/search/user?${stringify(params)}`); return { success: true, @@ -318,14 +266,13 @@ class ArtistService { users: response.user_previews, next_url: response.next_url, search_span_limit: response.search_span_limit, - total: response.user_previews.length - } + total: response.user_previews.length, + }, }; - } catch (error) { return { success: false, - error: error.message + error: error.message, }; } } @@ -335,82 +282,26 @@ class ArtistService { */ async getRecommendedArtists(options = {}) { try { - const { - offset = 0, - limit = 30 - } = options; + const { offset = 0, limit = 30 } = options; const params = { offset, - filter: 'for_ios' + filter: 'for_ios', }; - const response = await this.makeRequest( - 'GET', - `/v1/user/recommended?${stringify(params)}` - ); + const response = await this.makeRequest('GET', `/v1/user/recommended?${stringify(params)}`); return { success: true, data: { users: response.user_previews, - next_url: response.next_url - } + next_url: response.next_url, + }, }; - } catch (error) { return { success: false, - error: error.message - }; - } - } - - /** - * 获取作者统计信息 - */ - async getArtistStats(artistId) { - try { - const response = await this.makeRequest( - 'GET', - '/v1/user/detail', - { user_id: artistId } - ); - - const user = response.user; - - const stats = { - user_id: user.id, - total_illusts: user.total_illusts, - total_manga: user.total_manga, - total_novels: user.total_novels, - total_bookmarked_illust: user.total_bookmarked_illust, - total_following: user.total_following, - total_followers: user.total_followers, - total_illust_bookmarks_public: user.total_illust_bookmarks_public, - total_illust_series: user.total_illust_series, - total_novel_series: user.total_novel_series, - background: user.background, - twitter_account: user.twitter_account, - twitter_url: user.twitter_url, - pawoo_url: user.pawoo_url, - is_followed: user.is_followed, - is_following: user.is_following, - is_friend: user.is_friend, - is_blocking: user.is_blocking, - is_blocked: user.is_blocked, - accept_request: user.accept_request - }; - - return { - success: true, - data: stats - }; - - } catch (error) { - return { - success: false, - error: error.message + error: error.message, }; } } @@ -420,20 +311,20 @@ class ArtistService { */ async makeRequest(method, endpoint, data = null) { const headers = { - 'Authorization': `Bearer ${this.auth.accessToken}`, + Authorization: `Bearer ${this.auth.accessToken}`, 'Accept-Language': 'en-us', 'App-OS': 'android', 'App-OS-Version': '9.0', 'App-Version': '5.0.234', - 'User-Agent': 'PixivAndroidApp/5.0.234 (Android 9.0; Pixel 3)' + 'User-Agent': 'PixivAndroidApp/5.0.234 (Android 9.0; Pixel 3)', }; - const config = { - method, - url: `${this.baseURL}${endpoint}`, - headers, - timeout: 60000 // 增加到60秒 - }; + const config = { + method, + url: `${this.baseURL}${endpoint}`, + headers, + timeout: 60000, // 增加到60秒 + }; if (data) { if (method === 'GET') { @@ -454,11 +345,11 @@ class ArtistService { status: error.response?.status, statusText: error.response?.statusText, data: error.response?.data, - message: error.message + message: error.message, }); throw error; } } } -module.exports = ArtistService; \ No newline at end of file +module.exports = ArtistService; diff --git a/backend/services/repository.js b/backend/services/repository.js index b5d37b4..246dc8f 100644 --- a/backend/services/repository.js +++ b/backend/services/repository.js @@ -222,18 +222,80 @@ class RepositoryService { async getDiskUsage() { try { const currentBaseDir = this.getCurrentBaseDir() - const stats = await fs.statfs(currentBaseDir) - const total = stats.blocks * stats.bsize - const free = stats.bavail * stats.bsize - const used = total - free - return { - total, - used, - free, - usagePercent: Math.round((used / total) * 100) + // 尝试使用 fs.statfs (Node.js 内置方法) + try { + const stats = await fs.statfs(currentBaseDir) + const total = stats.blocks * stats.bsize + const free = stats.bavail * stats.bsize + const used = total - free + + return { + total, + used, + free, + usagePercent: Math.round((used / total) * 100) + } + } catch (statfsError) { + console.log('fs.statfs 不可用,尝试使用系统命令:', statfsError.message) + + // 如果 fs.statfs 不可用,尝试使用系统命令 + if (process.platform === 'win32') { + // Windows 系统 + try { + const { stdout } = await execAsync('wmic logicaldisk get size,freespace,caption') + const lines = stdout.trim().split('\n').slice(1) // 跳过标题行 + + for (const line of lines) { + const parts = line.trim().split(/\s+/) + if (parts.length >= 3) { + const caption = parts[0] + const freeSpace = parseInt(parts[1]) + const totalSize = parseInt(parts[2]) + + // 检查当前目录是否在这个磁盘上 + if (currentBaseDir.toUpperCase().startsWith(caption.toUpperCase())) { + const used = totalSize - freeSpace + return { + total: totalSize, + used, + free: freeSpace, + usagePercent: Math.round((used / totalSize) * 100) + } + } + } + } + } catch (wmicError) { + console.log('wmic 命令失败:', wmicError.message) + } + } else { + // Unix/Linux 系统 + try { + const { stdout } = await execAsync(`df -B1 "${currentBaseDir}" | tail -1`) + const parts = stdout.trim().split(/\s+/) + if (parts.length >= 4) { + const total = parseInt(parts[1]) + const used = parseInt(parts[2]) + const free = parseInt(parts[3]) + + return { + total, + used, + free, + usagePercent: Math.round((used / total) * 100) + } + } + } catch (dfError) { + console.log('df 命令失败:', dfError.message) + } + } + + // 如果所有方法都失败,返回默认值 + console.log('无法获取磁盘使用情况,返回默认值') + return { total: 0, used: 0, free: 0, usagePercent: 0 } } } catch (error) { + console.error('获取磁盘使用情况失败:', error) return { total: 0, used: 0, free: 0, usagePercent: 0 } } } diff --git a/ui/dist.zip b/ui/dist.zip index 87930c0..121943e 100644 Binary files a/ui/dist.zip and b/ui/dist.zip differ diff --git a/ui/src/components/TheWelcome.vue b/ui/src/components/TheWelcome.vue deleted file mode 100644 index 6092dff..0000000 --- a/ui/src/components/TheWelcome.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - diff --git a/ui/src/components/WelcomeItem.vue b/ui/src/components/WelcomeItem.vue deleted file mode 100644 index 6d7086a..0000000 --- a/ui/src/components/WelcomeItem.vue +++ /dev/null @@ -1,87 +0,0 @@ - - - diff --git a/ui/src/components/artist/ArtistCard.vue b/ui/src/components/artist/ArtistCard.vue new file mode 100644 index 0000000..afe9af5 --- /dev/null +++ b/ui/src/components/artist/ArtistCard.vue @@ -0,0 +1,241 @@ + + + + + \ No newline at end of file diff --git a/ui/src/components/search/ArtistSearch.vue b/ui/src/components/search/ArtistSearch.vue new file mode 100644 index 0000000..05cdb94 --- /dev/null +++ b/ui/src/components/search/ArtistSearch.vue @@ -0,0 +1,344 @@ + + + + + \ No newline at end of file diff --git a/ui/src/views/AboutView.vue b/ui/src/views/AboutView.vue deleted file mode 100644 index 756ad2a..0000000 --- a/ui/src/views/AboutView.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/ui/src/views/ArtistView.vue b/ui/src/views/ArtistView.vue index ea911da..da01490 100644 --- a/ui/src/views/ArtistView.vue +++ b/ui/src/views/ArtistView.vue @@ -13,7 +13,7 @@
- + {{ downloadSuccess }}
@@ -23,19 +23,15 @@
- +

{{ artist.name }}

{{ artist.comment }}

- +
- -
-
-
{{ artist.total_illusts }}
-
插画
-
-
-
{{ artist.total_manga }}
-
漫画
-
-
-
{{ artist.total_novels }}
-
小说
-
-
-
{{ artist.total_followers }}
-
粉丝
-
-
-
{{ artist.total_following }}
-
关注
-
-
+
@@ -102,12 +76,7 @@
- +
@@ -116,37 +85,24 @@ @@ -207,15 +163,15 @@ const visiblePages = computed(() => { const maxVisible = 5; let start = Math.max(1, currentPage.value - Math.floor(maxVisible / 2)); let end = Math.min(totalPages.value, start + maxVisible - 1); - + if (end - start + 1 < maxVisible) { start = Math.max(1, end - maxVisible + 1); } - + for (let i = start; i <= end; i++) { pages.push(i); } - + return pages; }); @@ -228,17 +184,17 @@ const getCacheKey = (type: string, page: number) => { const getCache = (key: string) => { const cached = cache.value.get(key); const timeout = cacheTimeout.value.get(key); - + if (cached && timeout && Date.now() < timeout) { return cached; } - + // 清除过期缓存 if (cached) { cache.value.delete(key); cacheTimeout.value.delete(key); } - + return null; }; @@ -273,9 +229,9 @@ const fetchArtistInfo = async () => { try { loading.value = true; error.value = null; - + const response = await artistService.getArtistInfo(artistId); - + if (response.success && response.data) { artist.value = response.data; setCache(cacheKey, response.data); @@ -296,7 +252,7 @@ const fetchArtworks = async (page = 1) => { const cacheKey = getCacheKey(artworkType.value, page); const cached = getCache(cacheKey); - + if (cached) { artworks.value = cached.artworks; totalCount.value = cached.totalCount; @@ -307,20 +263,20 @@ const fetchArtworks = async (page = 1) => { try { artworksLoading.value = true; - + const offset = (page - 1) * pageSize.value; const response = await artistService.getArtistArtworks(artist.value.id, { type: artworkType.value, offset: offset, limit: pageSize.value }); - + if (response.success && response.data) { artworks.value = response.data.artworks; - + // 基于 next_url 来判断是否还有更多页面 const hasMore = !!response.data.next_url; - + if (page === 1) { // 第一页,基于是否有下一页来判断总数 if (hasMore) { @@ -344,9 +300,9 @@ const fetchArtworks = async (page = 1) => { totalPages.value = Math.max(totalPages.value, page); } } - + currentPage.value = page; - + // 缓存结果 setCache(cacheKey, { artworks: response.data.artworks, @@ -383,7 +339,7 @@ const handleFollow = async () => { try { const action = artist.value.is_followed ? 'unfollow' : 'follow'; const response = await artistService.followArtist(artist.value.id, action); - + if (response.success) { artist.value.is_followed = !artist.value.is_followed; // 更新缓存 @@ -408,12 +364,12 @@ const handleDownloadAll = async () => { type: artworkType.value, limit: parseInt(downloadLimit.value) }); - + if (response.success) { console.log('下载任务已创建:', response.data); const limitText = downloadLimit.value === '9999' ? '全部' : downloadLimit.value; downloadSuccess.value = `下载任务已创建,将下载 ${limitText} 个作品`; - + // 3秒后清除成功提示 setTimeout(() => { downloadSuccess.value = null; @@ -432,13 +388,13 @@ const handleDownloadAll = async () => { // 处理图片URL,通过后端代理 const getImageUrl = (originalUrl: string) => { if (!originalUrl) return ''; - + // 如果是Pixiv的图片URL,通过后端代理 if (originalUrl.includes('i.pximg.net')) { const encodedUrl = encodeURIComponent(originalUrl); return `http://localhost:3000/api/proxy/image?url=${encodedUrl}`; } - + return originalUrl; }; @@ -466,7 +422,7 @@ watch(() => route.params.id, () => { // 清除缓存并重新加载 clearCache(); fetchArtistInfo(); - + // 检查是否有返回的页面信息 const returnPage = parseInt(route.query.page as string); if (returnPage && returnPage > 0) { @@ -484,7 +440,7 @@ onUnmounted(() => { onMounted(async () => { await fetchArtistInfo(); - + // 检查是否有返回的页面信息 const returnPage = parseInt(route.query.page as string); if (returnPage && returnPage > 0) { @@ -547,6 +503,7 @@ onMounted(async () => { transform: translateX(100%); opacity: 0; } + to { transform: translateX(0); opacity: 1; @@ -675,33 +632,7 @@ onMounted(async () => { background: #e5e7eb; } -.artist-stats { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: 1rem; - margin-bottom: 2rem; -} -.stat-card { - background: white; - border-radius: 0.75rem; - padding: 1.5rem; - text-align: center; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); -} - -.stat-number { - font-size: 2rem; - font-weight: 700; - color: #3b82f6; - margin-bottom: 0.5rem; -} - -.stat-label { - color: #6b7280; - font-size: 0.875rem; - font-weight: 500; -} .artworks-section { background: white; @@ -832,58 +763,58 @@ onMounted(async () => { .container { padding: 0 1rem; } - + .artist-header { flex-direction: column; align-items: stretch; } - + .artist-profile { flex-direction: column; text-align: center; } - + .artist-actions { flex-direction: row; } - + .download-section { flex-direction: row; align-items: center; gap: 1rem; } - + .download-input-group { flex-shrink: 0; } - + .btn { flex: 1; } - + .section-header { flex-direction: column; gap: 1rem; align-items: stretch; } - + .artworks-grid { grid-template-columns: 1fr; } - + .pagination { flex-direction: column; gap: 1rem; } - + .page-numbers { order: -1; } - + .page-info { flex-direction: column; gap: 0.5rem; text-align: center; } } - \ No newline at end of file + \ No newline at end of file diff --git a/ui/src/views/ArtistsView.vue b/ui/src/views/ArtistsView.vue index 49d8402..3dcfabf 100644 --- a/ui/src/views/ArtistsView.vue +++ b/ui/src/views/ArtistsView.vue @@ -4,23 +4,10 @@ +
+
- -
-

搜索结果

-
-
-
- -
-

{{ artist.name }}

- -
-
- -
-
- -
-
- {{ artist.total_illusts }} - 插画 -
-
- {{ artist.total_manga }} - 漫画 -
-
- {{ artist.total_followers }} - 粉丝 -
-
- -
- - 查看作品 - - -
+ + + + +
+
+ + + + {{ downloadSuccess }}
\ No newline at end of file + \ No newline at end of file diff --git a/ui/src/views/SearchView.vue b/ui/src/views/SearchView.vue index 9657aff..5714910 100644 --- a/ui/src/views/SearchView.vue +++ b/ui/src/views/SearchView.vue @@ -36,32 +36,22 @@
- +
- +
- + {{ tag }} @@ -121,41 +111,49 @@
-
- + +
+
-
-
-

搜索结果 ({{ totalResults }})

-
- + +
+
+ +
+ +
+
+

搜索结果 ({{ totalResults }})

+
+ +
+
+ +
+
-
- +
+
+ + + +

未找到相关作品

+

尝试使用不同的关键词或调整搜索条件

+
-
-
-
- - - -

未找到相关作品

-

尝试使用不同的关键词或调整搜索条件

-
-
- -
-
-

开始搜索

-

输入关键词来搜索你喜欢的作品

+
+
+

开始搜索

+

输入关键词来搜索你喜欢的作品

+
@@ -172,6 +170,7 @@ import type { Artwork, SearchParams } from '@/types'; import LoadingSpinner from '@/components/common/LoadingSpinner.vue'; import ErrorMessage from '@/components/common/ErrorMessage.vue'; import ArtworkCard from '@/components/artwork/ArtworkCard.vue'; +import ArtistSearch from '@/components/search/ArtistSearch.vue'; const router = useRouter(); const route = useRoute(); @@ -198,6 +197,7 @@ const loadingMore = ref(false); const error = ref(null); const hasSearched = ref(false); const offset = ref(0); +const artistSearchRef = ref(); const handleSearch = async () => { if (!searchKeyword.value.trim()) { @@ -243,7 +243,7 @@ const loadMore = async () => { // 检查是否有搜索条件 const hasKeyword = searchKeyword.value.trim(); const hasTags = searchTags.value.length > 0; - + if (!hasKeyword && !hasTags) { return; } @@ -317,6 +317,8 @@ const handleArtistSearch = () => { return; } + // 切换到作者搜索模式并跳转 + searchMode.value = 'artist'; router.push(`/artist/${id}`); }; @@ -373,16 +375,23 @@ const clearError = () => { error.value = null; }; +// 处理作者下载 +const handleArtistDownload = (artist: any) => { + // 这里可以添加下载作者的逻辑 + // 暂时跳转到作者页面 + router.push(`/artist/${artist.id}`); +}; + // 监听路由变化,处理URL参数 watch(() => route.query, () => { const urlMode = route.query.mode as string; const urlTag = route.query.tag as string; const urlTags = route.query.tags; - + if (urlMode === 'tags') { // 自动设置标签搜索模式 searchMode.value = 'tags'; - + if (urlTags) { // 处理多个标签 if (Array.isArray(urlTags)) { @@ -398,7 +407,7 @@ watch(() => route.query, () => { // 清除sessionStorage中的多标签选择 sessionStorage.removeItem('currentSearchTags'); } - + // 如果有标签,自动执行搜索 if (searchTags.value.length > 0) { handleTagSearch(); @@ -588,6 +597,13 @@ watch(() => route.query, () => { padding: 2rem 0; } +.artist-search-section { + background: white; + border-radius: 0.5rem; + padding: 2rem; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + .error-section, .loading-section { margin-bottom: 2rem; diff --git a/ui/src/views/repository/RepositoryConfig.vue b/ui/src/views/repository/RepositoryConfig.vue index f54d7b2..fe380cd 100644 --- a/ui/src/views/repository/RepositoryConfig.vue +++ b/ui/src/views/repository/RepositoryConfig.vue @@ -143,6 +143,7 @@ interface Emits { (e: 'reset-config'): void (e: 'select-download-dir'): void (e: 'test-download-dir'): void + (e: 'config-saved'): void } const props = defineProps() @@ -165,6 +166,8 @@ const saveConfig = async () => { saving.value = true try { emit('save-config') + // 保存成功后触发刷新事件 + emit('config-saved') } finally { saving.value = false } @@ -173,6 +176,8 @@ const saveConfig = async () => { // 重置配置 const resetConfig = () => { emit('reset-config') + // 重置后也触发刷新事件 + emit('config-saved') }