Shadcn UI 组件
Shadcn UI 组件是一个基于 Vue 3 的 UI 组件库,提供了丰富的基础组件和高级组件,采用了现代化的设计风格,支持主题定制和响应式设计。
安装
npm install @cimom/vben-core-ui-kit-shadcn-ui
组件列表
Shadcn UI 组件库包含以下组件:
基础组件
- Avatar: 头像组件
- Button: 按钮组件
- Checkbox: 复选框组件
- Icon: 图标组件
- Input: 输入框组件
- InputPassword: 密码输入框组件
- PinInput: PIN 码输入组件
- Select: 选择器组件
- Spinner: 加载中组件
- Tooltip: 文字提示组件
导航组件
- BackTop: 回到顶部组件
- Breadcrumb: 面包屑导航组件
- ContextMenu: 上下文菜单组件
- DropdownMenu: 下拉菜单组件
- HoverCard: 悬停卡片组件
- Popover: 弹出框组件
- Scrollbar: 滚动条组件
- Segmented: 分段控制器组件
其他组件
- CountToAnimator: 数字动画组件
- ExpandableArrow: 可展开箭头组件
- FullScreen: 全屏组件
- Logo: Logo 组件
- RenderContent: 内容渲染组件
- SpineText: 脊柱文本组件
基础组件使用
Button 按钮
<script setup lang="ts">
import { VbenButton } from '@cimom/vben-core-ui-kit-shadcn-ui';
</script>
<template>
<div class="space-x-2">
<VbenButton>默认按钮</VbenButton>
<VbenButton variant="primary">主要按钮</VbenButton>
<VbenButton variant="destructive">危险按钮</VbenButton>
<VbenButton variant="outline">轮廓按钮</VbenButton>
<VbenButton variant="ghost">幽灵按钮</VbenButton>
<VbenButton variant="link">链接按钮</VbenButton>
</div>
<div class="mt-4 space-x-2">
<VbenButton size="sm">小型按钮</VbenButton>
<VbenButton>默认大小</VbenButton>
<VbenButton size="lg">大型按钮</VbenButton>
</div>
<div class="mt-4 space-x-2">
<VbenButton loading>加载中</VbenButton>
<VbenButton disabled>禁用按钮</VbenButton>
<VbenButton icon="Search">带图标</VbenButton>
</div>
</template>
Avatar 头像
<script setup lang="ts">
import { VbenAvatar } from '@cimom/vben-core-ui-kit-shadcn-ui';
</script>
<template>
<div class="flex space-x-4">
<VbenAvatar src="/path/to/avatar.jpg" alt="用户头像" />
<VbenAvatar>JD</VbenAvatar>
<VbenAvatar icon="User" />
</div>
<div class="mt-4 flex space-x-4">
<VbenAvatar size="sm" src="/path/to/avatar.jpg" />
<VbenAvatar src="/path/to/avatar.jpg" />
<VbenAvatar size="lg" src="/path/to/avatar.jpg" />
</div>
</template>
Input 输入框
<script setup lang="ts">
import {
VbenInput,
VbenInputPassword,
} from '@cimom/vben-core-ui-kit-shadcn-ui';
import { ref } from 'vue';
const username = ref('');
const password = ref('');
</script>
<template>
<div class="space-y-4">
<div>
<label class="mb-1 block">用户名</label>
<VbenInput v-model="username" placeholder="请输入用户名" />
</div>
<div>
<label class="mb-1 block">密码</label>
<VbenInputPassword v-model="password" placeholder="请输入密码" />
</div>
</div>
</template>
PinInput 验证码输入
<script setup lang="ts">
import { VbenPinInput } from '@cimom/vben-core-ui-kit-shadcn-ui';
import { ref } from 'vue';
const pinValue = ref('');
</script>
<template>
<div>
<label class="mb-2 block">验证码</label>
<VbenPinInput
v-model="pinValue"
:length="6"
@complete="(value) => console.log('验证码输入完成:', value)"
/>
</div>
</template>
Select 选择器
<script setup lang="ts">
import { VbenSelect } from '@cimom/vben-core-ui-kit-shadcn-ui';
import { ref } from 'vue';
const selectedValue = ref('');
const options = [
{ label: '选项1', value: '1' },
{ label: '选项2', value: '2' },
{ label: '选项3', value: '3' },
];
</script>
<template>
<div>
<label class="mb-1 block">选择项</label>
<VbenSelect
v-model="selectedValue"
:options="options"
placeholder="请选择"
/>
</div>
</template>
导航组件使用
Breadcrumb 面包屑
<script setup lang="ts">
import {
VbenBreadcrumb,
VbenBreadcrumbItem,
} from '@cimom/vben-core-ui-kit-shadcn-ui';
</script>
<template>
<VbenBreadcrumb>
<VbenBreadcrumbItem>首页</VbenBreadcrumbItem>
<VbenBreadcrumbItem>用户管理</VbenBreadcrumbItem>
<VbenBreadcrumbItem>用户详情</VbenBreadcrumbItem>
</VbenBreadcrumb>
</template>
DropdownMenu 下拉菜单
<script setup lang="ts">
import {
VbenDropdownMenu,
VbenDropdownMenuTrigger,
VbenDropdownMenuContent,
VbenDropdownMenuItem,
} from '@cimom/vben-core-ui-kit-shadcn-ui';
</script>
<template>
<VbenDropdownMenu>
<VbenDropdownMenuTrigger>
<button class="rounded border px-4 py-2">点击打开菜单</button>
</VbenDropdownMenuTrigger>
<VbenDropdownMenuContent>
<VbenDropdownMenuItem @select="() => console.log('选项1')">
选项1
</VbenDropdownMenuItem>
<VbenDropdownMenuItem @select="() => console.log('选项2')">
选项2
</VbenDropdownMenuItem>
<VbenDropdownMenuItem @select="() => console.log('选项3')">
选项3
</VbenDropdownMenuItem>
</VbenDropdownMenuContent>
</VbenDropdownMenu>
</template>
Scrollbar 滚动条
<script setup lang="ts">
import { VbenScrollbar } from '@cimom/vben-core-ui-kit-shadcn-ui';
</script>
<template>
<div class="h-60 w-full">
<VbenScrollbar>
<div class="p-4">
<p v-for="i in 20" :key="i" class="mb-4">
这是一段示例文本,用于演示滚动条组件。 {{ i }}
</p>
</div>
</VbenScrollbar>
</div>
</template>
BackTop 回到顶部
<script setup lang="ts">
import { VbenBackTop } from '@cimom/vben-core-ui-kit-shadcn-ui';
</script>
<template>
<div>
<!-- 页面内容 -->
<VbenBackTop :visibilityHeight="100" />
</div>
</template>
其他组件使用
CountToAnimator 数字动画
<script setup lang="ts">
import { VbenCountToAnimator } from '@cimom/vben-core-ui-kit-shadcn-ui';
import { ref } from 'vue';
const value = ref(0);
// 模拟数据变化
setTimeout(() => {
value.value = 1000;
}, 1000);
</script>
<template>
<div>
<h3>数字动画:</h3>
<VbenCountToAnimator
:value="value"
:duration="1000"
:decimals="0"
prefix="¥"
/>
</div>
</template>
FullScreen 全屏
<script setup lang="ts">
import { VbenFullScreen } from '@cimom/vben-core-ui-kit-shadcn-ui';
</script>
<template>
<div>
<VbenFullScreen>
<template #default="{ toggle, isFullscreen }">
<button @click="toggle">
{{ isFullscreen ? '退出全屏' : '进入全屏' }}
</button>
</template>
</VbenFullScreen>
</div>
</template>
Tooltip 文字提示
<script setup lang="ts">
import { VbenTooltip } from '@cimom/vben-core-ui-kit-shadcn-ui';
</script>
<template>
<div class="p-10">
<VbenTooltip content="这是一个提示文本">
<button class="rounded border px-4 py-2">鼠标悬停查看提示</button>
</VbenTooltip>
</div>
</template>
组件属性
Button 属性
| 属性名 | 类型 | 默认值 | 说明 |
|---|
| variant | 'default' \| 'primary' \| 'destructive' \| 'outline' \| 'ghost' \| 'link' | 'default' | 按钮样式变体 |
| size | 'sm' \| 'default' \| 'lg' | 'default' | 按钮大小 |
| loading | boolean | false | 是否显示加载状态 |
| disabled | boolean | false | 是否禁用 |
| icon | string \| Component | - | 按钮图标 |
| iconPlacement | 'left' \| 'right' | 'left' | 图标位置 |
Avatar 属性
| 属性名 | 类型 | 默认值 | 说明 |
|---|
| src | string | - | 头像图片地址 |
| alt | string | - | 头像图片替代文本 |
| size | 'sm' \| 'default' \| 'lg' | 'default' | 头像大小 |
| icon | string \| Component | - | 头像图标 |
| shape | 'circle' \| 'square' | 'circle' | 头像形状 |
Input 属性
| 属性名 | 类型 | 默认值 | 说明 |
|---|
| modelValue | string | '' | 输入框值 |
| placeholder | string | - | 占位文本 |
| disabled | boolean | false | 是否禁用 |
| readonly | boolean | false | 是否只读 |
| clearable | boolean | false | 是否可清空 |
| size | 'sm' \| 'default' \| 'lg' | 'default' | 输入框大小 |
| prefix | string | - | 前缀内容 |
| suffix | string | - | 后缀内容 |
Select 属性
| 属性名 | 类型 | 默认值 | 说明 |
|---|
| modelValue | string \| number \| Array | - | 选择器值 |
| options | Array | [] | 选项数据 |
| placeholder | string | - | 占位文本 |
| disabled | boolean | false | 是否禁用 |
| clearable | boolean | false | 是否可清空 |
| multiple | boolean | false | 是否多选 |
| filterable | boolean | false | 是否可搜索 |
示例
登录表单
<template>
<div class="mx-auto w-96 rounded border p-6 shadow-md">
<h2 class="mb-6 text-center text-2xl font-bold">用户登录</h2>
<form @submit.prevent="handleSubmit">
<div class="mb-4">
<label class="mb-1 block">用户名</label>
<VbenInput
v-model="form.username"
placeholder="请输入用户名"
:disabled="loading"
/>
</div>
<div class="mb-6">
<label class="mb-1 block">密码</label>
<VbenInputPassword
v-model="form.password"
placeholder="请输入密码"
:disabled="loading"
/>
</div>
<div class="mb-6 flex items-center">
<VbenCheckbox v-model="form.remember" :disabled="loading">
记住我
</VbenCheckbox>
<a href="#" class="ml-auto text-sm text-blue-500">忘记密码?</a>
</div>
<VbenButton
variant="primary"
class="w-full"
:loading="loading"
@click="handleSubmit"
>
登录
</VbenButton>
</form>
</div>
</template>
<script setup lang="ts">
import {
VbenButton,
VbenInput,
VbenInputPassword,
VbenCheckbox,
} from '@cimom/vben-core-ui-kit-shadcn-ui';
import { ref } from 'vue';
const form = ref({
username: '',
password: '',
remember: false,
});
const loading = ref(false);
const handleSubmit = async () => {
if (!form.value.username || !form.value.password) {
alert('请输入用户名和密码');
return;
}
loading.value = true;
try {
// 模拟登录请求
await new Promise((resolve) => setTimeout(resolve, 1500));
console.log('登录成功', form.value);
// 登录成功后的处理...
} catch (error) {
console.error('登录失败', error);
} finally {
loading.value = false;
}
};
</script>
数据展示卡片
<template>
<div class="grid grid-cols-1 gap-6 md:grid-cols-3">
<div
v-for="(card, index) in cards"
:key="index"
class="rounded-lg border p-6 shadow-sm"
>
<div class="mb-4 flex items-center justify-between">
<h3 class="text-lg font-medium">{{ card.title }}</h3>
<VbenIcon :name="card.icon" class="text-gray-400" />
</div>
<div class="mb-2">
<VbenCountToAnimator
:value="card.value"
:duration="1500"
:decimals="card.decimals"
:prefix="card.prefix"
class="text-2xl font-bold"
/>
</div>
<div class="flex items-center text-sm">
<span
:class="[card.trend > 0 ? 'text-green-500' : 'text-red-500', 'mr-1']"
>
{{ card.trend > 0 ? '+' : '' }}{{ card.trend }}%
</span>
<span class="text-gray-500">较上周</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {
VbenIcon,
VbenCountToAnimator,
} from '@cimom/vben-core-ui-kit-shadcn-ui';
import { ref } from 'vue';
const cards = ref([
{
title: '总销售额',
value: 126500,
decimals: 2,
prefix: '¥',
trend: 12.5,
icon: 'TrendingUp',
},
{
title: '访问量',
value: 8846,
decimals: 0,
prefix: '',
trend: -2.3,
icon: 'Users',
},
{
title: '订单数',
value: 1293,
decimals: 0,
prefix: '',
trend: 8.7,
icon: 'ShoppingCart',
},
]);
</script>
交互式表格
<template>
<div class="rounded-lg border p-6 shadow-sm">
<div class="mb-4 flex items-center justify-between">
<h2 class="text-xl font-bold">用户列表</h2>
<div class="flex space-x-2">
<VbenInput
v-model="searchQuery"
placeholder="搜索用户"
suffix="Search"
class="w-64"
/>
<VbenButton variant="primary" icon="Plus"> 添加用户 </VbenButton>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="bg-gray-50">
<th class="p-3 text-left">用户名</th>
<th class="p-3 text-left">邮箱</th>
<th class="p-3 text-left">角色</th>
<th class="p-3 text-left">状态</th>
<th class="p-3 text-left">操作</th>
</tr>
</thead>
<tbody>
<tr
v-for="(user, index) in filteredUsers"
:key="index"
class="border-t hover:bg-gray-50"
>
<td class="p-3">
<div class="flex items-center">
<VbenAvatar :src="user.avatar" size="sm" class="mr-2" />
{{ user.username }}
</div>
</td>
<td class="p-3">{{ user.email }}</td>
<td class="p-3">{{ user.role }}</td>
<td class="p-3">
<span
:class="[
user.status === 'active'
? 'bg-green-100 text-green-800'
: 'bg-gray-100 text-gray-800',
'rounded px-2 py-1 text-xs',
]"
>
{{ user.status === 'active' ? '活跃' : '禁用' }}
</span>
</td>
<td class="p-3">
<div class="flex space-x-2">
<VbenTooltip content="编辑">
<button class="text-blue-500 hover:text-blue-700">
<VbenIcon name="Edit" size="sm" />
</button>
</VbenTooltip>
<VbenTooltip content="删除">
<button class="text-red-500 hover:text-red-700">
<VbenIcon name="Trash" size="sm" />
</button>
</VbenTooltip>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script setup lang="ts">
import {
VbenButton,
VbenInput,
VbenAvatar,
VbenIcon,
VbenTooltip,
} from '@cimom/vben-core-ui-kit-shadcn-ui';
import { ref, computed } from 'vue';
const searchQuery = ref('');
const users = ref([
{
username: 'admin',
email: 'admin@example.com',
role: '管理员',
status: 'active',
avatar: 'https://via.placeholder.com/40',
},
{
username: 'user1',
email: 'user1@example.com',
role: '编辑',
status: 'active',
avatar: 'https://via.placeholder.com/40',
},
{
username: 'user2',
email: 'user2@example.com',
role: '访客',
status: 'inactive',
avatar: 'https://via.placeholder.com/40',
},
]);
const filteredUsers = computed(() => {
if (!searchQuery.value) return users.value;
const query = searchQuery.value.toLowerCase();
return users.value.filter(
(user) =>
user.username.toLowerCase().includes(query) ||
user.email.toLowerCase().includes(query) ||
user.role.toLowerCase().includes(query),
);
});
</script>