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:
@@ -0,0 +1,8 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
scene: git_message
|
||||
---
|
||||
|
||||
在此处编写规则,自定义 AI 生成提交信息的风格。
|
||||
|
||||
使用中文编写提交信息
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { GLOBAL_SEARCH_EVENT_NAME } from '@/constants/events'
|
||||
import FgTabbar from '@/tabbar/index.vue'
|
||||
import { isPageTabbar } from './tabbar/store'
|
||||
import { currRoute } from './utils'
|
||||
@@ -21,6 +22,17 @@ const helloKuRoot = ref('Hello AppKuVue')
|
||||
|
||||
const exposeRef = ref('this is form app.Ku.vue')
|
||||
|
||||
const isShowGlobalSearch = ref(false)
|
||||
function onShowGlobalSearch() {
|
||||
isShowGlobalSearch.value = !isShowGlobalSearch.value
|
||||
console.log('isShowGlobalSearch', isShowGlobalSearch.value)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
uni.$off(GLOBAL_SEARCH_EVENT_NAME, onShowGlobalSearch)
|
||||
uni.$on(GLOBAL_SEARCH_EVENT_NAME, onShowGlobalSearch)
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
exposeRef,
|
||||
})
|
||||
@@ -35,6 +47,9 @@ defineExpose({
|
||||
|
||||
<KuRootView />
|
||||
|
||||
<!-- 全局搜索弹窗 -->
|
||||
<uh-global-search v-if="isShowGlobalSearch" />
|
||||
|
||||
<FgTabbar v-if="isCurrentPageTabbar" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { http } from '@/http/alova'
|
||||
import type { IndexV1alpha1PublicApiIndicesSearchRequest, SearchResult } from '@halo-dev/api-client'
|
||||
import type { SearchOption, SearchResult } from '@halo-dev/api-client'
|
||||
|
||||
/**
|
||||
* 检查插件是否可用
|
||||
@@ -17,7 +17,7 @@ export function checkPluginAvailable(pluginId: string) {
|
||||
* @param data 搜索参数
|
||||
* @returns 搜索结果
|
||||
*/
|
||||
export function indicesSearch(data: IndexV1alpha1PublicApiIndicesSearchRequest) {
|
||||
export function indicesSearch(data: SearchOption) {
|
||||
return http.Post<SearchResult>(`/apis/api.halo.run/v1alpha1/indices/-/search`, data, {
|
||||
meta: { isHalo: true },
|
||||
})
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1,2 @@
|
||||
/** 全局搜索弹窗时间名称 */
|
||||
export const GLOBAL_SEARCH_EVENT_NAME = 'uh-show-global-search'
|
||||
@@ -115,7 +115,7 @@ const { list: postList, refresh } = useListData<ListedPostVo>({
|
||||
onLoad(() => {
|
||||
console.log('home onLoad')
|
||||
refresh()
|
||||
runSingle({ name: '019eb1ca-e37c-7729-97af-e0b658aeef0f' })
|
||||
// runSingle({ name: '019eb1ca-e37c-7729-97af-e0b658aeef0f' })
|
||||
})
|
||||
|
||||
onPageScroll(() => { })
|
||||
|
||||
@@ -14,8 +14,8 @@ definePage({
|
||||
onLoad(() => {
|
||||
console.log('index onLoad')
|
||||
// 默认跳转首页
|
||||
// uni.switchTab({ url: '/pages/home/home' })
|
||||
uni.switchTab({ url: '/pages/moments/moments' })
|
||||
uni.switchTab({ url: '/pages/home/home' })
|
||||
// uni.switchTab({ url: '/pages/moments/moments' })
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -9,3 +9,19 @@
|
||||
.uh-shadow-card {
|
||||
box-shadow: 0 16rpx 60rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.uh-backdrop-blur {
|
||||
backdrop-filter: blur(4rpx);
|
||||
}
|
||||
|
||||
.uh-backdrop-blur-xs {
|
||||
backdrop-filter: blur(8rpx);
|
||||
}
|
||||
|
||||
.uh-backdrop-blur-sm {
|
||||
backdrop-filter: blur(12rpx);
|
||||
}
|
||||
|
||||
.uh-backdrop-blur-md {
|
||||
backdrop-filter: blur(24rpx);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { setTabbarItem } from './i18n'
|
||||
import { tabbarList, tabbarStore } from './store'
|
||||
import { sleep } from '@/utils/common'
|
||||
import { debounce } from '@/utils/base/debounce'
|
||||
import { GLOBAL_SEARCH_EVENT_NAME } from '@/constants/events'
|
||||
import TabbarItem from './TabbarItem.vue'
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
@@ -132,6 +133,10 @@ function handleScrollToTop() {
|
||||
duration: 300,
|
||||
})
|
||||
}
|
||||
|
||||
function handleShowGlobalSearch() {
|
||||
uni.$emit(GLOBAL_SEARCH_EVENT_NAME)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -159,7 +164,7 @@ function handleScrollToTop() {
|
||||
class="uh-shadow-sm uh-backdrop-blur box-border h-54px w-54px flex items-center justify-center border border-white rounded-full border-solid bg-white/95 text-2xl"
|
||||
>
|
||||
<wd-icon v-if="backTopActive.show" name="arrow-up" :size="22" @click="handleScrollToTop" />
|
||||
<wd-icon v-else name="search-line" :size="22" />
|
||||
<wd-icon v-else name="search-line" :size="22" @click="handleShowGlobalSearch()" />
|
||||
</view>
|
||||
|
||||
<view v-if="!tabBarConfig.useFloat" class="pb-safe" />
|
||||
|
||||
Reference in New Issue
Block a user