Menu

定价分组功能使用指南

功能概述

定价分组是 NEXTY.DEV 模板提供的一项灵活的定价管理功能,允许你将定价计划按照自定义分组进行组织和展示。这为前端页面的定价展示提供了极大的灵活性——无论你需要在多个页面、多个组件中以不同方式展示定价信息,都可以轻松实现。

核心优势

  • 灵活分组: 自定义分组标识 (slug),按需组织定价计划
  • 前端自由度高: 可根据分组筛选展示,适配多种页面布局

数据结构

分组表 (pricing_plan_groups)

分组使用 slug 作为主键,简化查询和关联:

lib/db/schema.ts
export const pricingPlanGroups = pgTable('pricing_plan_groups', {
  slug: varchar('slug', { length: 100 }).primaryKey(),
  createdAt: timestamp('created_at', { withTimezone: true })
    .defaultNow()
    .notNull(),
})

定价计划表中的分组字段

每个定价计划通过 groupSlug 字段关联到分组:

lib/db/schema.ts
// pricingPlans 表
groupSlug: varchar('group_slug', { length: 100 })
  .references(() => pricingPlanGroups.slug, { onDelete: 'restrict' })
  .default('default')
  .notNull(),

注意: 分组采用外键约束,删除分组前需先移除该分组下的所有定价计划。

管理后台操作

1. 分组管理入口

在管理员仪表板的定价管理页面 (/dashboard/prices),点击 "Manage Groups" 按钮打开分组管理弹窗。

2. 创建分组

在分组管理弹窗中:

  1. 在输入框中输入分组标识 (slug)
  2. Slug 只能包含小写字母、数字和连字符 (如 annualmonthlyenterprise-plans)
  3. 点击 "+" 按钮或按回车键创建分组
pricing-groups-create

3. 删除分组

在分组管理弹窗中:

  • 每个分组右侧显示该分组下的定价计划数量
  • 只有没有关联定价计划的分组才能删除
  • default 分组是系统保留分组,不可删除

4. 分组列表展示

在定价计划列表页 (/dashboard/prices) 的表格中,Group 列显示每个定价计划所属的分组,方便快速查看和筛选。

为定价计划指定分组

创建定价计划时选择分组

在创建定价计划表单中,Group 字段允许你:

  1. 从下拉列表中选择已有分组
  2. 直接输入新的分组标识并快捷创建

编辑定价计划时修改分组

编辑定价计划时,可以随时更改其所属分组,系统会立即生效。

pricing-groups-edit

前端展示组件

NEXTY.DEV 提供了三个开箱即用的定价展示组件,满足不同场景需求:

1. PricingByGroup (推荐)

根据 groupSlug 分组展示定价卡片,支持 Tab 切换。

这是最灵活的展示方式,适用于大多数 SaaS 产品的定价页面。

import { PricingByGroup } from "@/components/pricing";
 
export default function PricingPage() {
  return <PricingByGroup />;
}

默认行为:

  • 筛选 groupSlugannualmonthlyonetime 的计划
  • 以 Tab 形式切换展示
  • 优先显示年付 (annual) 标签页
  • 自动隐藏无计划的标签页

自定义分组展示:

如果你使用了自定义分组 (如 basicproenterprise),只需修改前端组件逻辑,让前端根据分组筛选定价计划即可:

// 示例:按自定义分组筛选
const basicPlans = allPlans.filter((plan) => plan.groupSlug === "basic");
const proPlans = allPlans.filter((plan) => plan.groupSlug === "pro");
const enterprisePlans = allPlans.filter((plan) => plan.groupSlug === "enterprise");

2. PricingAll

一次性展示所有激活的定价卡片,不进行分组。

适用于定价计划较少、或需要并排展示所有选项的场景。

import { PricingAll } from "@/components/pricing";
 
export default function SimplePricingPage() {
  return <PricingAll />;
}

特点:

  • 展示所有 isActive = true 的定价计划
  • 自动根据计划数量调整网格列数
  • 无 Tab 切换,适合简洁的定价展示

3. PricingByPaymentType

根据支付类型 (月付/年付/一次性) 自动分组展示。

PricingByGroup 类似的 Tab 切换体验,但分组逻辑基于 paymentTyperecurringInterval 字段,而非 groupSlug

import { PricingByPaymentType } from "@/components/pricing";
 
export default function PaymentTypePricingPage() {
  return <PricingByPaymentType />;
}

自动分类逻辑:

  • 月付: paymentType 为 recurring 且 recurringInterval 为 month/every-month
  • 年付: paymentType 为 recurring 且 recurringInterval 为 year/every-year
  • 一次性: paymentType 为 one_time/onetime

组件对比

组件分组依据展示方式推荐场景
PricingByGroupgroupSlug 字段Tab 切换自定义分组、灵活控制展示
PricingAll无分组全部展示计划较少、简洁页面
PricingByPaymentType支付类型字段Tab 切换按支付周期区分展示

相关文件

  • 组件: components/pricing/PricingByGroup.tsx, PricingAll.tsx, PricingByPaymentType.tsx
  • Server Actions: actions/prices/groups.ts, actions/prices/public.ts
  • 数据库 Schema: lib/db/schema.ts
  • 类型定义: types/pricing.ts
  • 状态管理: stores/pricingGroupStore.ts
  • 管理页面: app/[locale]/(protected)/dashboard/(admin)/prices/