实现套餐管理页面:三档套餐卡片、权益对比表、定价规则、上架/下架

This commit is contained in:
yml2213
2026-07-14 11:16:19 +08:00
parent 312ae17cb9
commit 6d187d5960
+121 -5
View File
@@ -1,6 +1,122 @@
const Plans = () => (
<div className="h-full flex items-center justify-center text-neutral-400 text-center">
<div><div className="text-lg mb-1"></div><div className="text-sm">...</div></div>
</div>
)
import { Card, Tag, Switch, Button, Table, message } from 'antd'
import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons'
const plans = [
{
key: 'basic', name: '基础版', price: 299, color: '#64748b',
features: { seats: '2 坐席', history: '30 天', knowledge: '50 条', stats: '基础报表', api: false, channels: ['网页'], brand: false, dedicated: false },
tenants: 68, active: true,
},
{
key: 'pro', name: '专业版', price: 699, color: '#2563eb', recommended: true,
features: { seats: '10 坐席', history: '180 天', knowledge: '500 条', stats: '高级报表', api: true, channels: ['网页', '微信', 'APP'], brand: true, dedicated: false },
tenants: 52, active: true,
},
{
key: 'enterprise', name: '企业版', price: 1999, color: '#7c3aed',
features: { seats: '50(可扩展)', history: '永久', knowledge: '不限', stats: '自定义报表', api: true, channels: ['全渠道'], brand: true, dedicated: true },
tenants: 36, active: true,
},
]
const compareData = [
{ label: '月费', basic: '¥299/月', pro: '¥699/月', enterprise: '¥1999/月' },
{ label: '坐席数量', basic: '2', pro: '10', enterprise: '50(可扩展)' },
{ label: '对话记录保存', basic: '30 天', pro: '180 天', enterprise: '永久' },
{ label: '知识库容量', basic: '50 条', pro: '500 条', enterprise: '不限' },
{ label: '数据统计', basic: '基础报表', pro: '高级报表', enterprise: '自定义报表' },
{ label: 'API 接口', basic: '不支持', pro: '基础 API', enterprise: '完整 API' },
{ label: '渠道', basic: '网页', pro: '网页+微信+APP', enterprise: '全渠道' },
{ label: '自定义品牌', basic: '不支持', pro: '支持', enterprise: '支持' },
{ label: '专属客服', basic: '不支持', pro: '不支持', enterprise: '支持' },
]
const Plans = () => {
return (
<div>
<div className="flex items-center justify-between mb-5">
<h2 className="text-lg font-semibold text-neutral-800"></h2>
<Button type="primary" onClick={() => message.success('保存成功')}></Button>
</div>
{/* 套餐卡片 */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mb-8">
{plans.map(plan => (
<Card
key={plan.key}
className={`!rounded-xl ${plan.recommended ? 'ring-2 ring-blue-500 shadow-lg' : ''}`}
bordered={false}
title={
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-base font-semibold" style={{ color: plan.color }}>{plan.name}</span>
{plan.recommended && <Tag color="blue" className="text-xs"></Tag>}
</div>
</div>
}
>
<div className="text-center mb-4">
<span className="text-3xl font-bold text-neutral-800">¥{plan.price}</span>
<span className="text-sm text-neutral-400">/</span>
</div>
<div className="space-y-2.5 mb-4">
{[
{ label: '坐席数量', value: plan.features.seats },
{ label: '对话记录', value: plan.features.history },
{ label: '知识库', value: plan.features.knowledge },
{ label: '统计报表', value: plan.features.stats },
{ label: 'API 接口', value: plan.features.api ? '支持' : '不支持' },
{ label: '渠道', value: plan.features.channels.join('、') },
{ label: '自定义品牌', value: plan.features.brand ? '支持' : '不支持' },
{ label: '专属客服', value: plan.features.dedicated ? '支持' : '不支持' },
].map((item, i) => (
<div key={i} className="flex justify-between text-sm">
<span className="text-neutral-400">{item.label}</span>
<span className="text-neutral-700 font-medium">{item.value}</span>
</div>
))}
</div>
<div className="flex items-center justify-between pt-3 border-t border-neutral-100">
<span className="text-sm text-neutral-400">{plan.tenants} 使</span>
<Switch defaultChecked={plan.active} checkedChildren="上架" unCheckedChildren="下架" />
</div>
</Card>
))}
</div>
{/* 对比表 */}
<Card title="套餐权益对比" className="!rounded-lg" bordered={false}>
<Table
dataSource={compareData}
rowKey="label"
pagination={false}
size="middle"
columns={[
{ title: '权益项', dataIndex: 'label', key: 'label', width: 160, render: (t: string) => <span className="font-medium text-neutral-700">{t}</span> },
{ title: '基础版', dataIndex: 'basic', key: 'basic', render: (t: string) => <RenderCell text={t} /> },
{ title: '专业版', dataIndex: 'pro', key: 'pro', render: (t: string) => <RenderCell text={t} /> },
{ title: '企业版', dataIndex: 'enterprise', key: 'enterprise', render: (t: string) => <RenderCell text={t} /> },
]}
/>
</Card>
{/* 定价规则 */}
<Card title="定价规则" className="!rounded-lg mt-4" bordered={false}>
<ul className="text-sm text-neutral-600 space-y-2 ml-4">
<li> <span className="text-blue-600 font-medium">8 </span> </li>
<li> <span className="text-blue-600 font-medium">¥49//</span> </li>
<li></li>
<li></li>
</ul>
</Card>
</div>
)
}
function RenderCell({ text }: { text: string }) {
if (text === '不支持') return <CloseCircleOutlined className="text-neutral-300" />
if (text === '支持') return <CheckCircleOutlined className="text-green-500" />
return <span className="text-sm text-neutral-600">{text}</span>
}
export default Plans