PricingTable专业版
用法
PricingTable 组件提供了一种响应式且可定制的方式,以表格形式显示定价方案,在桌面端自动切换为水平表格布局以便比较,在移动端则切换为垂直卡片布局以提高可读性。
- 个人版最受欢迎面向独立开发者。$249按年计费/月功能开发者数量1项目GitHub 仓库访问权限更新补丁和次要更新支持社区安全SSO审计日志定制安全审查
- 团队面向成长型团队。$499按年计费/月功能开发者数量5项目GitHub 仓库访问权限更新所有更新支持优先安全SSO审计日志定制安全审查
- 企业版面向大型组织。定制功能开发者数量无限制项目GitHub 仓库访问权限更新所有更新支持24/7安全SSO审计日志定制安全审查
层级
使用 tiers
属性作为对象数组来定义您的定价方案。每个层级对象支持以下属性:
id: string
- 层级的唯一标识符(必填)title?: string
- 定价方案的名称description?: string
- 方案的简短描述price?: string
- 方案的当前价格(例如:“$99”、“€99”、“免费”)discount?: string
- 折扣价格,将以删除线显示price
(例如:“$79”、“€79”)billingCycle?: string
- 显示在价格旁边的单价周期(例如:“/月”、“/席位/月”)billingPeriod?: string
- 显示在计费周期上方的额外计费上下文(例如:“按月计费”)badge?: string | BadgeProps
- 在标题旁边显示徽章{ color: 'primary', variant: 'subtle' }
button?: ButtonProps
- 配置 CTA 按钮{ size: 'lg', block: true }
highlight?: boolean
- 是否将此层级视觉上强调为推荐选项
- 个人版最受欢迎面向独立开发者。$249按年计费/月
- 团队面向成长型团队。$499按年计费/月
- 企业版面向大型组织。定制
<script setup lang="ts">
const tiers = ref([
{
id: 'solo',
title: 'Solo',
description: 'For indie hackers.',
price: '$249',
billingCycle: '/month',
billingPeriod: 'billed annually',
badge: 'Most popular',
button: {
label: 'Buy now',
variant: 'subtle'
}
},
{
id: 'team',
title: 'Team',
description: 'For growing teams.',
price: '$499',
billingCycle: '/month',
billingPeriod: 'billed annually',
button: {
label: 'Buy now'
},
highlight: true
},
{
id: 'enterprise',
title: 'Enterprise',
description: 'For large organizations.',
price: 'Custom',
button: {
label: 'Contact sales',
color: 'neutral'
}
}
])
</script>
<template>
<UPricingTable :tiers="tiers" />
</template>
部分
使用 sections
属性将功能组织成逻辑分组。每个部分代表您希望在不同定价层级之间进行比较的功能类别。
title: string
- 功能部分的标题features: PricingTableSectionFeature[]
- 一个包含功能的数组,以及它们在每个层级中的可用性- 每个功能都需要一个
title
和一个将层级 ID 映射到值的tiers
对象 - 布尔值(
true
/false
)将显示为勾选标记 (✓) 或减号图标 (-) - 字符串值将显示为文本(例如:“无限制”、“最多 5 个用户”)
- 数字值将按原样显示(例如:10、100)
- 每个功能都需要一个
- 个人版面向独立开发者。$249/月功能开发者数量1项目安全SSO
- 团队面向成长型团队。$499/月功能开发者数量5项目安全SSO
- 企业版面向大型组织。定制功能开发者数量无限制项目安全SSO
<script setup lang="ts">
const tiers = ref([
{
id: 'solo',
title: 'Solo',
price: '$249',
description: 'For indie hackers.',
billingCycle: '/month',
button: {
label: 'Buy now',
variant: 'subtle'
}
},
{
id: 'team',
title: 'Team',
price: '$499',
description: 'For growing teams.',
billingCycle: '/month',
button: {
label: 'Buy now'
}
},
{
id: 'enterprise',
title: 'Enterprise',
price: 'Custom',
description: 'For large organizations.',
button: {
label: 'Contact sales',
color: 'neutral'
}
}
])
const sections = ref([
{
title: 'Features',
features: [
{
title: 'Number of developers',
tiers: {
solo: '1',
team: '5',
enterprise: 'Unlimited'
}
},
{
title: 'Projects',
tiers: {
solo: true,
team: true,
enterprise: true
}
}
]
},
{
title: 'Security',
features: [
{
title: 'SSO',
tiers: {
solo: false,
team: true,
enterprise: true
}
}
]
}
])
</script>
<template>
<UPricingTable :tiers="tiers" :sections="sections" />
</template>
示例
使用插槽
PricingTable 组件提供了强大的插槽自定义选项,以根据您的内容定制显示。您可以使用通用插槽自定义单个元素,或使用它们的 ID 定位特定项目。
<script setup lang="ts">
const tiers = [
{
id: 'solo',
title: 'Solo',
price: '$249',
description: 'For indie hackers.',
billingCycle: '/month',
button: { label: 'Buy now', variant: 'subtle' as const }
},
{
id: 'team',
title: 'Team',
price: '$499',
description: 'For growing teams.',
billingCycle: '/month',
button: { label: 'Buy now' },
highlight: true
},
{
id: 'enterprise',
title: 'Enterprise',
price: 'Custom',
description: 'For large organizations.',
button: { label: 'Contact sales', color: 'neutral' as const }
}
]
const sections = [
{
id: 'features',
title: 'Features',
features: [
{
id: 'developers',
title: 'Number of developers',
tiers: { solo: '1', team: '5', enterprise: 'Unlimited' }
},
{
id: 'projects',
title: 'Projects',
tiers: { solo: true, team: true, enterprise: true }
}
]
},
{
id: 'security',
title: 'Security',
features: [
{
title: 'SSO',
tiers: { solo: false, team: true, enterprise: true }
}
]
}
]
</script>
<template>
<UPricingTable :tiers="tiers" :sections="sections">
<!-- Customize specific tier title -->
<template #team-title="{ tier }">
<div class="flex items-center gap-2">
<UIcon name="i-lucide-crown" class="size-4 text-amber-500" />
{{ tier.title }}
</div>
</template>
<!-- Customize specific section title -->
<template #section-security-title="{ section }">
<div class="flex items-center gap-2">
<UIcon name="i-lucide-shield-check" class="size-4 text-green-500" />
<span class="font-semibold text-green-700">{{ section.title }}</span>
</div>
</template>
<!-- Customize specific feature value -->
<template #feature-developers-value="{ feature, tier }">
<template v-if="feature.tiers?.[tier.id]">
<UBadge :label="String(feature.tiers[tier.id])" color="primary" variant="soft" />
</template>
<UIcon v-else name="i-lucide-x" class="size-4 text-muted" />
</template>
</UPricingTable>
</template>
该组件支持多种插槽类型,以实现最大的自定义灵活性:
插槽类型 | 模式 | 描述 | 示例 |
---|---|---|---|
层级插槽 | #{tier-id}-{element} | 定位特定层级 | #team-title , #solo-price |
部分插槽 | #section-{id|formatted-title}-title | 定位特定部分 | #section-features-title |
功能插槽 | #feature-{id|formatted-title}-{title|value} | 定位特定功能 | #feature-developers-title |
通用插槽 | #tier-title , #section-title 等 | 适用于所有项目 | #feature-value |
id
时,插槽名称将从标题自动生成(例如,“Premium Features!” 变为 #section-premium-features-title
)。API
属性
属性 | 默认值 | 类型 |
---|---|---|
as |
|
此组件应渲染为的元素或组件。 |
tiers |
在表格中显示的定价层级。每个层级代表一个定价方案,包含其标题、描述、价格和功能。
| |
sections |
在表格中显示的功能部分。每个部分包含一个标题和功能列表,以及它们在每个层级中的可用性。 | |
caption |
显示在表格上方的标题。 | |
ui |
|
插槽
插槽 | 类型 |
---|---|
caption |
|
tier |
|
tier-title |
|
tier-description |
|
tier-badge |
|
tier-button |
|
tier-billing |
|
tier-discount |
|
tier-price |
|
section-title |
|
feature-title |
|
feature-value |
|
主题
export default defineAppConfig({
uiPro: {
pricingTable: {
slots: {
root: 'w-full relative',
table: 'w-full table-fixed border-separate border-spacing-x-0 hidden md:table',
list: 'md:hidden flex flex-col gap-6 w-full',
item: 'p-6 flex flex-col border border-default rounded-lg',
caption: 'sr-only',
thead: '',
tbody: '',
tr: '',
th: 'py-4 font-normal text-left border-b border-default',
td: 'px-6 py-4 text-center border-b border-default',
tier: 'p-6 text-left font-normal',
tierTitleWrapper: 'flex items-center gap-3',
tierTitle: 'text-lg font-semibold text-highlighted',
tierDescription: 'text-sm font-normal text-muted mt-1',
tierBadge: 'truncate',
tierPriceWrapper: 'flex items-center gap-1 mt-4',
tierPrice: 'text-highlighted text-3xl sm:text-4xl font-semibold',
tierDiscount: 'text-muted line-through text-xl sm:text-2xl',
tierBilling: 'flex flex-col justify-between min-w-0',
tierBillingPeriod: 'text-toned truncate text-xs font-medium',
tierBillingCycle: 'text-muted truncate text-xs font-medium',
tierButton: 'mt-6',
tierFeatureIcon: 'size-5 shrink-0',
section: 'mt-6 flex flex-col gap-2',
sectionTitle: 'font-semibold text-sm text-highlighted',
feature: 'flex items-center justify-between gap-1',
featureTitle: 'text-sm text-default',
featureValue: 'text-sm text-muted flex justify-center min-w-5'
},
variants: {
section: {
true: {
tr: '*:pt-8'
}
},
active: {
true: {
tierFeatureIcon: 'text-primary'
}
},
highlight: {
true: {
tier: 'bg-elevated/50 border-x border-t border-default rounded-t-lg',
td: 'bg-elevated/50 border-x border-default',
item: 'bg-elevated/50'
}
}
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
uiPro: {
pricingTable: {
slots: {
root: 'w-full relative',
table: 'w-full table-fixed border-separate border-spacing-x-0 hidden md:table',
list: 'md:hidden flex flex-col gap-6 w-full',
item: 'p-6 flex flex-col border border-default rounded-lg',
caption: 'sr-only',
thead: '',
tbody: '',
tr: '',
th: 'py-4 font-normal text-left border-b border-default',
td: 'px-6 py-4 text-center border-b border-default',
tier: 'p-6 text-left font-normal',
tierTitleWrapper: 'flex items-center gap-3',
tierTitle: 'text-lg font-semibold text-highlighted',
tierDescription: 'text-sm font-normal text-muted mt-1',
tierBadge: 'truncate',
tierPriceWrapper: 'flex items-center gap-1 mt-4',
tierPrice: 'text-highlighted text-3xl sm:text-4xl font-semibold',
tierDiscount: 'text-muted line-through text-xl sm:text-2xl',
tierBilling: 'flex flex-col justify-between min-w-0',
tierBillingPeriod: 'text-toned truncate text-xs font-medium',
tierBillingCycle: 'text-muted truncate text-xs font-medium',
tierButton: 'mt-6',
tierFeatureIcon: 'size-5 shrink-0',
section: 'mt-6 flex flex-col gap-2',
sectionTitle: 'font-semibold text-sm text-highlighted',
feature: 'flex items-center justify-between gap-1',
featureTitle: 'text-sm text-default',
featureValue: 'text-sm text-muted flex justify-center min-w-5'
},
variants: {
section: {
true: {
tr: '*:pt-8'
}
},
active: {
true: {
tierFeatureIcon: 'text-primary'
}
},
highlight: {
true: {
tier: 'bg-elevated/50 border-x border-t border-default rounded-t-lg',
td: 'bg-elevated/50 border-x border-default',
item: 'bg-elevated/50'
}
}
}
}
}
})
]
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'
export default defineConfig({
plugins: [
vue(),
uiPro({
uiPro: {
pricingTable: {
slots: {
root: 'w-full relative',
table: 'w-full table-fixed border-separate border-spacing-x-0 hidden md:table',
list: 'md:hidden flex flex-col gap-6 w-full',
item: 'p-6 flex flex-col border border-default rounded-lg',
caption: 'sr-only',
thead: '',
tbody: '',
tr: '',
th: 'py-4 font-normal text-left border-b border-default',
td: 'px-6 py-4 text-center border-b border-default',
tier: 'p-6 text-left font-normal',
tierTitleWrapper: 'flex items-center gap-3',
tierTitle: 'text-lg font-semibold text-highlighted',
tierDescription: 'text-sm font-normal text-muted mt-1',
tierBadge: 'truncate',
tierPriceWrapper: 'flex items-center gap-1 mt-4',
tierPrice: 'text-highlighted text-3xl sm:text-4xl font-semibold',
tierDiscount: 'text-muted line-through text-xl sm:text-2xl',
tierBilling: 'flex flex-col justify-between min-w-0',
tierBillingPeriod: 'text-toned truncate text-xs font-medium',
tierBillingCycle: 'text-muted truncate text-xs font-medium',
tierButton: 'mt-6',
tierFeatureIcon: 'size-5 shrink-0',
section: 'mt-6 flex flex-col gap-2',
sectionTitle: 'font-semibold text-sm text-highlighted',
feature: 'flex items-center justify-between gap-1',
featureTitle: 'text-sm text-default',
featureValue: 'text-sm text-muted flex justify-center min-w-5'
},
variants: {
section: {
true: {
tr: '*:pt-8'
}
},
active: {
true: {
tierFeatureIcon: 'text-primary'
}
},
highlight: {
true: {
tier: 'bg-elevated/50 border-x border-t border-default rounded-t-lg',
td: 'bg-elevated/50 border-x border-default',
item: 'bg-elevated/50'
}
}
}
}
}
})
]
})