50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | LiveUserFilter(实时用户过滤组件)

📅 我们继续 50 个小项目挑战!—— LiveUserFilter组件

仓库地址:https://github.com/SunACong/50-vue-projects

项目预览地址:https://50-vue-projects.vercel.app/

在这里插入图片描述


🔍 使用 Vue 3 和 Tailwind CSS 构建实时用户搜索过滤器

欢迎来到本篇教程!今天我们将使用 Vue 3<script setup> 语法结合 Tailwind CSS 来创建一个功能强大且视觉美观的实时用户搜索过滤组件。这个组件能够从远程 API 获取用户数据,并根据用户输入的关键词实时过滤并展示结果,非常适合用于构建用户目录、联系人列表或任何需要搜索功能的界面。

让我们开始吧!🚀


📝 应用目标

  • 使用 Vue 3 Composition API 管理异步数据获取与状态
  • 实现基于姓名和/或位置的实时用户搜索过滤
  • 展示加载状态和无结果状态
  • 使用 Tailwind CSS 快速构建现代化的 UI 组件
  • 优化用户体验,提供流畅的搜索反馈

🔧 技术实现点

技术点描述
Vue 3 <script setup>使用 ref, onMounted, computed 管理状态、生命周期和计算属性
async/await异步获取远程用户数据
computed 属性响应式地过滤用户列表
v-model 双向绑定绑定搜索输入框的值
v-for 渲环渲染动态生成用户列表项
v-if 条件渲染根据状态显示加载中、用户列表或无结果提示
Tailwind CSS快速实现响应式布局与视觉样式

📚 常量和路由定义

虽然本示例是一个独立的功能组件,但理解其核心配置和数据流对于维护和扩展至关重要:

常量/配置值/说明
API_URLhttps://randomuser.me/api?results=50 - 用于获取随机用户数据的公共 API
resultsCount50 - 一次请求获取的用户数量
searchTerm (响应式)存储用户输入的搜索关键词
users (响应式)存储从 API 获取的原始用户数据数组
filteredUsers (计算属性)根据 searchTerm 过滤后的用户列表
isLoading (响应式)控制加载状态显示的布尔值

💡 提示:在实际项目中,这些配置(如 API 地址)可以定义为常量或从环境变量中读取,便于统一管理和环境切换。


🖌️ 组件实现

🎨 模板结构 <template>

<template>
    <div
        class="flex min-h-screen items-center justify-center overflow-hidden bg-gray-100 font-sans">
        <div class="w-72 overflow-hidden rounded-lg bg-white shadow-md">
            <!-- 头部搜索区域 -->
            <div class="bg-blue-600 p-6 text-white">
                <h4 class="mb-1 text-xl font-bold">Live User Filter</h4>
                <small class="opacity-80">Search by name and/or location</small>
                <input
                    v-model="searchTerm"
                    @input="filterUsers"
                    class="mt-3 w-full rounded-full bg-blue-800/50 px-4 py-2 text-sm text-white focus:outline-none"
                    placeholder="Search" />
            </div>

            <!-- 用户列表区域 -->
            <ul class="max-h-96 overflow-y-auto">
                <!-- 加载状态 -->
                <li v-if="isLoading" class="p-6 text-center">
                    <h3>Loading...</h3>
                </li>

                <!-- 过滤后的用户列表 -->
                <li
                    v-for="user in filteredUsers"
                    :key="user.login.uuid"
                    class="flex items-center border-b border-gray-100 p-4">
                    <img
                        :src="user.picture.large"
                        :alt="`${user.name.first} ${user.name.last}`"
                        class="h-12 w-12 rounded-full object-cover" />
                    <div class="ml-3">
                        <h4 class="font-medium text-gray-900">
                            {{ user.name.first }} {{ user.name.last }}
                        </h4>
                        <p class="text-xs text-gray-500">
                            {{ user.location.city }}, {{ user.location.country }}
                        </p>
                    </div>
                </li>

                <!-- 无结果状态 -->
                <li
                    v-if="!isLoading && filteredUsers.length === 0"
                    class="p-6 text-center text-gray-500">
                    No users found
                </li>
            </ul>
        </div>
    </div>
</template>

模板部分清晰地分为两个主要区域:

  1. 头部搜索区域:包含标题、说明和一个搜索输入框。输入框使用 v-model 绑定到 searchTerm,并监听 @input 事件(尽管实际过滤由 computed 属性驱动)。
  2. 用户列表区域:一个可滚动的列表,根据 isLoadingfilteredUsers 的状态动态显示:
    • isLoadingtrue 时显示“Loading…”。
    • 否则,使用 v-for 遍历 filteredUsers 显示用户头像、姓名和位置。
    • filteredUsers 为空且非加载状态时,显示“No users found”。

💻 脚本逻辑 <script setup>

