feat: 优化搜索功能并添加历史记录功能

1. 新增历史记录API模块,支持获取和添加搜索历史
2. 优化搜索组件交互体验
3. 更新搜索推荐功能,支持历史记录展示
4. 修复搜索框焦点控制问题
5. 优化搜索状态管理逻辑
This commit is contained in:
Huangzhe
2025-05-18 11:40:32 +08:00
parent 036fe0b4f7
commit 51a9fd085e
7 changed files with 128 additions and 38 deletions

38
src/api/history/index.ts Normal file
View File

@@ -0,0 +1,38 @@
import request from '@/utils/request';
/**
* 获取历史记录
* @returns
*/
export function fetchHistory() {
return request({
url: `/console/hot_search_history/list`,
method: 'get'
});
}
/**
* 增加历史记录
* @param keyword {string}
* @returns
*/
export function addHistory(keyword: string) {
return request({
url: `/console/hot_search_history/add`,
method: 'post',
data: {
keyword: keyword
}
});
}
/**
* 删除所有的历史记录
* @returns
*/
export function clearHistory() {
return request({
url: `/console/hot_search_history/clear`,
method: 'post'
});
}

View File

@@ -1,6 +1,7 @@
<template> <template>
<section class="search-container"> <section class="search-container">
<van-search <van-search
v-focus="focus"
v-model="value" v-model="value"
:placeholder="placeholder" :placeholder="placeholder"
style="--van-search-padding: 0" style="--van-search-padding: 0"
@@ -12,8 +13,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { vFocus } from '@/utils/directives/useVFocus';
// 搜索的关键词 // 搜索的关键词
const value = defineModel<string>('value', { required: true }); const value = defineModel<string>('value', { required: true });
// focus 是否默认调出键盘
const focus = defineModel('focus', { default: false });
/** /**
* @description 搜索方法 * @description 搜索方法
*/ */

View File

@@ -1,10 +1,11 @@
import type { Directive } from 'vue'; import type { Directive, DirectiveBinding } from 'vue';
/** /**
* 自定义指令,用于在元素挂载后自动获取焦点 * 自定义指令,用于在元素挂载后自动获取焦点
*/ */
export const vFocus: Directive = { export const vFocus: Directive = {
mounted(el: HTMLInputElement) { mounted(el: HTMLInputElement, binding: DirectiveBinding<any, string>) {
el.focus(); const value = binding.value;
value && el.focus();
} }
}; };

View File

