From cc008ab99c8cad512369b6dfe8830126e440c82f Mon Sep 17 00:00:00 2001 From: Huangzhe Date: Wed, 14 May 2025 10:51:00 +0800 Subject: [PATCH] =?UTF-8?q?=E7=83=AD=E6=90=9C=E5=8A=9F=E8=83=BD=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增热搜类型定义 HotSearchItem 接口 2. 新增通用响应类型 ApiResponse 和 AxiosResponse 接口 3. 实现热搜列表展示功能 4. 实现搜索历史记录保存和展示功能 5. 优化搜索逻辑,添加搜索后状态更新 --- .vscode/settings.json | 2 +- components.d.ts | 5 - src/api/home/index.js | 11 +++ src/api/types/hotSearch.d.ts | 38 ++++++++ src/api/types/response.d.ts | 95 +++++++++++++++++++ src/hooks/chart/usePieChart.ts | 2 +- src/views/HomeSearch/Hooks/useSurveySearch.ts | 16 +++- src/views/HomeSearch/Index.vue | 40 +++++--- .../HomeSearch/components/Recommend/Index.vue | 36 +++++++ .../Recommend/hooks/useRecommend.ts | 36 +++++++ vite.config.ts | 4 +- 11 files changed, 258 insertions(+), 27 deletions(-) create mode 100644 src/api/types/hotSearch.d.ts create mode 100644 src/api/types/response.d.ts create mode 100644 src/views/HomeSearch/components/Recommend/Index.vue create mode 100644 src/views/HomeSearch/components/Recommend/hooks/useRecommend.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index a4f3d56..c4bc1c7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,7 +32,7 @@ "editor.formatOnSave": true }, "[vue]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "Vue.volar", "editor.formatOnSave": true }, "css.validate": false, //用来校验CSS文件中的语法错误和潜在的问题 diff --git a/components.d.ts b/components.d.ts index 41c15ba..0a031aa 100644 --- a/components.d.ts +++ b/components.d.ts @@ -33,7 +33,6 @@ declare module 'vue' { RichText: typeof import('./src/components/RichText.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] - SimpleLayout: typeof import('./src/components/Layout/SimpleLayout.vue')['default'] VanActionSheet: typeof import('vant/es')['ActionSheet'] VanButton: typeof import('vant/es')['Button'] VanCard: typeof import('vant/es')['Card'] @@ -41,11 +40,8 @@ declare module 'vue' { VanCellGroup: typeof import('vant/es')['CellGroup'] VanCheckbox: typeof import('vant/es')['Checkbox'] VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup'] - VanCol: typeof import('vant/es')['Col'] VanDivider: typeof import('vant/es')['Divider'] VanField: typeof import('vant/es')['Field'] - VanGrid: typeof import('vant/es')['Grid'] - VanGridItem: typeof import('vant/es')['GridItem'] VanIcon: typeof import('vant/es')['Icon'] VanList: typeof import('vant/es')['List'] VanNavBar: typeof import('vant/es')['NavBar'] @@ -54,7 +50,6 @@ declare module 'vue' { VanPopup: typeof import('vant/es')['Popup'] VanRadio: typeof import('vant/es')['Radio'] VanRadioGroup: typeof import('vant/es')['RadioGroup'] - VanRow: typeof import('vant/es')['Row'] VanSearch: typeof import('vant/es')['Search'] VanSpace: typeof import('vant/es')['Space'] VanStepper: typeof import('vant/es')['Stepper'] diff --git a/src/api/home/index.js b/src/api/home/index.js index c7f0966..7137109 100644 --- a/src/api/home/index.js +++ b/src/api/home/index.js @@ -75,3 +75,14 @@ export function saveTemplates(sn, data) { data }); } + +/** + * 获取推荐列表 + * @returns { import('src/api/types/response').HotSearchResponse} + */ +export function hotSearch() { + return request({ + url: `/console/hot_search/list`, + method: 'post', + }); +} diff --git a/src/api/types/hotSearch.d.ts b/src/api/types/hotSearch.d.ts new file mode 100644 index 0000000..03e46f2 --- /dev/null +++ b/src/api/types/hotSearch.d.ts @@ -0,0 +1,38 @@ +/** + * 热搜项类型定义 + */ +export interface HotSearchItem { + /** 热搜ID */ + id: number; + /** 热搜序列号 */ + sn: string; + /** 热搜状态 */ + status: number; + /** 创建时间 */ + created_at: string; + /** 更新时间 */ + updated_at: string; + /** 删除时间,为null表示未删除 */ + deleted_at: null | string; + /** 热搜关键词 */ + key_word: string; + /** 显示顺序 */ + display_order: number; + /** 创建用户ID */ + created_user_id: number; + /** 更新用户ID */ + updated_user_id: number; +} + +/** + * 热搜列表响应类型 + */ +export interface HotSearchListResponse { + list: HotSearchItem[]; + total?: number; +} + +/** + * 兼容旧类型 + */ +type HotSearch = HotSearchItem; \ No newline at end of file diff --git a/src/api/types/response.d.ts b/src/api/types/response.d.ts new file mode 100644 index 0000000..74c5097 --- /dev/null +++ b/src/api/types/response.d.ts @@ -0,0 +1,95 @@ +import { HotSearchItem } from './hotSearch'; + +/** + * 分页元数据接口 + */ +export interface MetaData { + /** 起始记录 */ + from: number; + /** 总记录数 */ + total: number; + /** 当前页码 */ + current_page: number; + /** 最后页码 */ + last_page: number; + /** 每页记录数 */ + per_page: number; +} + +/** + * 通用API响应接口 + */ +export interface ApiResponse { + /** 响应状态码,0表示成功 */ + code: number; + /** 响应消息,成功时为null */ + message: string | null; + /** 响应数据 */ + data: T; + /** 分页元数据,可选 */ + meta?: MetaData; +} + +/** + * Axios响应头部接口 + */ +export interface AxiosResponseHeaders { + 'content-type': string; + [key: string]: string; +} + +/** + * Axios配置接口 + */ +export interface AxiosConfig { + transitional: { + silentJSONParsing: boolean; + forcedJSONParsing: boolean; + clarifyTimeoutError: boolean; + }; + adapter: string[]; + transformRequest: any[]; + transformResponse: any[]; + timeout: number; + xsrfCookieName: string; + xsrfHeaderName: string; + maxContentLength: number; + maxBodyLength: number; + env: Record; + headers: { + Accept: string; + 'Content-Type': string; + Authorization: string; + Source: string; + remoteIp: string; + [key: string]: string; + }; + baseURL: string; + url: string; + method: string; + allowAbsoluteUrls: boolean; + [key: string]: any; +} + +/** + * 完整的Axios响应接口 + */ +export interface AxiosResponse { + /** 响应数据 */ + data: ApiResponse; + /** HTTP状态码 */ + status: number; + /** HTTP状态文本 */ + statusText: string; + /** 响应头 */ + headers: AxiosResponseHeaders; + /** 请求配置 */ + config: AxiosConfig; + /** 请求对象 */ + request: Record; +} + +/** + * 热搜列表响应类型 + */ +export type HotSearchResponse = AxiosResponse; diff --git a/src/hooks/chart/usePieChart.ts b/src/hooks/chart/usePieChart.ts index e5dcd3d..a2f91db 100644 --- a/src/hooks/chart/usePieChart.ts +++ b/src/hooks/chart/usePieChart.ts @@ -48,7 +48,7 @@ function useSetPieChart( // 检测边界范围 dom 和 data 是否存在 onMounted(() => { - console.log(dom); + // console.log(dom); if (!dom || data.length === 0) return; // 在 dom 挂载之后,显示饼图 pieChart.value = chart.init(dom.value); diff --git a/src/views/HomeSearch/Hooks/useSurveySearch.ts b/src/views/HomeSearch/Hooks/useSurveySearch.ts index c7370b1..d4aa19a 100644 --- a/src/views/HomeSearch/Hooks/useSurveySearch.ts +++ b/src/views/HomeSearch/Hooks/useSurveySearch.ts @@ -1,5 +1,6 @@ import { nextTick, ref, watch } from 'vue'; import { getSurveysPage } from '@/api/home'; +import { saveSearchHistory } from "@/views/HomeSearch/components/Recommend/hooks/useRecommend" // 问卷 const surveys = ref([]); @@ -17,7 +18,7 @@ const dirty = ref(true); // loading 状态 const loading = ref(false); -async function handleSearch() { +async function handleSearch () { // loading 状态开启 // loading.value = true; const params = { @@ -43,21 +44,25 @@ async function handleSearch() { /** * 更新 搜索关键字信息 */ -async function updateKeyword() { +async function updateKeyword () { // 排除边界条件 if (!keyword.value) return; // 重新获取数据 await handleSearch(); -// 处理后置条件,点击搜索之后,索引更新, + // 处理后置条件,点击搜索之后,索引更新, index.value = 1; + // 把关键词添加到 localStorage 中 + saveSearchHistory(keyword.value) + // 打开页面展示状态 + loading.value = true } /** * * @param value */ -function updatePageCount(value: number) { +function updatePageCount (value: number) { pageCount.value = value; } @@ -77,4 +82,5 @@ watch(index, async () => { }); }); -export { keyword, updateKeyword as handleSearch, lastIndex, loading, surveys, index }; \ No newline at end of file +export { keyword, updateKeyword as handleSearch, lastIndex, loading, surveys, index }; + diff --git a/src/views/HomeSearch/Index.vue b/src/views/HomeSearch/Index.vue index 28412c5..7c05a80 100644 --- a/src/views/HomeSearch/Index.vue +++ b/src/views/HomeSearch/Index.vue @@ -2,9 +2,16 @@ import Search from '@/components/Search/Index.vue'; import TemplateMarket from '@/views/HomeSearch/components/TemplateMarket/Index.vue'; import MineSurvey from '@/views/HomeSearch/components/MineSurvey/Index.vue'; -import { handleSearch, keyword } from '@/views/HomeSearch/Hooks/useSurveySearch'; +import { handleSearch, keyword, loading } from '@/views/HomeSearch/Hooks/useSurveySearch'; import Layout from '@/components/Layout/CommonLayout.vue'; import { visible } from '@/views/HomeSearch/Hooks/useHomeSearch'; +import RecommendTag from '@/views/HomeSearch/components/Recommend/Index.vue'; +import { onMounted } from 'vue'; + +onMounted(() => { + // 当页面取消挂载时,重置页面展示的状态 + loading.value = false +}) \ No newline at end of file diff --git a/src/views/HomeSearch/components/Recommend/hooks/useRecommend.ts b/src/views/HomeSearch/components/Recommend/hooks/useRecommend.ts new file mode 100644 index 0000000..c0aba9b --- /dev/null +++ b/src/views/HomeSearch/components/Recommend/hooks/useRecommend.ts @@ -0,0 +1,36 @@ +import { hotSearch } from "@/api/home"; +import type { HotSearch } from "@/api/types/hotSearch"; +import { ref } from "vue"; + +// 从服务器接受的列表内容 +const list = ref([]) +// 历史列表 +const history = ref>() +initialHistory() + +async function useFetchRecommon () { + const { data } = await hotSearch() + list.value = data.data +} + +// 初始化历史 +function initialHistory () { + const historyStr = localStorage.getItem('history') + if (historyStr) { + history.value = new Set(JSON.parse(historyStr)) + } else { + history.value = new Set() + } +} + +// 保存历史 +function saveSearchHistory (val: string) { + if (!history.value) { + history.value = new Set() + } + history.value.add(val) + localStorage.setItem('history', JSON.stringify(Array.from(history.value))) +} + + +export { useFetchRecommon, list,saveSearchHistory, history } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 4be5a5d..b73d762 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,7 +10,7 @@ import postCssPxToRem from 'postcss-pxtorem'; import legacy from '@vitejs/plugin-legacy'; // shift + alt 快速定位到对应组件 import { codeInspectorPlugin } from 'code-inspector-plugin'; -import tailwindcss from '@tailwindcss/vite'; +// import tailwindcss from '@tailwindcss/vite'; export default defineConfig(({ mode }) => { // 接收 mode 参数 @@ -77,7 +77,7 @@ export default defineConfig(({ mode }) => { codeInspectorPlugin({ bundler: 'vite' }), - tailwindcss() + // tailwindcss() ], resolve: { alias: {