<script setup>
    import { ref, onMounted, computed } from 'vue'

    // 响应式状态
    const users = ref([])
    const searchTerm = ref('')
    const isLoading = ref(true)

    // 获取用户数据
    const fetchUsers = async () => {
        try {
            isLoading.value = true
            const response = await fetch('https://randomuser.me/api?results=50')
            const data = await response.json()
            users.value = data.results
        } catch (error) {
            console.error('Error fetching users:', error)
        } finally {
            isLoading.value = false
        }
    }

    // 过滤用户数据
    const filteredUsers = computed(() => {
        if (!searchTerm.value) return users.value
        const term = searchTerm.value.toLowerCase()
        return users.value.filter((user) => {
            const fullName = `${user.name.first} ${user.name.last}`.toLowerCase()
            const location = `${user.location.city} ${user.location.country}`.toLowerCase()
            return fullName.includes(term) || location.includes(term)
        })
    })

    // 组件挂载时获取数据
    onMounted(() => {
        fetchUsers()
    })

    // 暴露给模板的方法
    const filterUsers = () => {} // 仅为了模板中绑定,实际过滤通过computed完成
</script>

🎉 关键功能解析

状态定义

users 存储用户数据,searchTerm 存储搜索关键词,isLoading 控制加载状态。

数据获取

fetchUsers 函数使用 fetch API 异步获取 50 个随机用户数据,并在成功或失败后更新 isLoading 状态。

数据过滤

filteredUsers 是一个 computed 属性,它会自动响应 searchTermusers 的变化。当 searchTerm 为空时返回所有用户;否则,将用户姓名和位置转换为小写,并检查是否包含搜索关键词(也转为小写),返回匹配的用户数组。

生命周期

onMounted 钩子确保组件挂载后立即调用 fetchUsers 获取数据。

方法占位

filterUsers 函数虽然在模板中被绑定,但实际过滤逻辑由 computed 属性 filteredUsers 完成,因此该函数为空。v-model 的更新足以触发 computed 的重新计算。


🎨 Tailwind CSS 样式重点

类名作用
flex启用 Flexbox 布局
min-h-screen设置最小高度为视口高度,确保内容居中
items-center / justify-center在主轴和交叉轴上居中对齐
overflow-hidden隐藏溢出内容
bg-gray-100设置浅灰色背景
font-sans使用无衬线字体
w-72设置固定宽度(18rem)
rounded-lg添加中等圆角
bg-white白色背景
shadow-md添加中等阴影,提升立体感
bg-blue-600搜索区域深蓝色背景
p-6内边距
text-white白色文字
mb-1下边距
text-xl / text-sm字体大小
font-bold / font-medium字体粗细
opacity-80降低不透明度
mt-3上边距
rounded-full完全圆形(圆角)
bg-blue-800/50深蓝色背景,50% 透明度(使用 Tailwind CSS 新的透明度语法)
px-4 py-2水平/垂直内边距
focus:outline-none移除聚焦轮廓
max-h-96设置最大高度(24rem),创建滚动区域
overflow-y-autoY轴自动出现滚动条
border-b border-gray-100底部浅灰色边框,分隔列表项
p-4列表项内边距
flex items-center水平 Flex 布局,垂直居中
h-12 w-12头像固定大小
rounded-full头像圆形
object-cover图片填充容器并保持比例
ml-3左边距
text-gray-900 / text-gray-500文字颜色
text-xs小号文字

📁 常量定义 + 组件路由

constants/index.js 添加组件预览常量:

{
        id: 42,
        title: 'LiveUserFilter',
        image: 'https://50projects50days.com/img/projects-img/42-live-user-filter.png',
        link: 'LiveUserFilter',
    },

router/index.js 中添加路由选项:

{
        path: '/LiveUserFilter',
        name: 'LiveUserFilter',
        component: () => import('@/projects/LiveUserFilter.vue'),
    },

🏁 总结

在这篇教程中,我们成功构建了一个功能完整、界面美观的实时用户搜索过滤组件。我们利用了 Vue 3 的响应式系统(特别是 computed 属性)来实现高效的过滤逻辑,并通过 Tailwind CSS 快速搭建了现代化的 UI。

想要让你的用户过滤器更加强大?试试这些扩展功能:

  • 防抖搜索:如果数据量巨大或 API 有调用限制,可以为搜索输入添加防抖(debounce)功能,避免过于频繁的计算或 API 调用。
  • 高级筛选:除了搜索框,添加下拉菜单或复选框进行更精确的筛选(如按国家、性别等)。
  • 分页:当用户数据量非常大时,实现分页功能。
  • 缓存机制:缓存已获取的数据,避免重复请求。
  • 错误处理 UI:除了 console.error,在界面上显示友好的错误提示。
  • 键盘导航:支持使用键盘上下键在用户列表中导航。

👉 下一篇,我们将完成FeedbackUiDesign组件,实现了一个好玩儿的好评反馈组件。🚀

感谢阅读,欢迎点赞、收藏和分享 😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值