@@ -1,6 +1,6 @@
import { nextTick, ref, watch } from 'vue'; import { nextTick, ref, watch } from 'vue';
import { getSurveysPage } from '@/api/home'; import { getSurveysPage } from '@/api/home';
import { saveSearchHistory } from '@/views/HomeSearch/components/Recommend/hooks/useRecommend'; import { initialHistory, saveSearchHistory } from '@/views/HomeSearch/components/Recommend/hooks/useRecommend';
import { getSearchInfo } from '@/api/home/search'; import { getSearchInfo } from '@/api/home/search';
// 问卷 // 问卷
@@ -23,7 +23,7 @@ const templates = ref();
// banners // banners
const banners = ref(); const banners = ref();
async function handleSearch () { async function handleSearch() {
// loading 状态开启 // loading 状态开启
// loading.value = true; // loading.value = true;
const params = { const params = {
@@ -54,13 +54,16 @@ async function handleSearch () {
/** /**
* 更新 搜索关键字信息 * 更新 搜索关键字信息
*/ */
async function updateKeyword (key?: string) { async function updateKeyword(key?: string) {
// 如果 key 存在,则更新关键字 // 如果 key 存在,则更新关键字
if (key) keyword.value = key; if (key) keyword.value = key;
// 排除边界条件 // 排除边界条件
if (!keyword.value) return; if (!keyword.value) {
initialHistory()
loading.value = false;
return;
}
// 重新获取数据 // 重新获取数据
await handleSearch(); await handleSearch();
@@ -76,7 +79,7 @@ async function updateKeyword (key?: string) {
* *
* @param value * @param value
*/ */
function updatePageCount (value: number) { function updatePageCount(value: number) {
pageCount.value = value; pageCount.value = value;
} }

View File

@@ -19,7 +19,7 @@ onMounted(() => {
<template> <template>
<section class="search-container"> <section class="search-container">
<div class="search"> <div class="search">
<search v-model:value="keyword as string" :search="handleSearch" /> <search :focus="true" v-model:value="keyword as string" :search="handleSearch" />
</div> </div>
<!-- 广告区域 --> <!-- 广告区域 -->
<image-slider :banners="banners" v-if="banners?.length" /> <image-slider :banners="banners" v-if="banners?.length" />
@@ -27,10 +27,16 @@ onMounted(() => {
<section class="result" v-if="loading"> <section class="result" v-if="loading">
<!-- 我的任务区域 --> <!-- 我的任务区域 -->
<layout v-if="visible.mineSurvey" title="我的任务"> <layout v-if="visible.mineSurvey" title="我的任务">
<template #title>
<span class="title">我的任务</span>
</template>
<mine-survey /> <mine-survey />
</layout> </layout>
<!-- 更多模板区域 --> <!-- 更多模板区域 -->
<layout v-if="visible.templateMarket" title="问卷模板"> <layout v-if="visible.templateMarket" title="问卷模板">
<template #title>
<span class="title">问卷模板</span>
</template>
<template-market /> <template-market />
</layout> </layout>
</section> </section>
@@ -44,6 +50,18 @@ onMounted(() => {
<style scoped lang="scss"> <style scoped lang="scss">
@use '@/assets/css/theme'; @use '@/assets/css/theme';
.title {
overflow-wrap: break-word;
color: rgba(0, 0, 0, 1);
font-size: 14px;
font-family: PingFangSC-Medium;
font-weight: 500;
text-align: left;
white-space: nowrap;
line-height: 20px;
margin: 28px 297px 0 2px;
}
.search-container { .search-container {
.search { .search {
padding-top: 10px; padding-top: 10px;

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import Layout from '@/components/Layout/CommonLayout.vue'; import Layout from '@/components/Layout/CommonLayout.vue';
import { list, useFetchRecommon, history } from './hooks/useRecommend'; import { list, useFetchRecommon, history, delHistory } from './hooks/useRecommend';
import { Delete } from '@element-plus/icons-vue'; import { Delete } from '@element-plus/icons-vue';
import { handleSearch } from '../../Hooks/useSurveySearch'; import { handleSearch } from '../../Hooks/useSurveySearch';
@@ -10,6 +10,9 @@ useFetchRecommon();
<template> <template>
<section class="recommend-container"> <section class="recommend-container">
<layout title="热门搜索"> <layout title="热门搜索">
<template #title>
<el-text class="title">热门搜索</el-text>
</template>
<el-space> <el-space>
<el-tag style="color: black" color="white" :hit="false" round v-for="tag in list">{{ <el-tag style="color: black" color="white" :hit="false" round v-for="tag in list">{{
tag.key_word tag.key_word
@@ -20,9 +23,9 @@ useFetchRecommon();
<layout> <layout>
<template #title> <template #title>
<section class="history-title"> <section class="history-title">
<el-text>历史记录</el-text> <el-text class="title">历史记录</el-text>
<div> <div>
<el-icon><Delete /></el-icon> <el-icon @click="delHistory"><Delete /></el-icon>
</div> </div>
</section> </section>
</template> </template>
@@ -44,6 +47,15 @@ useFetchRecommon();
<style scoped lang="scss"> <style scoped lang="scss">
@use '@/assets/css/theme'; @use '@/assets/css/theme';
.title {
font-size: 25px;
font-weight: 800;
margin-bottom: 10px;
color: rgba(102, 102, 102, 1);
font-size: 13px;
font-family: PingFangSC;
}
.recommend-container { .recommend-container {
margin-top: 20px; margin-top: 20px;
padding: 0 (theme.$gap * 2); padding: 0 (theme.$gap * 2);

View File

@@ -1,36 +1,49 @@
import { hotSearch } from "@/api/home"; import { addHistory, clearHistory, fetchHistory } from '@/api/history';
import type { HotSearch } from "@/api/types/hotSearch"; import { hotSearch } from '@/api/home';
import { ref } from "vue"; import type { HotSearch } from '@/api/types/hotSearch';
import { ref } from 'vue';
// 从服务器接受的列表内容 // 从服务器接受的列表内容
const list = ref<HotSearch[]>([]) const list = ref<HotSearch[]>([]);
// 历史列表 // 历史列表
const history = ref<Set<string>>() const history = ref<Set<string>>(new Set());
initialHistory() initialHistory();
async function useFetchRecommon () { async function useFetchRecommon() {
const { data } = await hotSearch() const { data } = await hotSearch();
list.value = data.data list.value = data.data;
} }
// 初始化历史 // 初始化历史
function initialHistory () { async function initialHistory() {
const historyStr = localStorage.getItem('history') // 屏蔽从storage获取记录的方式
if (historyStr) { // const historyStr = localStorage.getItem('history')
history.value = new Set(JSON.parse(historyStr)) // if (historyStr) {
} else { // history.value = new Set(JSON.parse(historyStr))
history.value = new Set() // } else {
} // history.value = new Set()
// }
const { data } = await fetchHistory();
console.log(data);
data.data.forEach((item: { id: number; keyword: string }) => {
history.value.add(item.keyword);
});
} }
// 保存历史 // 保存历史
function saveSearchHistory (val: string) { async function saveSearchHistory(val: string) {
if (!history.value) { // 屏蔽从storage获取记录的方式
history.value = new Set() // if (!history.value) {
} // history.value = new Set();
history.value.add(val) // }
localStorage.setItem('history', JSON.stringify(Array.from(history.value))) // history.value.add(val);
// localStorage.setItem('history', JSON.stringify(Array.from(history.value)));
addHistory(val);
} }
async function delHistory() {
export { useFetchRecommon, list,saveSearchHistory, history } await clearHistory();
}
export { useFetchRecommon, list, saveSearchHistory, history, delHistory,initialHistory };