1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-07-27 04:20:43 +08:00

feat: 新增全局搜索功能,优化页面默认跳转逻辑

1. 新增全局搜索弹窗组件及相关事件常量
2. 修复首页默认跳转路径,改回首页而非动态页
3. 注释无用的单例运行代码
4. 添加背景模糊样式类
5. 更新搜索API类型定义
6. 底部导航栏搜索按钮触发全局搜索弹窗
This commit is contained in:
小莫唐尼
2026-06-13 00:29:49 +08:00
parent 8510b71ff6
commit 50cad268d3
9 changed files with 218 additions and 6 deletions
@@ -0,0 +1,166 @@
<script setup lang="ts">
import { indicesSearch } from '@/api/halo-base/common'
import { timeFormat } from '@/utils/base/timeFormat'
import { debounce } from '@/utils/base/debounce'
import { GLOBAL_SEARCH_EVENT_NAME } from '@/constants/events'
import type { HaloDocument, SearchOption, SearchResult } from '@halo-dev/api-client'
const { loading, error, data, run } = useRequest<SearchResult, SearchOption>(indicesSearch, { loadingToast: false, loadingToastContent: '加载中...', loadingToastMask: true })
const scroll = reactive({
top: 0,
})
const searchParams = ref<SearchOption>({
keyword: undefined,
limit: 50,
})
const searchResultTypeMap = {
'moment.moment.halo.run': 'moment',
'post.content.halo.run': 'post',
'doc.halo.run': 'doc',
}
const searchResultTypeCN = {
'moment.moment.halo.run': '瞬间',
'post.content.halo.run': '文章',
'doc.halo.run': '文档',
}
interface SearchResultItem extends HaloDocument {
releaseTime: string
typeCN: string
}
const searchResult = computed<Array<SearchResultItem>>(() => {
if (!data.value || data.value?.hits?.length === 0) {
return []
}
return data.value.hits.map((item) => {
return {
...item,
type: searchResultTypeMap[item.type],
typeCN: searchResultTypeCN[item.type],
releaseTime: timeFormat(item.updateTimestamp ?? item.creationTimestamp),
}
})
})
function handleSearch() {
if (!searchParams.value.keyword) {
uni.showToast({
title: '请输入搜索内容',
icon: 'none',
})
return
}
scroll.top = 0
run(searchParams.value)
}
function handleClose() {
setTimeout(() => {
uni.$emit(GLOBAL_SEARCH_EVENT_NAME)
}, 30)
}
const onViewScroll = debounce((e: any) => {
scroll.top = e.detail.scrollTop
}, 150)
onMounted(() => {
handleSearch()
})
</script>
<template>
<view
class="uh-backdrop-blur fixed inset-0 z-110 flex items-center justify-center bg-black/20"
@click.stop="handleClose"
>
<!-- 内容区 -->
<view class="box-border h-[65vh] w-[80vw] flex flex-col overflow-hidden rounded-2xl bg-page2 p-2" @click.stop>
<view class="box-border w-full flex shrink-0 items-center justify-between gap-x-4 p-2">
<view class="flex flex-1 items-center gap-x-2">
<input
v-model="searchParams.keyword" :auto-focus="true" type="text" placeholder="请输入搜索内容"
class="flex-1 border-2 border-gray-900 rounded-md border-solid px-2 py-1 text-xs text-gray-900 placeholder:text-xs"
>
<view
class="flex items-center justify-center rounded-lg bg-gray-900 px-3 py-2"
@click="handleSearch()"
>
<wd-icon name="search-line" color="#ffffff" :size="16" />
<text class="text-xs text-white">搜索</text>
</view>
</view>
<view class="shrink-0">
<wd-icon name="close-circle" size="24px" color="#1A1A1A" @click="handleClose" />
</view>
</view>
<!-- 搜索结果区 -->
<view class="mb-2 box-border flex items-center justify-between px-2">
<text class="text-xs text-gray-900 font-bold">搜索结果</text>
<text class="text-xs text-gray-600"> {{ searchResult.length }} </text>
</view>
<scroll-view
v-if="searchResult.length !== 0" class="h-[53vh] w-full" :scroll-y="true"
:scroll-top="scroll.top" @scroll="onViewScroll"
>
<view class="box-border flex flex-col gap-y-3 px-2">
<!-- 卡片 -->
<view
v-for="(item, index) in searchResult" :key="item.metadataName"
class="uh-shadow-xs box-border w-full flex flex-col gap-y-2 rounded-lg bg-white p-3"
>
<view class="flex items-center gap-x-2">
<view
class="flex items-center rounded-md bg-gray-900 px-1.5 py-0.5 text-xs text-white font-bold"
>
# <text class="pl-0.5 text-[12px]">{{ index + 1 }}</text>
</view>
<rich-text :nodes="item.title" class="line-clamp-1 text-sm text-gray-900 font-bold" />
</view>
<view class="line-clamp-2 w-full text-xs text-gray-600 leading-relaxed">
<rich-text :nodes="item.description ?? item.content" />
</view>
<view class="h-1px w-full bg-gray-50" />
<view class="flex items-center justify-between gap-x-4">
<view class="rounded-md bg-gray-900 px-2 py-1 text-[11px] text-white">
来自{{ item.typeCN }}
</view>
<text class="text-xs text-gray-600">{{ item.releaseTime }}</text>
</view>
</view>
</view>
</scroll-view>
<view v-else-if="loading" class="flex flex-1 items-center justify-center">
<wd-loading text="搜索中..." color="#1A1A1A" :size="52" />
</view>
<view v-else-if="error" class="flex flex-1 items-center justify-center">
<wd-empty tip="搜索失败">
<template #image>
<wd-icon name="close-circle" color="#1A1A1A" :size="52" />
</template>
<template #bottom>
<view
class="flex items-center justify-center rounded-lg bg-gray-900 px-3 py-2"
@click="handleSearch()"
>
<wd-icon name="search-line" color="#ffffff" :size="16" />
<text class="pl-1 text-xs text-white">点击重试</text>
</view>
</template>
</wd-empty>
</view>
<view v-else class="flex flex-1 items-center justify-center">
<wd-empty tip="暂无搜索结果">
<template #image>
<wd-icon name="empty" color="#1A1A1A" :size="52" />
</template>
</wd-empty>
</view>
</view>
</view>
</template>