该项目用的是React+TS写的,是自学写的,写得不好见谅!然后里面有一个自封的BtnIcon组件,那个是我页面需要用的,相当于是我的草稿。和希望清单无关。
1、介绍
Shadcn UI:Shadcn UI不是一个UI库 或者 框架,它也是一个集合【可复用组件的集合】
- 官网
https://ui.shadcn.com/
- 中文网
https://www.shadcn.com.cn/docs/installation
2、安装
- 安装
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
- 在 tsconfig.json 里面添加配置
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
}
}
- 更新 vite.config.ts
npm i -D @types/node
import path from "path"
import react from "@vitejs/plugin-react"
import { defineConfig } from "vite"
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
})
- 运行 init 命令以设置您的项目:
shadcn-ui
npx shadcn-ui@latest init
- 配置components.json
Would you like to use TypeScript (recommended)? no / yes
Which style would you like to use? › Default
Which color would you like to use as base color? › Slate
Where is your global CSS file? › › src/index.css
Do you want to use CSS variables for colors? › no / yes
Where is your tailwind.config.js located? › tailwind.config.js
Configure the import alias for components: › @/components
Configure the import alias for utils: › @/lib/utils
Are you using React Server Components? › no / yes (no)
3、使用
Shadcn UI 通过 npx 添加你需要用到的组件
-
案列【按钮】
- 首先 添加按钮
npx shadcn-ui@latest add button添加完之后,在 src 的 components 文件夹下的 ui 文件夹里面就会生成一个 button.tsx
- 在需要的部分
```
//导入
import { Button } from "@/components/ui/button"
function App() {
return (
<>
<div>
{/*使用*/}
<Button>Click me</Button>
</div>
</>
)
}
export default Apps
```
4、希望清单
- App.tsx
import React, { useState } from 'react';
import './App.css';
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { InputForm } from './InputForm';
import { ListType } from '@/types/ListType';
import { BtnIcon } from '@/btnIcon';
import logo from "@/assets/1.png";
function App() {
const [data, setData] = useState<ListType[]>([
{
id: 1,
plan: '学习TailwindCSS',
date: '2024-08-12',
status: 1,
},
]);
const addBtn = (newData: ListType) => {
setData([...data, newData]);
};
const delBtn = (id: number) => {
setData(data.map((item) =>
item.id === id ? { ...item, status: 0 } : item
));
};
const selectsData = ['选择1', '选择2'];
const handleClick = (value: string) => {
console.log(value);
}
const handleSelect = (value: string) => {
console.log('select',value);
}
return (
<div className='w-screen h-screen flex justify-center'>
<div className="tabs">
<Tabs defaultValue="dataList" className="w-[400px]">
<TabsList>
<TabsTrigger value="btn">按钮</TabsTrigger>
<TabsTrigger value="dataList">列表</TabsTrigger>
<TabsTrigger value="addData">添加</TabsTrigger>
</TabsList>
{/* 按钮 (前端机器管理)*/}
<TabsContent value="btn">
<BtnIcon title='测试1' photo={logo} selects={selectsData} dataClick={handleClick} onSelect={handleSelect}></BtnIcon>
<BtnIcon title='测试2' selects={selectsData} className='mt-[20px]' dataClick={handleClick} onSelect={handleSelect}></BtnIcon>
</TabsContent>
{/* 列表 */}
<TabsContent value="dataList" className='w-[500px]'>
<Table className='w-[500px]'>
<TableHeader>
<TableRow>
<TableHead className="w-[150px]">计划</TableHead>
<TableHead className='w-[150px]'>时间</TableHead>
<TableHead className='w-[100px]'>操作</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((item) => (
item.status === 1 ? (
<TableRow key={item.id}>
<TableCell className="font-medium">{item.plan}</TableCell>
<TableCell>{item.date}</TableCell>
<TableCell>
<Button variant="destructive" onClick={() => delBtn(item.id)}>删除</Button>
</TableCell>
</TableRow>
) : null
))}
</TableBody>
</Table>
</TabsContent>
{/* 添加 */}
<TabsContent value="addData">
<InputForm submitData={addBtn} />
</TabsContent>
</Tabs>
</div>
</div>
);
}
export default App;
- BtnIcon.tsx【组件封装,使用】
import React, { useState } from 'react';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
interface BtnIconProps {
title: string;
photo?: string;
selects: string[];
className?: string;
dataClick?: (value: string) => void;
onSelect?: (value: string) => void;
}
export function BtnIcon({ title, photo, selects, className, dataClick, onSelect }: BtnIconProps) {
const handleClick = () => {
if (dataClick) {
dataClick(title);
}
}
const handleSelect = (value: string) => {
if (onSelect) {
onSelect(value);
}
}
const [isHovered, setIsHovered] = useState(false);
return (
<>
<div className={className}>
<div
onClick={handleClick}
className={`border w-[300px] h-[150px] flex-col justify-center rounded-lg ${isHovered ? 'bg-[#0479ffda] text-[#fff]' : 'bg-white'}`}
>
<div className="
flex
justify-center
items-center
w-[300px] h-[100px]
border-b
font-semibold
text-[24px]
hover:bg-[#0479ffda]
hover:rounded-lg
hover:text-[#fff]
transition-all
duration-300
ease-in-out
">
<div className='h-[50px] flex justify-center items-center'>
{photo && <img src={photo} alt="" className='w-[80px] h-[50px]' />}
<div>{title}</div>
</div>
</div>
<Select onValueChange={handleSelect}>
<SelectTrigger
className="w-[300px] h-[50px] bg-transparent border-none outline-none"
style={{ border: 'none', boxShadow: 'none' }}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<SelectValue placeholder="Please Select" />
</SelectTrigger>
<SelectContent>
{selects.map((item, index) => (
<SelectItem key={index} value={item}>{item}</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</>
);
}
- InputForm.tsx
import React from 'react';
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { ListType } from '@/types/ListType';
const FormSchema = z.object({
plan: z.string(),
date: z.string(),
});
interface InputFormProps {
submitData: (data: ListType) => void;
}
export function InputForm({ submitData }: InputFormProps) {
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
plan: "",
date: "",
},
});
const onSubmit = (data: z.infer<typeof FormSchema>) => {
const newData = { id: Date.now(), ...data, status: 1 };
submitData(newData);
form.reset();
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-2/3 space-y-6">
<FormField
control={form.control}
name="plan"
render={({ field }) => (
<FormItem>
<FormLabel>目标</FormLabel>
<FormControl>
<Input placeholder="请输入..." {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="date"
render={({ field }) => (
<FormItem>
<FormLabel>时间</FormLabel>
<FormControl>
<Input placeholder="请输入..." {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">提交</Button>
</form>
</Form>
);
}
-
ListType.tsx
数据类型
export type ListType = {
id: number;
plan: string;
date: string;
status: number;
};
- 结果展示