动态更改Tailwind的class

工具ts

import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

使用例子

<div
    className={cn(
        "ml-auto text-xs",
        mail.selected === item.id
            ? "text-foreground"
            : "text-muted-foreground"
    )}
>
    {item.date}
</div>

即判定mail.selected === item.id​​是否成立,如果成立的话则将text-foreground​​类与前面的合并在一起,就可以实现动态的更改Tailwind的class的效果

自定义列表的点击选中改变样式

本质上还是通过全局状态控制,这里使用的是-->🍜Jotai原子状态控制来实现状态控制。
首先,创建一个全局状态,即一个对象,里面有一个selected​属性,然后在对应的列表的点击事件中,将selected​属性设置为当前列表的id,然后再通过-->动态更改Tailwind的class来改变对应的效果即可。

所以说说白了还是用全局状态的控制。

自定义列表

这里使用的是一个ScrollArea​​即一个可以下拉的组件,里面使用<Button>​​来当作列表。

<ScrollArea className="h-screen">
            <div className="flex flex-col gap-2 p-4 pt-0">
                {items.map((item) => (
                    <button
                        key={item.id}
                        className={cn(
                            "flex flex-col items-start gap-2 rounded-lg border p-3 text-left text-sm transition-all hover:bg-accent",
                            mail.selected === item.id && "bg-muted"
                        )}
                        onClick={() =>
                            setMail({
                                ...mail,
                                selected: item.id,
                            })
                        }
                    >
                        <div className="flex w-full flex-col gap-1">
                            <div className="flex items-center">
                                <div className="flex items-center gap-2">
                                    <div className="font-semibold">{item.name}</div>
                                    {!item.read && (
                                        <span className="flex h-2 w-2 rounded-full bg-blue-600"/>
                                    )}
                                </div>
                                <div
                                    className={cn(
                                        "ml-auto text-xs",
                                        mail.selected === item.id
                                            ? "text-foreground"
                                            : "text-muted-foreground"
                                    )}
                                >
                                    {item.date}
                                </div>
                            </div>
                            <div className="text-xs font-medium">{item.subject}</div>
                        </div>
                        {/*显示文本内容*/}
                        <div className="line-clamp-2 text-xs text-muted-foreground">
                            {item.text.substring(0, 300)}
                        </div>
                        {/*判定labels是否有内容*/}
                        {item.labels.length ? (
                            <div className="flex items-center gap-2">
                                {item.labels.map((label) => (
                                    <Badge key={label} variant={getBadgeVariantFromLabel(label)}>
                                        {label}
                                    </Badge>
                                ))}
                            </div>
                        ) : null}
                    </button>
                ))}
            </div>
        </ScrollArea>

首先最外层是一个div,没什么好说的,然后通过{items.map((item) => ())}​​来遍历items中的内容,每一个为一个item​​,针对点击的效果可以参考-->自定义列表的点击选中改变样式

封装Axios网络请求

一般不管是在Vue中还是在React中,使用最频繁的网络请求框架即Axios框架,而普通使用Axios只是简单的进行网络请求,我们可以将其进行封装,更系统更便捷的使用Axios。

主要封装的效果如下:

  1. 配置baseURL

  2. 设置超时时间

  3. Request拦截器

    1. 为Request添加token

  4. Response拦截器统一对请求失败进行处理

例子如下:

//axios 网络请求封装
import axios from 'axios'
import {getToken} from '@/utils/auth'
import {IResponse, IResponseCode} from '@/types/Response'
import {useNavigate} from "react-router-dom";


// 创建axios实例
const service = axios.create({
    // baseURL: import.meta.env.VITE_BASE_API as string, // api的base_url
    baseURL: "https://httpbin.org", // api的base_url
    timeout: 5000 // 请求超时时间
})
// request拦截器
service.interceptors.request.use(
    (config) => {
        // if (store.getters.token) {
        //     config.headers['Authorization'] = getToken()
        // }
        return config
    },
    (error) => {
        // Do something with request error
        console.log(error) // for debug
        Promise.reject(error)
    }
)
// response 拦截器
service.interceptors.response.use(
    (response) => {
        const res: IResponse = response.data
        if (res.code !== IResponseCode.SUCCESS) {
            console.log(res)
            // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
            if (
                res.code === IResponseCode.TOKEN_INVALID ||
                res.code === IResponseCode.TOKEN_EXPIRED
            ) {
                // 请自行在引入 MessageBox
                // import { Message, MessageBox } from 'element-plus'
                // MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
                //   confirmButtonText: '重新登录',
                //   cancelButtonText: '取消',
                //   type: 'warning'
                // }).then(() => {
                //   store.dispatch('FedLogOut').then(() => {
                //     location.reload() // 为了重新实例化vue-router对象 避免bug
                //   })
                // })
                const navigate = useNavigate();
                navigate('/login');
            }
            return Promise.reject('error')
        } else {
            return response.data
        }
    },
    (error) => {
        console.log('err' + error) // for debug
        return Promise.reject(error)
    }
)
export default service

用户信息存储

针对一般的博客或者论坛什么的,一般都需要存储当前的用户信息,已判断是否登录或者显示用户的信息,不会在每一次的请求中都更改用户的信息,那么在用户的信息存储中,我们可以使用比较简单的jotai​原子状态来存储全局数据。

就拿传统的User信息来举例子,在全局状态控制的文件state-controller.ts​中:

import {atom, useAtom} from "jotai";
// 创建一个Type保存用户的个人数据
type BcUser = {
    userId: number;
    username: string;
    address: string;
    publicKey: string;
    privateKey: string;
}
// 定义一个用户原子
const userAtom = atom<BcUser>({
    userId: 0,
    username: '初始化用户',
    address: '初始化地址',
    publicKey: '初始化公钥',
    privateKey: '初始化私钥'
})
// 导出函数
export function useBcUser() {
    return useAtom(userAtom);
}

在其他的组件中想使用的话,只需要
先获得useBcUser​方法,用此方法获得数据和set方法即可

import {useBcUser} from "@/state/state-controller.ts";
const [bcUser, setBcUser] = useBcUser();
//输出用户名
console.log("用户ID:" + bcUser.userId);
console.log("用户名:" + bcUser.username);
// 更改用户信息
setBcUser({
   ...bcUser,
    username: "new username"
});
// 输出用户信息
console.log("更改后的用户名:" + bcUser.username);