前端react面试题之实现网页多选搜索框

发布于:2025-06-29 ⋅ 阅读:(16) ⋅ 点赞:(0)

需求

提供100位用户信息。其用户信息含:{ id: 1, age: 42, name: '张小强', address: "北京" },
要求1:需要设计可以多选择来筛选得到指点条件用户表,可以选择=>各阶段年龄端或者不同地区的。选择的条件,可以清空;
要求2: 选择的条件,需要在页面路由上呈现;方便其他用户copy,可以查询到一样的结果;

实例

网页实现

实现

需要提前下载相关依赖哈,nanoid

                            
import React, { Fragment, useEffect, useState } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { nanoid } from 'nanoid'
import "./tableStyle.css"
export default function ShoppingMall(props) {

    const users = [
        {
            id: nanoid(),
            age: 42,
            name: '张小强',
            address: "北京"
        },
        {
            id: nanoid(),
            age: 12,
            name: '明明',
            address: "上海"
        },
        {
            id: nanoid(),
            age: 32,
            name: '王小美',
            address: "深圳"
        },
        {
            id: nanoid(),
            age: 22,
            name: '王小希',
            address: "上海"
        },
    ]
    const [defaultAgeRange, setDefaultAgeRange] = useState([])
    const [defaultAddresList, setDefaultAddresList] = useState([])
    const [tableSources, setTableSources] = useState([])
    const navigate = useNavigate()
    const [searchParams, setSearchParams] = useSearchParams()
    useEffect(() => {
        getAgeRange()
        getUsersAddress()
    }, [searchParams])

    const getAgeRange = (max = 100, range = 10) => {
        let ageRange = []
        for (let i = 0; i < max; i += range) {
            const checked = searchParams.get("ageRange")?.length > 0 ? searchParams.get("ageRange").includes(`${i}-${i + range}`) : undefined
            ageRange.push({
                id: nanoid(),
                label: `${i}-${i + range}`,
                value: {
                    min: i,
                    max: i + range
                },
                checked,
            })
        }
        setDefaultAgeRange(ageRange)

    }
    const getUsersAddress = () => {
        if (users?.length > 0) {
            const addresList = users?.map((item) => item?.address)
            const uniqueAddresList = [...new Set(addresList)].map((item) => {
                const checked = searchParams.get("addresList")?.length > 0 ? searchParams.get("addresList").includes(item) : undefined
                return {
                    id: nanoid(),
                    label: item,
                    value: item,
                    checked,
                }
            })
            setDefaultAddresList(uniqueAddresList)
        }

    }
    const changeChecked = (checked, value, fn) => {
        fn((pre) => {
            return pre.map((item) => {
                if (item.value === value) {
                    return {
                        ...item,
                        checked,
                    }
                } else {
                    return item
                }
            })
        })
    }

    const onSumbit = (settUrl = true) => {
        const ageRange = defaultAgeRange.filter((item) => item.checked)
        const addresList = defaultAddresList.filter((item) => item.checked).map((item) => item.value)
        if (ageRange.length == 0 && addresList.length == 0) {
            alert("请选择筛选条件")
        }
        let finde = []
        let url = ""
        if (ageRange.length > 0) {
            users.filter((item) => {
                ageRange.forEach((ite) => {
                    if (ite.value.min <= item.age && ite.value.max > item.age) {
                        finde.push(item)
                    }
                })
            })

            const ageRangeValue = ageRange.map((item) => item.label)
            url = `ageRange=${ageRangeValue}`
        }
        if (addresList.length > 0) {
            finde = url.length > 1 ? finde.filter((item) => addresList.includes(item.address)) : users.filter((item) => addresList.includes(item.address))
            url = url.length > 1 ? `${url}&addresList=${addresList.join(',')}` : `addresList=${addresList.join(',')}`
        }
        console.log("   finde==>", finde)
        setTableSources(finde)
        if (settUrl) {
            navigate(`/shoppingMall?${url}`)
        }


    }
    const clearAll = (data, fu) => {
        const newData = data.map((item) => {
            return {
                ...item,
                checked: false
            }
        })
        fu([...newData])
    }
    const onReset = () => {
        clearAll(defaultAgeRange, setDefaultAgeRange)
        clearAll(defaultAddresList, setDefaultAddresList)
    }
    const combineCompoent = (title, defaultList, changeFun) => {
        return <Fragment>
            <h2>{title}:
                {
                    defaultList?.filter((item) => item.checked)?.map((item, index) => {
                        return <Fragment>
                            <span style={{ marginRight: "10px" }} key={`select${index}`}>{item.label}</span>
                        </Fragment>
                    })
                }
                {defaultList?.some((item) => item?.checked) && <a key={`clear${title}`} style={{ color: "red" }} onClick={() => clearAll(defaultList, changeFun)}>清空</a>
                }
            </h2>
            <div>
                {
                    defaultList?.map((item, index) => {
                        return <Fragment>
                            <div style={{ display: "inline-block", width: "75px",border: "1px solid #ccc", borderRadius: "5px",marginLeft: "10px"}} key={`default${index}`}>
                                
                                <input type='checkbox'  
                                onChange={(e) => changeChecked(e.target.checked, item.value, changeFun)} checked={item?.checked} />{item.label}
                            </div>
                        </Fragment>
                    })
                }
                <hr />
            </div>
        </Fragment>
    }

    return (
        <div>
            <div key={"ageList"}>
                {combineCompoent("年龄", defaultAgeRange, setDefaultAgeRange)}
            </div>
            <div key={"cityList"}>
                {combineCompoent("城市", defaultAddresList, setDefaultAddresList)}
            </div>
            <div style={{
                display: "flex",
                justifyContent: "flex-end",
            }}>
                <button style={{ marginRight: "10px" }} onClick={() => onReset()}>取消</button>
                <button style={{ color: "red" }} onClick={() => onSumbit()}>提交</button>
            </div>
            <br />

            <table >
                <thead>
                    <tr>
                        <th > ID </th>
                        <th >Name</th>
                        <th >Age</th>
                        <th >City</th>
                    </tr>
                </thead>
                <tbody>
                    {
                        tableSources.length > 0 ? tableSources?.map((item) => {
                            return <tr style={{ textAlign: "center" }} key={item?.id}>
                                <td >{item?.id}</td>
                                <td >{item?.name}</td>
                                <td >{item?.age}</td>
                                <td >{item?.address}</td>
                            </tr>
                        }) : <tr style={{ textAlign: "center" }}>
                            <td >暂无数据</td>
                            <td >暂无数据</td>
                            <td >暂无数据</td>
                            <td >暂无数据</td>

                        </tr>
                    }
                </tbody>
            </table>
        </div>
    )
}

样式:

     /* 基础表格样式 */
     table {
         border-collapse: collapse;
         /* 关键:合并边框 */
         width: 100%;
         font-family: Arial, sans-serif;
     }

     /* 设置所有单元格边框 */
     th,
     td {
         border: 2px solid #7adb59;
         /* 1px实线边框,颜色自定义 */
         padding: 8px;
         text-align: left;
         background-color: #c9edd8;

     }

     /* 表头样式 */
     th {
         background-color: #c9edd8;
         border-color: #7adb59;
         /* 表头边框颜色可不同 */
     }

小结

实现代码中,我创建了mock data。只有简单的四条而已哈;如果想看更多数据的搜寻,可以自己再多创建几条数据;
使用到hook有 useEffect, useState,useNavigate, useSearchParams;
nanoid() 是可以生成随机唯一的id的
useNavigate 是可以跳转路由的
useSearchParams 是可以获取路由的参数的