需求分析
- 展示切换动画
- 搜索框输入文字,自动发送请求
- 搜索结果展示
- 搜索状态维护
- 历史搜索展示,点击历史搜索后发送请求
- 历史搜索更多切换动画
- 效果
<script setup lang="ts"> import OpSearch from '@/components/OpSearch.vue' import { ref } from 'vue' import { fetchSearchData } from '@/api/search' import type { ISearchResult } from '@/types' import { useToggle } from '@/use/useToggle' import { computed } from 'vue' import { watch } from 'vue' // 声明事件接口,接口中属性值是一个函数,函数名是cancel,返回值是一个函数void interface IEmits {(e: 'cancel'): void }const searchValue = ref('') const searchResult = ref([] as ISearchResult[])// 定义一个事件变量,用defineEmits方法实现,方法中引入声明的事件接口 const emits = defineEmits<IEmits>()const HISTORY_TAGS = ['披萨','标签2','标签3','标签4','标签5','标签6','标签7', ] const [isHistoryTagShown, toggleHistoryTag] = useToggle(false) const historyTags = computed(() => (isHistoryTagShown.value ? HISTORY_TAGS : HISTORY_TAGS.slice(0, 5)))// 有三种状态:搜索初始化、搜索完成、搜索中 const [INIT, DONE, DOING] = [-1, 0, 1] const searchState = ref(INIT)const onSearch = async (v?: string | number) => {console.log('onSearch', v)// 防止搜索状态错误try {searchState.value = DOINGconst { list } = await fetchSearchData(v as string)searchResult.value = list} finally {searchState.value = DONE} } const onTagClick = (v:string) => {searchValue.value = vonSearch(v) }watch(searchValue, (new_v) => {if(!new_v) {searchResult.value = []return}onSearch(new_v) }) </script><template><!-- 调用事件变量,传入事件名cancel // 模板代码中引入定义的事件,用来在父组件中使用对应的事件 --><div class="search-view"><OpSearchshow-actionv-model="searchValue"shape="round"placeholder="请输入搜索关键词"@search="onSearch"@cancel="emits('cancel')"/><div v-if="!searchValue" class="search-view__history"><div class="label">历史搜索</div><TransitionGroup name="list"><div class="history-tag" v-for="v in historyTags" :key="v" @click="onTagClick(v)">{{ v }}</div><div class="history-tag" key="arrow" @click="toggleHistoryTag"><VanIcon v-if="isHistoryTagShown" name="arrow-up"></VanIcon><VanIcon v-else name="arrow-down"></VanIcon></div></TransitionGroup></div><div v-else class="search-view__result"><div class="searching" v-if="searchState === DOING">~正在搜索</div><template v-if="searchState === DONE"><div class="result-item" v-for="v in searchResult" :key="v.label"><VanIcon name="search"></VanIcon><div class="name">{{ v.label }}</div><div class="count">约{{ v.resultCount }}个结果</div></div><!-- 搜索结果状态维护 --><div class="no-result" v-if="!searchResult.length">~暂无推荐</div></template></div></div> </template><style lang="scss"> .search-view {position: absolute;top: 0;bottom: 0;right: 0;left: 0;background-color: white;z-index: 999;&__history {padding: var(--van-padding-sm);.label {margin-bottom: var(--van-padding-xs);}.history-tag {display: inline-block;font-size: 12px;border-radius: 10px;color: var(--van-gray-6);background: var(--van-gray-1);padding: 4px 8px;margin-right: 10px;margin-bottom: var(--van-padding-xs);}}&__result {.result-item {display: flex;align-items: center;font-size: 12px;padding: 10px;border-radius: 1px solid var(--van-gray-1);.name {// 撑满满足padding的一行flex: 1;padding-left: 6px;}.count {font-size: 12px;color: var(--van-gray-6);}}.no-result, .searching {font-size: 12px;padding: 100px 0;text-align: center;color: var(--van-gray-6)}} }.list-enter-active, .list-leave-active {transition: all 1s ease; } .list-enter-from, .list-leave-to {opacity: 0;transform: translateY(30px); } </style>
使用 <transition>和 <transition-group> 实现动画效果
使用
- 在
<transition>
组件中,你可以使用name
属性来指定动画的类名,在CSS中定义类名,并为其添加过渡效果
<transition>
<template><!-- 动画组件使用方法 --><Transition name="fade"><SearchView v-if="isSearchViewShown" @cancel="toggleSearchView"></SearchView></Transition></template><style lang="scss"> // 动画执行效果,消失效果 .fade-enter-active, .fade-leave-active {transition: opacity 0.5s ease; } // 动画进行时状态效果 .fade-enter-from, .fade-leave-to {opacity: 0; } </style>
<transition-group>
<template><TransitionGroup name="list">// 组件里内容使用了v-for,是数组形式<div class="history-tag" v-for="v in historyTags" :key="v" @click="onTagClick(v)">{{ v }}</div><div class="history-tag" key="arrow" @click="toggleHistoryTag"><VanIcon v-if="isHistoryTagShown" name="arrow-up"></VanIcon><VanIcon v-else name="arrow-down"></VanIcon></div></TransitionGroup></template><style lang="scss"> // 定义动画css样式 .list-enter-active, .list-leave-active {transition: all 1s ease; } .list-enter-from, .list-leave-to {opacity: 0;transform: translateY(30px); }</style>
Search 组件复用
- 将之前章节写好的OpSearch组件复用到SearchView组件中
<script setup lang="ts"> //引入组件 import OpSearch from '@/components/OpSearch.vue' import { ref } from 'vue'const onSearch = async (v?: string | number) => {console.log('onSearch', v) }// 定义搜索输入框里的参数变量 const searchValue = ref('')// 声明事件接口,接口中属性值是一个函数,函数名是cancel,返回值是一个函数void interface IEmits {(e: 'cancel'): void } // 定义一个事件变量,用defineEmits方法实现,方法中引入声明的事件接口 const emits = defineEmits<IEmits>()</script><template> // 使用组件<OpSearchshow-action //对变量searchValue值进行双向绑定v-model="searchValue"shape="round"placeholder="请输入搜索关键词" // 创建onSearch方法@search="onSearch" //定义cancel事件@cancel="emits('cancel')"/></template>
computed 计算属性
理解
- 方便地计算和监听数据的变化。
<script setup lang="ts"> import { useToggle } from '@/use/useToggle' import { computed } from 'vue'const HISTORY_TAGS = ['披萨','标签2','标签3','标签4','标签5','标签6','标签7', ] const [isHistoryTagShown, toggleHistoryTag] = useToggle(false)const historyTags = computed(() => (isHistoryTagShown.value ? HISTORY_TAGS : HISTORY_TAGS.slice(0, 5)))<template><div class="history-tag" key="arrow" @click="toggleHistoryTag"><VanIcon v-if="isHistoryTagShown" name="arrow-up"></VanIcon><VanIcon v-else name="arrow-down"></VanIcon></div></template>
watch 监听属性
理解
watch
函数接受两个参数:一个是要监听的参数,以及一个回调函数。回调函数触发的前提是,当被监听的参数发生变化时,回调函数将被执行。<script setup lang="ts"> // 引入watch函数 import { watch } from 'vue'// watch监听函数的使用方法,监听searchValue参数又叫属性值的变化,有变动时就会触发回调函数中的代码。 watch(searchValue, (new_v) => {if(!new_v) {searchResult.value = []return}onSearch(new_v) })</script>
使用
axios实例发送业务请求
- 开发环境配置反向代理使用服务接口
- 设置请求响应拦截
- 创建具体功能请求函数
- 调用功能请求函数
mock 请求:
看这篇文章 使用apifox创建一个Mock Server Api 接口-CSDN博客