diff --git a/spx-gui/src/apis/common/index.ts b/spx-gui/src/apis/common/index.ts index 57fefcf70..c6eaf2de5 100644 --- a/spx-gui/src/apis/common/index.ts +++ b/spx-gui/src/apis/common/index.ts @@ -10,7 +10,7 @@ export type ByPage = { data: T[] } -export const OwnerAll = '*' +export const ownerAll = '*' export enum IsPublic { personal = 0, diff --git a/spx-gui/src/apis/project.ts b/spx-gui/src/apis/project.ts index 101303c8f..4f0e65a0f 100644 --- a/spx-gui/src/apis/project.ts +++ b/spx-gui/src/apis/project.ts @@ -1,8 +1,8 @@ import type { FileCollection, ByPage, PaginationParams } from './common' -import { client, IsPublic } from './common' +import { client, IsPublic, ownerAll } from './common' import { ApiException, ApiExceptionCode } from './common/exception' -export { IsPublic } +export { IsPublic, ownerAll } export enum ProjectDataType { Sprite = 0, @@ -101,12 +101,16 @@ export function deleteProject(owner: string, name: string) { export type ListProjectParams = PaginationParams & { isPublic?: IsPublic + /** Name of project owner, `*` indicates projects of all users */ owner?: string + /** Filter projects by name pattern */ + keyword?: string + /** Field by which to order the results */ + orderBy?: 'cTime' | 'uTime' | 'likeCount' | 'remixCount' | 'recentLikeCount' | 'recentRemixCount' + /** Order in which to sort the results */ + sortOrder?: 'asc' | 'desc' } -/** `owner: ownerAll` indicates that we want to list project of all users */ -export const ownerAll = '*' - export async function listProject(params?: ListProjectParams) { const { total, data } = await (client.get('/projects/list', params) as Promise< ByPage diff --git a/spx-gui/src/components/community/CommunityNavbar.vue b/spx-gui/src/components/community/CommunityNavbar.vue index 586ed3c96..462b8bc6d 100644 --- a/spx-gui/src/components/community/CommunityNavbar.vue +++ b/spx-gui/src/components/community/CommunityNavbar.vue @@ -31,13 +31,13 @@ import NavbarWrapper from '@/components/navbar/NavbarWrapper.vue' import NavbarDropdown from '../navbar/NavbarDropdown.vue' import NavbarNewProjectItem from '@/components/navbar/NavbarNewProjectItem.vue' import NavbarOpenProjectItem from '@/components/navbar/NavbarOpenProjectItem.vue' -import { getProjectsRoute } from '@/router' +import { getSearchRoute } from '@/router' const router = useRouter() const searchInput = ref('') function handleSearch() { - router.push(getProjectsRoute(searchInput.value)) + router.push(getSearchRoute(searchInput.value)) } diff --git a/spx-gui/src/components/project/item/ProjectItem.vue b/spx-gui/src/components/project/item/ProjectItem.vue index 38507936a..58b68e05f 100644 --- a/spx-gui/src/components/project/item/ProjectItem.vue +++ b/spx-gui/src/components/project/item/ProjectItem.vue @@ -205,7 +205,7 @@ const handleRemove = useMessageHandle( margin-top: 4px; display: flex; gap: 12px; - font-size: 14px; + font-size: 13px; color: var(--ui-color-grey-700); .part { diff --git a/spx-gui/src/components/ui/UIPagination.vue b/spx-gui/src/components/ui/UIPagination.vue index 179fde63d..1dc196717 100644 --- a/spx-gui/src/components/ui/UIPagination.vue +++ b/spx-gui/src/components/ui/UIPagination.vue @@ -58,7 +58,9 @@ import { computed } from 'vue' const props = defineProps<{ + /** Total page num */ total: number + /** Current page index, start from `1` */ current: number }>() const emit = defineEmits<{ diff --git a/spx-gui/src/pages/community/index.vue b/spx-gui/src/pages/community/index.vue index 343079e45..5e1fa8ccb 100644 --- a/spx-gui/src/pages/community/index.vue +++ b/spx-gui/src/pages/community/index.vue @@ -13,6 +13,8 @@ import CommunityNavbar from '@/components/community/CommunityNavbar.vue' .wrapper { width: 100%; height: 100%; + display: flex; + flex-direction: column; overflow-y: auto; background-color: var(--ui-color-grey-300); } diff --git a/spx-gui/src/pages/community/projects.vue b/spx-gui/src/pages/community/projects.vue deleted file mode 100644 index 7cb4e2a10..000000000 --- a/spx-gui/src/pages/community/projects.vue +++ /dev/null @@ -1,20 +0,0 @@ - - - - - diff --git a/spx-gui/src/pages/community/search.vue b/spx-gui/src/pages/community/search.vue new file mode 100644 index 000000000..94fba696e --- /dev/null +++ b/spx-gui/src/pages/community/search.vue @@ -0,0 +1,235 @@ + + + + + diff --git a/spx-gui/src/router.ts b/spx-gui/src/router.ts index 96be1c6f5..1e8bc5f1c 100644 --- a/spx-gui/src/router.ts +++ b/spx-gui/src/router.ts @@ -21,8 +21,8 @@ export function getProjectShareRoute(owner: string, name: string) { return getProjectPageRoute(owner, name) } -export function getProjectsRoute(keyword: string = '') { - return keyword !== '' ? `/projects?keyword=${encodeURIComponent(keyword)}` : '/projects' +export function getSearchRoute(keyword: string = '') { + return keyword !== '' ? `/search?q=${encodeURIComponent(keyword)}` : '/search' } const routes: Array = [ @@ -39,8 +39,8 @@ const routes: Array = [ component: () => import('@/pages/community/explore.vue') }, { - path: '/projects', - component: () => import('@/pages/community/projects.vue') + path: '/search', + component: () => import('@/pages/community/search.vue') }, { path: '/user/:name', diff --git a/spx-gui/src/utils/route.ts b/spx-gui/src/utils/route.ts new file mode 100644 index 000000000..ee247fd1d --- /dev/null +++ b/spx-gui/src/utils/route.ts @@ -0,0 +1,39 @@ +import { useRouter, type LocationQuery } from 'vue-router' + +function getStringParam(query: LocationQuery, key: string): string | null { + const value = query[key] + if (value == null) return null + if (Array.isArray(value)) return value[0] + return value +} + +/** + * Simplify manipulation for query parameters of current route. + * ```ts + * const query = useRouteQuery<'foo' | 'bar'>() + * const foo = query.get('foo') + * query.set('foo', '123') + * query.set({ foo: '123', bar: null }) + * ``` + */ +export function useRouteQuery() { + const router = useRouter() + + function get(key: K) { + return getStringParam(router.currentRoute.value.query, key) + } + + function set(key: K, value: string | null): void + function set(kvs: Partial>): void + function set(keyOrKvs: Partial> | string, value?: string | null) { + const kvs = typeof keyOrKvs === 'string' ? { [keyOrKvs]: value } : keyOrKvs + const newQuery = { ...router.currentRoute.value.query, ...kvs } + Object.keys(newQuery).forEach((k) => { + // remove fields with null value + if (newQuery[k] == null) delete newQuery[k] + }) + router.push({ query: newQuery }) + } + + return { get, set } +}