shadcn-vue 是一个社区驱动的 Vue 版本的 shadcn/ui,使用 Radix Vue 和 Tailwind CSS 开发。它旨在提供可重用的 UI 组件,增强开发体验,但不被正式归类为组件库。此库支持高度定制和便于集成,使开发者能快速访问样式化组件来构建应用程序。不过有一定的学习成本。
支持使用 CLI 自动添加组件,或将代码复制并粘贴到您的项目中并根据您的需求进行自定义。
例如:
shell
npx shadcn-vue@latest add select
npx shadcn-vue@latest add select
UI设计非常简洁、优雅。
配置文件
json
{
"$schema": "https://shadcn-vue.com/schema.json",
"style": "default",
"typescript": true,
"tsConfigPath": "./tsconfig.json",
"tailwind": {
"config": "tailwind.config.js",
"css": "src/assets/index.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": "y" // 如果需要修改,要同步调整tailwind.config.js文件
},
"framework": "vite",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
{
"$schema": "https://shadcn-vue.com/schema.json",
"style": "default",
"typescript": true,
"tsConfigPath": "./tsconfig.json",
"tailwind": {
"config": "tailwind.config.js",
"css": "src/assets/index.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": "y" // 如果需要修改,要同步调整tailwind.config.js文件
},
"framework": "vite",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
radix-icons
radix-icons 提供了丰富的icons
json
"@radix-icons/vue": "^1.0.0",
// 使用
import {DotsHorizontalIcon} from '@radix-icons/vue'
"@radix-icons/vue": "^1.0.0",
// 使用
import {DotsHorizontalIcon} from '@radix-icons/vue'
zod
用于约束数据类型。支持运行时 数据校验。
js
import { z } from 'zod'
// We're keeping a simple non-relational schema here.
// IRL, you will have a schema for your data models.
export const taskSchema = z.object({
income_or_expenses: z.string(),
pay_status: z.string(),
plat_form: z.string()
})
export type Task = z.infer<typeof taskSchema>
// use
const task = computed(() => taskSchema.parse(props.row.original))
import { z } from 'zod'
// We're keeping a simple non-relational schema here.
// IRL, you will have a schema for your data models.
export const taskSchema = z.object({
income_or_expenses: z.string(),
pay_status: z.string(),
plat_form: z.string()
})
export type Task = z.infer<typeof taskSchema>
// use
const task = computed(() => taskSchema.parse(props.row.original))
不恰当的类型使用将抛出错误。
vueuse
Collection of Essential Vue Composition Utilities
useMouse工具集示例:
js
import { useMouse } from '@vueuse/core'
// "x" and "y" are refs
const { x, y } = useMouse()
console.log(x.value)
import { useMouse } from '@vueuse/core'
// "x" and "y" are refs
const { x, y } = useMouse()
console.log(x.value)
再比如:
js
import { useMounted } from '@vueuse/core'
const isMounted = useMounted()
//相当于
const isMounted = ref(false)
onMounted(() => {
isMounted.value = true
})
import { useMounted } from '@vueuse/core'
const isMounted = useMounted()
//相当于
const isMounted = ref(false)
onMounted(() => {
isMounted.value = true
})
ts双向绑定的例子:
js
import { useVModel } from '@vueuse/core'
const emits = defineEmits<{
'update:open': [open: boolean]
}>()
const props = withDefaults(defineProps<{
defaultOpen?: boolean
open?: boolean
class?: HTMLAttributes['class']
}>(), {
defaultOpen: true,
open: undefined,
})
const open = useVModel(props, 'open', emits, {
defaultValue: props.defaultOpen ?? false,
passive: (props.open === undefined) as false,
}) as Ref<boolean>
import { useVModel } from '@vueuse/core'
const emits = defineEmits<{
'update:open': [open: boolean]
}>()
const props = withDefaults(defineProps<{
defaultOpen?: boolean
open?: boolean
class?: HTMLAttributes['class']
}>(), {
defaultOpen: true,
open: undefined,
})
const open = useVModel(props, 'open', emits, {
defaultValue: props.defaultOpen ?? false,
passive: (props.open === undefined) as false,
}) as Ref<boolean>
@tanstack/vue-table
Headless UI for building powerful tables & datagrids
Headless UI是一个术语,指的是为 UI 元素和交互提供逻辑、状态、处理和 API,但不提供markup、styles或pre-built实现的库和实用程序。
因此这个框架中,可以随意结合taiwindcss、shadcn等使用。
核心概念:
- Data 提供数据
- Column Ref 用于配置列及其数据模型、显示模板等的对象
- Table Instance 包含状态和API的核心表对象
- Row Models 如何根据您使用的功能将数据数组转换为有用的行
- Rows 每一行镜像其各自的行数据并提供特定于行的 API
js
<script setup lang="ts">
import {
FlexRender,
getCoreRowModel,
useVueTable,
createColumnHelper,
} from '@tanstack/vue-table'
import {ref} from 'vue'
type Person = {
firstName: string
lastName: string
age: number
}
const defaultData: Person[] = [
{
firstName: 'tandy',
lastName: 'miller',
age: 40,
}
]
const columnHelper = createColumnHelper<Person>()
const columns = [
columnHelper.accessor('firstName', {
cell: info => info.getValue(),
}),
columnHelper.accessor('lastName', {
cell: info => info.getValue(),
}),
columnHelper.accessor('age', {
cell: info => info.getValue(),
}),
]
const data = ref(defaultData)
const table = useVueTable({
get data() {
return data.value
},
columns,
getCoreRowModel: getCoreRowModel(),
})
</script>
<template>
<div class="p-20">
<table>
<tbody>
<tr v-for="row in table.getRowModel().rows" :key="row.id">
<td v-for="cell in row.getVisibleCells()" :key="cell.id">
<FlexRender
:render="cell.column.columnDef.cell"
:props="cell.getContext()"
/>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<style scoped>
table {
border: 1px solid lightgray;
}
td {
border-right: 1px solid lightgray;
}
</style>
<script setup lang="ts">
import {
FlexRender,
getCoreRowModel,
useVueTable,
createColumnHelper,
} from '@tanstack/vue-table'
import {ref} from 'vue'
type Person = {
firstName: string
lastName: string
age: number
}
const defaultData: Person[] = [
{
firstName: 'tandy',
lastName: 'miller',
age: 40,
}
]
const columnHelper = createColumnHelper<Person>()
const columns = [
columnHelper.accessor('firstName', {
cell: info => info.getValue(),
}),
columnHelper.accessor('lastName', {
cell: info => info.getValue(),
}),
columnHelper.accessor('age', {
cell: info => info.getValue(),
}),
]
const data = ref(defaultData)
const table = useVueTable({
get data() {
return data.value
},
columns,
getCoreRowModel: getCoreRowModel(),
})
</script>
<template>
<div class="p-20">
<table>
<tbody>
<tr v-for="row in table.getRowModel().rows" :key="row.id">
<td v-for="cell in row.getVisibleCells()" :key="cell.id">
<FlexRender
:render="cell.column.columnDef.cell"
:props="cell.getContext()"
/>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<style scoped>
table {
border: 1px solid lightgray;
}
td {
border-right: 1px solid lightgray;
}
</style>
效果如图:
官方提供了复杂示例:
https://tanstack.com/table/v8/docs/framework/vue/examples/basic