diff --git a/backend/routes/repository.js b/backend/routes/repository.js index c66b60f..425aa2d 100644 --- a/backend/routes/repository.js +++ b/backend/routes/repository.js @@ -72,7 +72,7 @@ router.get('/artists', async (req, res) => { router.get('/artists/:artistName/artworks', async (req, res) => { try { const { artistName } = req.params - const { offset = 0, limit = 20 } = req.query + const { offset = 0, limit = 50 } = req.query const artworks = await repositoryService.getArtworksByArtist( artistName, parseInt(offset), @@ -84,10 +84,21 @@ router.get('/artists/:artistName/artworks', async (req, res) => { } }) +// 获取所有作品列表 +router.get('/artworks', async (req, res) => { + try { + const { offset = 0, limit = 50 } = req.query + const results = await repositoryService.searchArtworks('', parseInt(offset), parseInt(limit)) + res.json(ResponseUtil.success(results)) + } catch (error) { + res.status(500).json(ResponseUtil.error(error.message)) + } +}) + // 搜索作品 router.get('/search', async (req, res) => { try { - const { q, offset = 0, limit = 20 } = req.query + const { q, offset = 0, limit = 50 } = req.query if (!q) { return res.status(400).json(ResponseUtil.error('搜索关键词不能为空')) } diff --git a/backend/services/repository.js b/backend/services/repository.js index 24a3989..b5d37b4 100644 --- a/backend/services/repository.js +++ b/backend/services/repository.js @@ -238,12 +238,12 @@ class RepositoryService { } } - // 按作者浏览作品 - async getArtworksByArtist(artistName, offset = 0, limit = 20) { + // 获取作者作品 + async getArtworksByArtist(artistName, offset = 0, limit = 50) { try { const stats = await this.scanRepository() const artistArtworks = stats.artworks.filter(artwork => - artwork.artist === artistName + artwork.artist.toLowerCase() === artistName.toLowerCase() ) return { @@ -327,7 +327,7 @@ class RepositoryService { } // 搜索作品 - async searchArtworks(query, offset = 0, limit = 20) { + async searchArtworks(query, offset = 0, limit = 50) { try { const stats = await this.scanRepository() const filtered = stats.artworks.filter(artwork => diff --git a/ui/dist.zip b/ui/dist.zip index 1ec9f7d..99b9f17 100644 Binary files a/ui/dist.zip and b/ui/dist.zip differ diff --git a/ui/src/stores/repository.ts b/ui/src/stores/repository.ts index ea6d18b..2845ca8 100644 --- a/ui/src/stores/repository.ts +++ b/ui/src/stores/repository.ts @@ -138,12 +138,17 @@ export const useRepositoryStore = defineStore('repository', () => { } // 获取作者作品 - const getArtworksByArtist = async (artistName: string, offset = 0, limit = 20) => { + const getArtworksByArtist = async (artistName: string, offset = 0, limit = 50) => { return await apiCall(`/artists/${encodeURIComponent(artistName)}/artworks?offset=${offset}&limit=${limit}`) } + // 获取所有作品 + const getAllArtworks = async (offset = 0, limit = 50) => { + return await apiCall(`/artworks?offset=${offset}&limit=${limit}`) + } + // 搜索作品 - const searchArtworks = async (query: string, offset = 0, limit = 20) => { + const searchArtworks = async (query: string, offset = 0, limit = 50) => { return await apiCall(`/search?q=${encodeURIComponent(query)}&offset=${offset}&limit=${limit}`) } @@ -206,6 +211,7 @@ export const useRepositoryStore = defineStore('repository', () => { getStats, getArtists, getArtworksByArtist, + getAllArtworks, searchArtworks, getArtwork, deleteArtwork, diff --git a/ui/src/views/RepositoryView.vue b/ui/src/views/RepositoryView.vue index c1f622b..8ae70f8 100644 --- a/ui/src/views/RepositoryView.vue +++ b/ui/src/views/RepositoryView.vue @@ -50,6 +50,8 @@ :artworks="artworks" :current-page="currentPage" :page-size="pageSize" + :total-items="totalItems" + :view-mode="viewMode" @update:search-query="handleSearchQuery" @update:view-mode="handleViewMode" @select-artist="selectArtist" @@ -93,10 +95,13 @@ const config = ref({ // 浏览相关 const searchQuery = ref('') +const viewMode = ref('artworks') // 默认显示作品模式 const artists = ref([]) const artworks = ref([]) const currentPage = ref(1) -const pageSize = 20 +const pageSize = 50 // 增加每页显示数量 +const totalItems = ref(0) // 添加总数状态 +const currentArtist = ref('') // 添加当前查看的作者状态 // 迁移相关 const migrateSourceDir = ref('') @@ -113,7 +118,7 @@ onMounted(async () => { await loadStats() await loadConfig() await loadArtists() - await loadAllArtworks() // 添加加载所有作品 + await loadAllArtworks(1) // 加载第一页 }) // 加载统计信息 @@ -228,11 +233,14 @@ const loadArtists = async () => { } // 加载所有作品 -const loadAllArtworks = async () => { +const loadAllArtworks = async (page = 1, limit = pageSize) => { try { - // 使用搜索空字符串来获取所有作品 - const result = await repositoryStore.searchArtworks('', 0, 1000) + currentArtist.value = '' // 加载所有作品时清除当前作者 + const offset = (page - 1) * limit + const result = await repositoryStore.getAllArtworks(offset, limit) artworks.value = result.artworks + totalItems.value = result.total // 更新总数 + currentPage.value = page } catch (error: any) { console.error('加载所有作品失败:', error) } @@ -241,10 +249,19 @@ const loadAllArtworks = async () => { // 选择作者 const selectArtist = async (artistName: string) => { try { - const result = await repositoryStore.getArtworksByArtist(artistName) + // 重置搜索和分页 + searchQuery.value = '' + currentPage.value = 1 + currentArtist.value = artistName // 设置当前查看的作者 + + // 获取作者作品 + const result = await repositoryStore.getArtworksByArtist(artistName, 0, pageSize) artworks.value = result.artworks + totalItems.value = result.total || result.artworks.length + + // 切换到作品视图 + viewMode.value = 'artworks' activeTab.value = 'browse' - currentPage.value = 1 // 重置到第一页 } catch (error: any) { console.error('加载作者作品失败:', error) } @@ -253,32 +270,45 @@ const selectArtist = async (artistName: string) => { // 处理搜索查询 const handleSearchQuery = async (query: string) => { searchQuery.value = query + currentArtist.value = '' // 搜索时清除当前作者 + + // 重置分页 + currentPage.value = 1 + if (query.trim()) { try { - const result = await repositoryStore.searchArtworks(query) + // 搜索作品 + const result = await repositoryStore.searchArtworks(query, 0, pageSize) artworks.value = result.artworks + totalItems.value = result.total + + // 切换到作品视图 + viewMode.value = 'artworks' activeTab.value = 'browse' - currentPage.value = 1 // 重置到第一页 } catch (error: any) { console.error('搜索失败:', error) } } else { - await loadAllArtworks() // 清空搜索时加载所有作品 + // 清空搜索,加载所有作品 + await loadAllArtworks(1) } } // 处理视图模式 const handleViewMode = (mode: string) => { + viewMode.value = mode + // 根据视图模式加载相应数据 if (mode === 'artists') { // 作者模式,确保作者数据已加载 + currentArtist.value = '' // 切换到作者模式时清除当前作者 if (artists.value.length === 0) { loadArtists() } } else if (mode === 'artworks' || mode === 'gallery') { // 作品模式,确保作品数据已加载 if (artworks.value.length === 0) { - loadAllArtworks() + loadAllArtworks(1) } } } @@ -305,16 +335,46 @@ const deleteArtwork = async (artworkId: string) => { closeArtworkModal() await loadStats() await loadArtists() - await loadAllArtworks() // 重新加载作品列表 + await loadAllArtworks(currentPage.value) // 重新加载当前页 } catch (error: any) { console.error('删除作品失败:', error) alert('删除作品失败: ' + error.message) } } -// 分页 -const changePage = (page: number) => { +// 分页处理 +const changePage = async (page: number, options?: { artist?: string }) => { currentPage.value = page + + // 如果传入了作者信息,使用作者特定的分页 + if (options?.artist) { + currentArtist.value = options.artist + } + + if (searchQuery.value.trim()) { + // 如果有搜索查询,重新搜索 + try { + const offset = (page - 1) * pageSize + const result = await repositoryStore.searchArtworks(searchQuery.value, offset, pageSize) + artworks.value = result.artworks + totalItems.value = result.total + } catch (error: any) { + console.error('搜索失败:', error) + } + } else if (currentArtist.value) { + // 如果当前在查看特定作者的作品 + try { + const offset = (page - 1) * pageSize + const result = await repositoryStore.getArtworksByArtist(currentArtist.value, offset, pageSize) + artworks.value = result.artworks + totalItems.value = result.total || result.artworks.length + } catch (error: any) { + console.error('加载作者作品失败:', error) + } + } else { + // 否则加载指定页面的所有作品 + await loadAllArtworks(page) + } } // 选择下载目录 diff --git a/ui/src/views/repository/RepositoryBrowse.vue b/ui/src/views/repository/RepositoryBrowse.vue index 0df3db9..1b73255 100644 --- a/ui/src/views/repository/RepositoryBrowse.vue +++ b/ui/src/views/repository/RepositoryBrowse.vue @@ -1,64 +1,5 @@ @@ -635,93 +226,6 @@ onUnmounted(() => { position: relative; } -.browse-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - margin-bottom: 2rem; - gap: 2rem; -} - -.search-filters { - flex: 1; - display: flex; - gap: 1rem; - align-items: center; -} - -.search-box { - position: relative; - flex: 1; - max-width: 400px; -} - -.search-input { - width: 100%; - padding: 0.75rem 2.5rem 0.75rem 1rem; - border: 1px solid #d1d5db; - border-radius: 0.375rem; - font-size: 0.875rem; -} - -.clear-btn { - position: absolute; - right: 0.5rem; - top: 50%; - transform: translateY(-50%); - background: none; - border: none; - color: #6b7280; - cursor: pointer; - padding: 0.25rem; - border-radius: 0.25rem; -} - -.clear-btn:hover { - background: #f3f4f6; -} - -.filter-controls { - display: flex; - gap: 0.5rem; -} - -.filter-select { - padding: 0.5rem; - border: 1px solid #d1d5db; - border-radius: 0.375rem; - background: white; - font-size: 0.875rem; -} - -.view-toggle { - display: flex; - gap: 0.5rem; -} - -.view-btn { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.75rem 1rem; - border: 1px solid #d1d5db; - background: white; - border-radius: 0.375rem; - cursor: pointer; - transition: all 0.2s; - font-size: 0.875rem; -} - -.view-btn.active { - background: #3b82f6; - color: white; - border-color: #3b82f6; -} - -.btn-icon { - font-size: 1rem; -} - .breadcrumb { display: flex; align-items: center; @@ -752,694 +256,4 @@ onUnmounted(() => { color: #6b7280; font-size: 0.875rem; } - -.artists-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - gap: 1rem; - margin-bottom: 2rem; -} - -.artist-card { - background: white; - border: 1px solid #e5e7eb; - border-radius: 0.5rem; - padding: 1.5rem; - cursor: pointer; - transition: all 0.2s; - display: flex; - align-items: center; - gap: 1rem; -} - -.artist-card:hover { - border-color: #3b82f6; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); -} - -.artist-avatar { - width: 60px; - height: 60px; - background: #3b82f6; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 1.5rem; - font-weight: bold; -} - -.artist-info { - flex: 1; -} - -.artist-info h4 { - margin: 0 0 0.5rem 0; - color: #1f2937; -} - -.artist-info p { - margin: 0.25rem 0; - color: #6b7280; - font-size: 0.875rem; -} - -.artist-actions { - display: flex; - gap: 0.5rem; -} - -.artworks-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); - gap: 1rem; - margin-bottom: 2rem; -} - -.artwork-card { - background: white; - border: 1px solid #e5e7eb; - border-radius: 0.5rem; - overflow: hidden; - cursor: pointer; - transition: all 0.2s; -} - -.artwork-card:hover { - border-color: #3b82f6; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); -} - -.artwork-preview { - position: relative; - height: 200px; - overflow: hidden; -} - -.preview-image { - width: 100%; - height: 100%; - object-fit: cover; - transition: transform 0.2s; -} - -.artwork-card:hover .preview-image { - transform: scale(1.05); -} - -.artwork-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); - display: flex; - align-items: center; - justify-content: center; - opacity: 0; - transition: opacity 0.2s; -} - -.artwork-card:hover .artwork-overlay { - opacity: 1; -} - -.view-btn-overlay { - background: white; - color: #1f2937; - border: none; - padding: 0.5rem 1rem; - border-radius: 0.375rem; - cursor: pointer; - font-size: 0.875rem; -} - -.artwork-info { - padding: 1rem; -} - -.artwork-info h4 { - margin: 0 0 0.5rem 0; - font-size: 0.875rem; - color: #1f2937; -} - -.artist-name { - color: #3b82f6 !important; - cursor: pointer; - font-weight: 500; -} - -.artist-name:hover { - text-decoration: underline; -} - -.artwork-info p { - margin: 0.25rem 0; - font-size: 0.75rem; - color: #6b7280; -} - -.file-count { - font-weight: 500; - color: #6b5563 !important; -} - -.artwork-actions { - padding: 0 1rem 1rem; - display: flex; - gap: 0.5rem; -} - -.action-btn { - flex: 1; - padding: 0.5rem; - border: 1px solid #d1d5db; - background: white; - border-radius: 0.375rem; - cursor: pointer; - font-size: 0.75rem; - transition: all 0.2s; -} - -.action-btn:hover { - background: #f3f4f6; - border-color: #3b82f6; -} - -.gallery-container { - position: relative; - margin-top: 2rem; - padding: 1rem; - background: #f9fafb; - border-radius: 0.5rem; - border: 1px solid #e5e7eb; -} - -.gallery-controls { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; - padding: 0.5rem 1rem; - background: white; - border-radius: 0.375rem; - border: 1px solid #e5e7eb; -} - -.zoom-controls { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.zoom-btn { - background: #3b82f6; - color: white; - border: none; - padding: 0.5rem 0.75rem; - border-radius: 0.375rem; - cursor: pointer; - font-size: 0.875rem; - transition: all 0.2s; -} - -.zoom-btn:hover:not(:disabled) { - background: #2563eb; -} - -.zoom-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.zoom-level { - font-size: 0.875rem; - color: #6b7280; - font-weight: 500; -} - -.view-controls { - display: flex; - gap: 0.5rem; -} - -.size-btn { - background: #3b82f6; - color: white; - border: none; - padding: 0.5rem 0.75rem; - border-radius: 0.375rem; - cursor: pointer; - font-size: 0.875rem; - transition: all 0.2s; -} - -.size-btn:hover:not(:disabled) { - background: #2563eb; -} - -.size-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.gallery-grid { - display: grid; - gap: 1rem; - margin-bottom: 2rem; -} - -.grid-small .gallery-grid { - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); -} - -.grid-medium .gallery-grid { - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); -} - -.grid-large .gallery-grid { - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); -} - -.gallery-item { - position: relative; - height: 250px; - border-radius: 0.5rem; - overflow: hidden; - cursor: pointer; - background: white; - border: 1px solid #e5e7eb; - transition: all 0.2s; -} - -.gallery-item:hover { - border-color: #3b82f6; - box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); -} - -.gallery-image-container { - position: relative; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - background: #f9fafb; - overflow: hidden; -} - -.gallery-image { - width: 100%; - height: 100%; - object-fit: contain; - transition: transform 0.3s ease; - transform: scale(v-bind(zoomLevel)); -} - -.gallery-item:hover .gallery-image { - transform: scale(v-bind(zoomLevel) * 1.05); -} - -.gallery-item:hover .gallery-image { - transform: scale(1.1); -} - -.gallery-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.7); - display: flex; - align-items: center; - justify-content: center; - opacity: 0; - transition: opacity 0.2s; -} - -.gallery-item:hover .gallery-overlay { - opacity: 1; -} - -.overlay-content { - text-align: center; - color: white; - padding: 1rem; -} - -.overlay-content h4 { - margin: 0 0 0.25rem 0; - font-size: 0.875rem; -} - -.overlay-content p { - margin: 0; - font-size: 0.75rem; - opacity: 0.8; -} - -.overlay-actions { - display: flex; - gap: 0.5rem; - margin-top: 0.5rem; -} - -.overlay-btn { - background: white; - color: #1f2937; - border: none; - padding: 0.5rem 1rem; - border-radius: 0.375rem; - cursor: pointer; - font-size: 0.75rem; - transition: all 0.2s; -} - -.overlay-btn:hover { - background: #f3f4f6; - color: #3b82f6; -} - -.pagination { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; - margin-top: 2rem; -} - -.page-btn { - padding: 0.5rem 0.75rem; - border: 1px solid #d1d5db; - background: white; - border-radius: 0.375rem; - cursor: pointer; - font-size: 0.875rem; - transition: all 0.2s; -} - -.page-btn:hover:not(:disabled) { - background: #f3f4f6; - border-color: #3b82f6; -} - -.page-btn.active { - background: #3b82f6; - color: white; - border-color: #3b82f6; -} - -.page-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.page-numbers { - display: flex; - gap: 0.25rem; -} - -.image-viewer-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.9); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -} - -.image-viewer-content { - background: white; - border-radius: 0.5rem; - max-width: 90vw; - max-height: 90vh; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.viewer-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1rem; - border-bottom: 1px solid #e5e7eb; -} - -.viewer-header h3 { - margin: 0; - color: #1f2937; -} - -.viewer-controls { - display: flex; - align-items: center; - gap: 1rem; -} - -.viewer-zoom-controls { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.close-btn { - background: none; - border: none; - font-size: 1.5rem; - cursor: pointer; - color: #6b7280; - padding: 0.25rem; -} - -.reset-btn { - background: #6b7280; - color: white; - border: none; - padding: 0.25rem 0.5rem; - border-radius: 0.25rem; - cursor: pointer; - font-size: 0.75rem; - transition: all 0.2s; -} - -.reset-btn:hover { - background: #4b5563; -} - -.viewer-main { - display: flex; - align-items: center; - gap: 1rem; - padding: 1rem; - flex: 1; - min-height: 400px; -} - -.nav-btn { - background: rgba(0, 0, 0, 0.5); - color: white; - border: none; - width: 50px; - height: 50px; - border-radius: 50%; - cursor: pointer; - font-size: 1.5rem; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s; -} - -.nav-btn:hover:not(:disabled) { - background: rgba(0, 0, 0, 0.7); -} - -.nav-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.image-container { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - max-height: 70vh; - overflow: auto; - background: #f9fafb; - border-radius: 0.375rem; -} - -.viewer-image { - max-width: none; - max-height: none; - object-fit: contain; - transition: transform 0.2s ease; - cursor: zoom-in; -} - -.viewer-footer { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1rem; - border-top: 1px solid #e5e7eb; - background: #f9fafb; -} - -.image-info p { - margin: 0.25rem 0; - font-size: 0.875rem; - color: #6b7280; -} - -.image-counter { - font-size: 0.875rem; - color: #6b7280; - font-weight: 500; -} - -.thumbnail-strip { - display: flex; - gap: 0.5rem; - padding: 1rem; - background: #f9fafb; - border-top: 1px solid #e5e7eb; - overflow-x: auto; -} - -.thumbnail { - width: 80px; - height: 60px; - border: 2px solid transparent; - border-radius: 0.25rem; - cursor: pointer; - overflow: hidden; - transition: all 0.2s; - flex-shrink: 0; -} - -.thumbnail.active { - border-color: #3b82f6; -} - -.thumbnail:hover { - border-color: #3b82f6; -} - -.thumbnail-img { - width: 100%; - height: 100%; - object-fit: cover; -} - -@media (max-width: 768px) { - .browse-header { - flex-direction: column; - gap: 1rem; - } - - .search-filters { - flex-direction: column; - width: 100%; - } - - .search-box { - max-width: none; - } - - .filter-controls { - width: 100%; - } - - .filter-select { - flex: 1; - } - - .view-toggle { - width: 100%; - justify-content: center; - } - - .gallery-controls { - flex-direction: column; - gap: 1rem; - } - - .zoom-controls, - .view-controls { - justify-content: center; - } - - .artists-grid, - .artworks-grid, - .gallery-grid { - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); - } - - .artist-card { - flex-direction: column; - text-align: center; - } - - .pagination { - flex-wrap: wrap; - } - - .page-numbers { - order: 3; - width: 100%; - justify-content: center; - margin-top: 0.5rem; - } - - .image-viewer-content { - width: 95%; - max-height: 95vh; - } - - .viewer-header { - flex-direction: column; - gap: 1rem; - align-items: stretch; - } - - .viewer-controls { - justify-content: space-between; - } - - .viewer-zoom-controls { - flex-wrap: wrap; - justify-content: center; - } - - .viewer-main { - flex-direction: column; - gap: 0.5rem; - } - - .nav-btn { - width: 40px; - height: 40px; - font-size: 1.2rem; - } - - .thumbnail-strip { - padding: 0.5rem; - } - - .thumbnail { - width: 60px; - height: 45px; - } -} \ No newline at end of file diff --git a/ui/src/views/repository/components/ArtistsView.vue b/ui/src/views/repository/components/ArtistsView.vue new file mode 100644 index 0000000..ef24dcd --- /dev/null +++ b/ui/src/views/repository/components/ArtistsView.vue @@ -0,0 +1,128 @@ + + + + + \ No newline at end of file diff --git a/ui/src/views/repository/components/ArtworksView.vue b/ui/src/views/repository/components/ArtworksView.vue new file mode 100644 index 0000000..414e3e2 --- /dev/null +++ b/ui/src/views/repository/components/ArtworksView.vue @@ -0,0 +1,187 @@ + + + + + \ No newline at end of file diff --git a/ui/src/views/repository/components/GalleryView.vue b/ui/src/views/repository/components/GalleryView.vue new file mode 100644 index 0000000..f271859 --- /dev/null +++ b/ui/src/views/repository/components/GalleryView.vue @@ -0,0 +1,314 @@ + + + + + \ No newline at end of file diff --git a/ui/src/views/repository/components/ImageViewer.vue b/ui/src/views/repository/components/ImageViewer.vue new file mode 100644 index 0000000..43867ce --- /dev/null +++ b/ui/src/views/repository/components/ImageViewer.vue @@ -0,0 +1,431 @@ + + + + + \ No newline at end of file diff --git a/ui/src/views/repository/components/Pagination.vue b/ui/src/views/repository/components/Pagination.vue new file mode 100644 index 0000000..a08be81 --- /dev/null +++ b/ui/src/views/repository/components/Pagination.vue @@ -0,0 +1,131 @@ + + + + + \ No newline at end of file diff --git a/ui/src/views/repository/components/SearchPanel.vue b/ui/src/views/repository/components/SearchPanel.vue new file mode 100644 index 0000000..ab4663c --- /dev/null +++ b/ui/src/views/repository/components/SearchPanel.vue @@ -0,0 +1,164 @@ + + + + + \ No newline at end of file diff --git a/ui/src/views/repository/components/ViewModeToggle.vue b/ui/src/views/repository/components/ViewModeToggle.vue new file mode 100644 index 0000000..37ed3ed --- /dev/null +++ b/ui/src/views/repository/components/ViewModeToggle.vue @@ -0,0 +1,79 @@ + + + + + \ No newline at end of file