Kaynağa Gözat

✨ feat(ui): 新增收费标准和个人中心功能

- 新增收费标准页面,展示基础版/标准版/专业版套餐
- 新增个人中心页面,支持用户信息编辑和账户状态查看
- 新增充值中心页面,支持多种套餐选择和支付宝/微信支付
- 新增支付记录实体和相关字段,完善用户会员体系

📝 docs(homepage): 更新首页文案和导航功能

- 更新首页导航栏,新增收费标准入口
- 优化"免费开始使用"按钮文案,明确5份以内3次免费体验
yourname 3 ay önce
ebeveyn
işleme
adf31d7875

+ 41 - 0
src/client/home-shadcn/components/ui/radio-group.tsx

@@ -0,0 +1,41 @@
+import * as React from "react"
+import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
+import { Circle } from "lucide-react"
+import { cn } from "@/client/lib/utils"
+
+const RadioGroup = React.forwardRef<
+  React.ElementRef<typeof RadioGroupPrimitive.Root>,
+  React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
+>(({ className, ...props }, ref) => {
+  return (
+    <RadioGroupPrimitive.Root
+      className={cn("grid gap-2", className)}
+      {...props}
+      ref={ref}
+    />
+  )
+})
+RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
+
+const RadioGroupItem = React.forwardRef<
+  React.ElementRef<typeof RadioGroupPrimitive.Item>,
+  React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
+>(({ className, ...props }, ref) => {
+  return (
+    <RadioGroupPrimitive.Item
+      ref={ref}
+      className={cn(
+        "aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
+        className
+      )}
+      {...props}
+    >
+      <RadioGroupPrimitive.Indicator className="flex items-center justify-center">
+        <Circle className="h-2.5 w-2.5 fill-current text-current" />
+      </RadioGroupPrimitive.Indicator>
+    </RadioGroupPrimitive.Item>
+  )
+})
+RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
+
+export { RadioGroup, RadioGroupItem }

+ 40 - 2
src/client/home-shadcn/pages/HomePage.tsx

@@ -57,11 +57,49 @@ export default function HomePage() {
 
   return (
     <div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-blue-50">
+      {/* Header Navigation */}
+      <header className="fixed top-0 left-0 right-0 z-50 bg-white/90 backdrop-blur-sm border-b">
+        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+          <div className="flex justify-between items-center h-16">
+            <div className="flex items-center">
+              <h1 className="text-xl font-bold text-gray-900">元亨Word</h1>
+            </div>
+            
+            <nav className="hidden md:flex items-center space-x-8">
+              <button
+                onClick={() => navigate('/templates')}
+                className="text-gray-700 hover:text-blue-600 transition-colors font-medium"
+              >
+                模板广场
+              </button>
+              <button
+                onClick={() => navigate('/pricing')}
+                className="text-gray-700 hover:text-blue-600 transition-colors font-medium"
+              >
+                收费标准
+              </button>
+              <button
+                onClick={() => navigate('/login')}
+                className="text-gray-700 hover:text-blue-600 transition-colors font-medium"
+              >
+                登录
+              </button>
+              <button
+                onClick={() => navigate('/register')}
+                className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors font-medium"
+              >
+                注册
+              </button>
+            </nav>
+          </div>
+        </div>
+      </header>
+
       {/* Hero Section */}
       <div className="relative overflow-hidden">
         <div className="absolute inset-0 bg-gradient-to-r from-blue-600/5 to-purple-600/5" />
         
-        <div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-20 pb-16">
+        <div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-32 pb-16">
           <div className="text-center">
             <div className="inline-flex items-center px-4 py-2 rounded-full bg-blue-100 text-blue-800 text-sm font-medium mb-6">
               <Zap className="h-4 w-4 mr-2" />
@@ -286,7 +324,7 @@ export default function HomePage() {
             onClick={() => navigate('/word-preview')}
           >
             <Shield className="h-5 w-5 mr-2" />
-            免费开始使用
+            免费开始使用(5份以内3次免费体验)
           </Button>
         </div>
       </div>

+ 229 - 0
src/client/home-shadcn/pages/PricingPage.tsx

@@ -0,0 +1,229 @@
+import { useState } from 'react';
+import { Button } from '@/client/components/ui/button';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card';
+import { Badge } from '@/client/components/ui/badge';
+import { Check, Star } from 'lucide-react';
+import { useNavigate } from 'react-router-dom';
+
+export default function PricingPage() {
+  const navigate = useNavigate();
+  const [selectedPlan, setSelectedPlan] = useState<string>('standard');
+
+  const plans = [
+    {
+      id: 'basic',
+      name: '基础版',
+      price: '9.9',
+      period: '/月',
+      description: '适合个人用户和小型团队',
+      features: [
+        '每月100份文档处理',
+        '基础模板支持',
+        '标准图片压缩',
+        '邮件技术支持',
+        '5天数据保留'
+      ],
+      popular: false,
+      color: 'border-gray-200'
+    },
+    {
+      id: 'standard',
+      name: '标准版',
+      price: '29.9',
+      period: '/月',
+      description: '适合中小企业和团队使用',
+      features: [
+        '每月500份文档处理',
+        '高级模板库',
+        '智能图片处理',
+        '批量压缩打包',
+        '优先技术支持',
+        '30天数据保留',
+        'API接口调用'
+      ],
+      popular: true,
+      color: 'border-blue-500'
+    },
+    {
+      id: 'professional',
+      name: '专业版',
+      price: '99.9',
+      period: '/月',
+      description: '适合大型企业和专业用户',
+      features: [
+        '无限文档处理',
+        '全部模板库',
+        'AI智能优化',
+        '自定义模板制作',
+        '专属技术支持',
+        '永久数据保留',
+        'API无限制调用',
+        '团队协作功能',
+        '数据统计分析'
+      ],
+      popular: false,
+      color: 'border-purple-500'
+    }
+  ];
+
+  return (
+    <div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-blue-50">
+      {/* Header */}
+      <header className="fixed top-0 left-0 right-0 z-50 bg-white/90 backdrop-blur-sm border-b">
+        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+          <div className="flex justify-between items-center h-16">
+            <div className="flex items-center">
+              <h1 className="text-xl font-bold text-gray-900">元亨Word - 收费标准</h1>
+            </div>
+            
+            <nav className="hidden md:flex items-center space-x-8">
+              <button 
+                onClick={() => navigate('/')}
+                className="text-gray-700 hover:text-blue-600 transition-colors font-medium"
+              >
+                首页
+              </button>
+              <button 
+                onClick={() => navigate('/templates')}
+                className="text-gray-700 hover:text-blue-600 transition-colors font-medium"
+              >
+                模板广场
+              </button>
+              <button 
+                onClick={() => navigate('/login')}
+                className="text-gray-700 hover:text-blue-600 transition-colors font-medium"
+              >
+                登录
+              </button>
+              <button 
+                onClick={() => navigate('/register')}
+                className="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors font-medium"
+              >
+                注册
+              </button>
+            </nav>
+          </div>
+        </div>
+      </header>
+
+      {/* Hero Section */}
+      <div className="pt-32 pb-16">
+        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+          <div className="text-center">
+            <h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-6">
+              选择适合您的套餐
+            </h1>
+            <p className="text-xl text-gray-600 max-w-2xl mx-auto mb-8">
+              灵活的价格方案,满足不同用户的需求。从个人用户到企业团队,总有一款适合您。
+            </p>
+            
+            <div className="flex justify-center items-center space-x-4">
+              <Badge variant="outline" className="text-lg px-4 py-2">
+                新用户专享:首月立减50%
+              </Badge>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      {/* Pricing Cards */}
+      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-20">
+        <div className="grid md:grid-cols-3 gap-8">
+          {plans.map((plan) => (
+            <Card 
+              key={plan.id} 
+              className={`relative ${plan.color} ${plan.popular ? 'scale-105' : ''} transition-transform duration-300`}
+            >
+              {plan.popular && (
+                <Badge className="absolute -top-3 left-1/2 transform -translate-x-1/2 bg-blue-500 text-white">
+                  <Star className="h-4 w-4 mr-1" />
+                  推荐
+                </Badge>
+              )}
+              
+              <CardHeader>
+                <CardTitle className="text-2xl">{plan.name}</CardTitle>
+                <CardDescription>{plan.description}</CardDescription>
+                <div className="mt-4">
+                  <span className="text-4xl font-bold text-gray-900">¥{plan.price}</span>
+                  <span className="text-gray-600">{plan.period}</span>
+                </div>
+              </CardHeader>
+              
+              <CardContent>
+                <ul className="space-y-3">
+                  {plan.features.map((feature, index) => (
+                    <li key={index} className="flex items-center">
+                      <Check className="h-5 w-5 text-green-500 mr-2" />
+                      <span className="text-gray-700">{feature}</span>
+                    </li>
+                  ))}
+                </ul>
+                
+                <Button 
+                  className={`w-full mt-6 ${plan.popular ? 'bg-blue-600 hover:bg-blue-700' : ''}`}
+                  onClick={() => navigate('/register')}
+                >
+                  立即选择
+                </Button>
+              </CardContent>
+            </Card>
+          ))}
+        </div>
+      </div>
+
+      {/* FAQ Section */}
+      <div className="bg-gray-50 py-20">
+        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+          <div className="text-center mb-16">
+            <h2 className="text-3xl font-bold text-gray-900 mb-4">常见问题</h2>
+            <p className="text-gray-600">关于收费标准的详细说明</p>
+          </div>
+          
+          <div className="max-w-3xl mx-auto space-y-6">
+            <div className="bg-white p-6 rounded-lg shadow-sm">
+              <h3 className="font-semibold text-lg mb-2">如何计费?</h3>
+              <p className="text-gray-600">按月订阅,从激活当天开始计算,到期自动续费。</p>
+            </div>
+            
+            <div className="bg-white p-6 rounded-lg shadow-sm">
+              <h3 className="font-semibold text-lg mb-2">可以升级套餐吗?</h3>
+              <p className="text-gray-600">随时可以升级套餐,差价按剩余天数折算。</p>
+            </div>
+            
+            <div className="bg-white p-6 rounded-lg shadow-sm">
+              <h3 className="font-semibold text-lg mb-2">支持退款吗?</h3>
+              <p className="text-gray-600">7天内不满意可全额退款,超过7天按剩余天数折算。</p>
+            </div>
+            
+            <div className="bg-white p-6 rounded-lg shadow-sm">
+              <h3 className="font-semibold text-lg mb-2">有免费试用吗?</h3>
+              <p className="text-gray-600">新用户注册后可免费处理5份文档,体验全部功能。</p>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      {/* Footer */}
+      <footer className="bg-gray-900 text-white py-12">
+        <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
+          <div className="text-center">
+            <h3 className="text-2xl font-bold mb-4">元亨Word批量处理工具</h3>
+            <p className="text-gray-400 mb-6">
+              让每一份文档都充满个性,让批量处理变得简单
+            </p>
+            <div className="flex justify-center space-x-6">
+              <Button 
+                variant="ghost" 
+                className="text-white hover:text-blue-400"
+                onClick={() => navigate('/')}
+              >
+                返回首页
+              </Button>
+            </div>
+          </div>
+        </div>
+      </footer>
+    </div>
+  );
+}

+ 239 - 0
src/client/home-shadcn/pages/ProfilePage.tsx

@@ -0,0 +1,239 @@
+import { useState, useEffect } from 'react';
+import { Button } from '@/client/components/ui/button';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card';
+import { Input } from '@/client/components/ui/input';
+import { Label } from '@/client/components/ui/label';
+import { Badge } from '@/client/components/ui/badge';
+import { useUser } from '../hooks/AuthProvider';
+import { User, Mail, Phone, Calendar, Shield } from 'lucide-react';
+import { format } from 'date-fns';
+
+export default function ProfilePage() {
+  const { user, updateUser } = useUser();
+  const [isEditing, setIsEditing] = useState(false);
+  const [formData, setFormData] = useState({
+    username: '',
+    email: '',
+    phone: '',
+    avatar: ''
+  });
+
+  useEffect(() => {
+    if (user) {
+      setFormData({
+        username: user.username || '',
+        email: user.email || '',
+        phone: user.phone || '',
+        avatar: user.avatar || ''
+      });
+    }
+  }, [user]);
+
+  const handleSave = async () => {
+    try {
+      await updateUser(formData);
+      setIsEditing(false);
+    } catch (error) {
+      console.error('更新用户信息失败:', error);
+    }
+  };
+
+  const handleCancel = () => {
+    setFormData({
+      username: user?.username || '',
+      email: user?.email || '',
+      phone: user?.phone || '',
+      avatar: user?.avatar || ''
+    });
+    setIsEditing(false);
+  };
+
+  if (!user) {
+    return (
+      <div className="min-h-screen flex items-center justify-center">
+        <Card>
+          <CardContent className="pt-6">
+            <p>请先登录</p>
+          </CardContent>
+        </Card>
+      </div>
+    );
+  }
+
+  return (
+    <div className="max-w-4xl mx-auto p-6">
+      <div className="mb-6">
+        <h1 className="text-3xl font-bold text-gray-900 mb-2">个人中心</h1>
+        <p className="text-gray-600">管理您的个人信息和账户设置</p>
+      </div>
+
+      <div className="grid gap-6 md:grid-cols-2">
+        {/* 基本信息 */}
+        <Card>
+          <CardHeader>
+            <CardTitle className="flex items-center gap-2">
+              <User className="h-5 w-5" />
+              基本信息
+            </CardTitle>
+            <CardDescription>您的账户基本信息</CardDescription>
+          </CardHeader>
+          <CardContent className="space-y-4">
+            <div className="flex items-center space-x-4">
+              <div className="w-20 h-20 bg-gray-200 rounded-full flex items-center justify-center">
+                <User className="h-10 w-10 text-gray-400" />
+              </div>
+              <div>
+                <h3 className="font-semibold">{user.username}</h3>
+                <Badge variant="outline">{user.userType === 'premium' ? '高级会员' : '普通会员'}</Badge>
+              </div>
+            </div>
+
+            <div className="space-y-3">
+              <div>
+                <Label>用户ID</Label>
+                <p className="text-sm text-gray-600">{user.id}</p>
+              </div>
+              <div>
+                <Label>注册时间</Label>
+                <p className="text-sm text-gray-600 flex items-center gap-1">
+                  <Calendar className="h-4 w-4" />
+                  {user.createdAt && format(new Date(user.createdAt), 'yyyy-MM-dd HH:mm')}
+                </p>
+              </div>
+            </div>
+          </CardContent>
+        </Card>
+
+        {/* 联系信息 */}
+        <Card>
+          <CardHeader>
+            <CardTitle>联系信息</CardTitle>
+            <CardDescription>更新您的联系方式</CardDescription>
+          </CardHeader>
+          <CardContent className="space-y-4">
+            {isEditing ? (
+              <>
+                <div>
+                  <Label htmlFor="username">用户名</Label>
+                  <Input
+                    id="username"
+                    value={formData.username}
+                    onChange={(e) => setFormData({ ...formData, username: e.target.value })}
+                  />
+                </div>
+                <div>
+                  <Label htmlFor="email">邮箱</Label>
+                  <Input
+                    id="email"
+                    type="email"
+                    value={formData.email}
+                    onChange={(e) => setFormData({ ...formData, email: e.target.value })}
+                  />
+                </div>
+                <div>
+                  <Label htmlFor="phone">手机号</Label>
+                  <Input
+                    id="phone"
+                    type="tel"
+                    value={formData.phone}
+                    onChange={(e) => setFormData({ ...formData, phone: e.target.value })}
+                  />
+                </div>
+                <div className="flex gap-2">
+                  <Button onClick={handleSave}>保存</Button>
+                  <Button variant="outline" onClick={handleCancel}>取消</Button>
+                </div>
+              </>
+            ) : (
+              <>
+                <div>
+                  <Label>用户名</Label>
+                  <p className="text-sm text-gray-600">{user.username}</p>
+                </div>
+                <div>
+                  <Label>邮箱</Label>
+                  <p className="text-sm text-gray-600 flex items-center gap-1">
+                    <Mail className="h-4 w-4" />
+                    {user.email || '未设置'}
+                  </p>
+                </div>
+                <div>
+                  <Label>手机号</Label>
+                  <p className="text-sm text-gray-600 flex items-center gap-1">
+                    <Phone className="h-4 w-4" />
+                    {user.phone || '未设置'}
+                  </p>
+                </div>
+                <Button onClick={() => setIsEditing(true)}>编辑信息</Button>
+              </>
+            )}
+          </CardContent>
+        </Card>
+
+        {/* 账户状态 */}
+        <Card>
+          <CardHeader>
+            <CardTitle className="flex items-center gap-2">
+              <Shield className="h-5 w-5" />
+              账户状态
+            </CardTitle>
+            <CardDescription>您的会员状态和余额信息</CardDescription>
+          </CardHeader>
+          <CardContent>
+            <div className="space-y-4">
+              <div>
+                <Label>会员等级</Label>
+                <div className="flex items-center gap-2">
+                  <Badge className={user.userType === 'premium' ? 'bg-purple-500' : 'bg-gray-500'}>
+                    {user.userType === 'premium' ? '高级会员' : '普通会员'}
+                  </Badge>
+                  {user.userType !== 'premium' && (
+                    <Button size="sm" onClick={() => navigate('/member/recharge')}>
+                      升级会员
+                    </Button>
+                  )}
+                </div>
+              </div>
+              <div>
+                <Label>剩余文档处理次数</Label>
+                <p className="text-2xl font-bold text-blue-600">{user.remainingCount || 0}</p>
+              </div>
+              <div>
+                <Label>有效期至</Label>
+                <p className="text-sm text-gray-600">
+                  {user.expireAt ? format(new Date(user.expireAt), 'yyyy-MM-dd') : '长期有效'}
+                </p>
+              </div>
+            </div>
+          </CardContent>
+        </Card>
+
+        {/* 使用统计 */}
+        <Card>
+          <CardHeader>
+            <CardTitle>使用统计</CardTitle>
+            <CardDescription>您的使用记录</CardDescription>
+          </CardHeader>
+          <CardContent>
+            <div className="space-y-3">
+              <div className="flex justify-between">
+                <span>本月处理文档</span>
+                <span className="font-semibold">{user.monthlyUsage || 0} 份</span>
+              </div>
+              <div className="flex justify-between">
+                <span>总处理文档</span>
+                <span className="font-semibold">{user.totalUsage || 0} 份</span>
+              </div>
+              <div className="flex justify-between">
+                <span>最后使用时间</span>
+                <span className="text-sm text-gray-600">
+                  {user.lastUsedAt ? format(new Date(user.lastUsedAt), 'MM-dd HH:mm') : '从未使用'}
+                </span>
+              </div>
+            </div>
+          </CardContent>
+        </Card>
+      </div>
+    </div>
+  );
+}

+ 252 - 0
src/client/home-shadcn/pages/RechargePage.tsx

@@ -0,0 +1,252 @@
+import { useState } from 'react';
+import { Button } from '@/client/components/ui/button';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card';
+import { Badge } from '@/client/components/ui/badge';
+import { Input } from '@/client/components/ui/input';
+import { Label } from '@/client/components/ui/label';
+import { RadioGroup, RadioGroupItem } from '@/client/components/ui/radio-group';
+import { Check, CreditCard, Wallet } from 'lucide-react';
+import { useUser } from '../hooks/AuthProvider';
+import { useNavigate } from 'react-router-dom';
+
+interface RechargePlan {
+  id: string;
+  name: string;
+  price: number;
+  count: number;
+  description: string;
+  popular?: boolean;
+}
+
+export default function RechargePage() {
+  const { user } = useUser();
+  const navigate = useNavigate();
+  const [selectedPlan, setSelectedPlan] = useState<string>('standard');
+  const [paymentMethod, setPaymentMethod] = useState<string>('alipay');
+
+  const rechargePlans: RechargePlan[] = [
+    {
+      id: 'basic',
+      name: '基础套餐',
+      price: 29.9,
+      count: 100,
+      description: '每份0.30元,适合轻度使用'
+    },
+    {
+      id: 'standard',
+      name: '标准套餐',
+      price: 99.9,
+      count: 500,
+      description: '每份0.20元,性价比之选',
+      popular: true
+    },
+    {
+      id: 'professional',
+      name: '专业套餐',
+      price: 299.9,
+      count: 2000,
+      description: '每份0.15元,大量处理首选'
+    },
+    {
+      id: 'enterprise',
+      name: '企业套餐',
+      price: 999.9,
+      count: 10000,
+      description: '每份0.10元,企业级服务'
+    }
+  ];
+
+  const selectedPlanData = rechargePlans.find(plan => plan.id === selectedPlan);
+
+  const handlePayment = () => {
+    // 这里应该调用支付接口
+    alert('支付功能开发中,请选择套餐后联系客服完成支付');
+  };
+
+  if (!user) {
+    return (
+      <div className="min-h-screen flex items-center justify-center">
+        <Card>
+          <CardContent className="pt-6">
+            <p>请先登录</p>
+          </CardContent>
+        </Card>
+      </div>
+    );
+  }
+
+  return (
+    <div className="max-w-4xl mx-auto p-6">
+      <div className="mb-6">
+        <h1 className="text-3xl font-bold text-gray-900 mb-2">充值中心</h1>
+        <p className="text-gray-600">选择适合您的充值套餐,立即开始高效文档处理</p>
+      </div>
+
+      <div className="grid gap-6">
+        {/* 当前账户状态 */}
+        <Card>
+          <CardHeader>
+            <CardTitle>账户状态</CardTitle>
+            <CardDescription>您的当前账户信息</CardDescription>
+          </CardHeader>
+          <CardContent className="space-y-4">
+            <div className="grid grid-cols-2 gap-4">
+              <div>
+                <Label>当前余额</Label>
+                <p className="text-2xl font-bold text-blue-600">{user.remainingCount || 0} 份</p>
+              </div>
+              <div>
+                <Label>会员等级</Label>
+                <p className="text-lg font-semibold">
+                  {user.userType === 'premium' ? '高级会员' : '普通会员'}
+                </p>
+              </div>
+            </div>
+          </CardContent>
+        </Card>
+
+        {/* 充值套餐选择 */}
+        <Card>
+          <CardHeader>
+            <CardTitle>选择套餐</CardTitle>
+            <CardDescription>选择适合您的文档处理套餐</CardDescription>
+          </CardHeader>
+          <CardContent>
+            <RadioGroup value={selectedPlan} onValueChange={setSelectedPlan} className="grid gap-4">
+              {rechargePlans.map((plan) => (
+                <Card 
+                  key={plan.id} 
+                  className={`cursor-pointer transition-all ${selectedPlan === plan.id ? 'border-blue-500' : 'border-gray-200'}`}
+                >
+                  <CardContent className="p-4">
+                    <RadioGroupItem value={plan.id} id={plan.id} className="sr-only" />
+                    <div className="flex items-center justify-between">
+                      <div className="flex items-center space-x-3">
+                        <RadioGroupItem value={plan.id} id={plan.id} />
+                        <div>
+                          <div className="flex items-center space-x-2">
+                            <h3 className="font-semibold">{plan.name}</h3>
+                            {plan.popular && (
+                              <Badge className="bg-blue-500">推荐</Badge>
+                            )}
+                          </div>
+                          <p className="text-sm text-gray-600">{plan.description}</p>
+                        </div>
+                      </div>
+                      <div className="text-right">
+                        <p className="text-2xl font-bold text-gray-900">¥{plan.price}</p>
+                        <p className="text-sm text-gray-600">{plan.count}份</p>
+                      </div>
+                    </div>
+                  </CardContent>
+                </Card>
+              ))}
+            </RadioGroup>
+          </CardContent>
+        </Card>
+
+        {/* 套餐详情 */}
+        {selectedPlanData && (
+          <Card>
+            <CardHeader>
+              <CardTitle>套餐详情</CardTitle>
+              <CardDescription>您选择的套餐内容</CardDescription>
+            </CardHeader>
+            <CardContent>
+              <div className="space-y-4">
+                <div className="grid grid-cols-2 gap-4">
+                  <div>
+                    <Label>套餐名称</Label>
+                    <p className="font-semibold">{selectedPlanData.name}</p>
+                  </div>
+                  <div>
+                    <Label>处理份数</Label>
+                    <p className="font-semibold text-blue-600">{selectedPlanData.count} 份</p>
+                  </div>
+                </div>
+                
+                <div className="border-t pt-4">
+                  <Label>价格计算</Label>
+                  <div className="space-y-1">
+                    <div className="flex justify-between">
+                      <span>套餐价格</span>
+                      <span>¥{selectedPlanData.price}</span>
+                    </div>
+                    <div className="flex justify-between font-semibold text-lg">
+                      <span>总计</span>
+                      <span>¥{selectedPlanData.price}</span>
+                    </div>
+                  </div>
+                </div>
+
+                <div className="bg-blue-50 p-4 rounded-lg">
+                  <p className="text-sm text-blue-800">
+                    <Check className="h-4 w-4 inline mr-1" />
+                    购买后立即到账,永久有效,支持随时使用
+                  </p>
+                </div>
+              </div>
+            </CardContent>
+          </Card>
+        )}
+
+        {/* 支付方式 */}
+        <Card>
+          <CardHeader>
+            <CardTitle>支付方式</CardTitle>
+            <CardDescription>选择您的支付方式</CardDescription>
+          </CardHeader>
+          <CardContent>
+            <RadioGroup value={paymentMethod} onValueChange={setPaymentMethod}>
+              <div className="flex items-center space-x-2">
+                <RadioGroupItem value="alipay" id="alipay" />
+                <Label htmlFor="alipay" className="flex items-center gap-2 cursor-pointer">
+                  <Wallet className="h-4 w-4" />
+                  支付宝
+                </Label>
+              </div>
+              <div className="flex items-center space-x-2">
+                <RadioGroupItem value="wechat" id="wechat" />
+                <Label htmlFor="wechat" className="flex items-center gap-2 cursor-pointer">
+                  <CreditCard className="h-4 w-4" />
+                  微信支付
+                </Label>
+              </div>
+            </RadioGroup>
+          </CardContent>
+        </Card>
+
+        {/* 支付按钮 */}
+        <Card>
+          <CardContent className="pt-6">
+            <Button 
+              className="w-full bg-blue-600 hover:bg-blue-700"
+              onClick={handlePayment}
+              disabled={!selectedPlanData}
+            >
+              立即支付 ¥{selectedPlanData?.price || 0}
+            </Button>
+            <p className="text-sm text-gray-600 text-center mt-2">
+              支付成功后,文档处理次数将立即到账
+            </p>
+          </CardContent>
+        </Card>
+
+        {/* 客服支持 */}
+        <Card>
+          <CardHeader>
+            <CardTitle>客服支持</CardTitle>
+            <CardDescription>遇到问题?我们随时为您服务</CardDescription>
+          </CardHeader>
+          <CardContent>
+            <div className="space-y-2 text-sm">
+              <p><strong>客服微信:</strong> yuanheng-word</p>
+              <p><strong>客服邮箱:</strong> support@yuanheng-word.com</p>
+              <p><strong>工作时间:</strong> 周一至周五 9:00-18:00</p>
+            </div>
+          </CardContent>
+        </Card>
+      </div>
+    </div>
+  );
+}

+ 19 - 0
src/client/home-shadcn/routes.tsx

@@ -9,6 +9,9 @@ import LoginPage from './pages/LoginPage';
 import RegisterPage from './pages/RegisterPage';
 import MemberPage from './pages/MemberPage';
 import WordPreview from './pages/WordPreview';
+import PricingPage from './pages/PricingPage';
+import ProfilePage from './pages/ProfilePage';
+import RechargePage from './pages/RechargePage';
 
 export const router = createBrowserRouter([
   {
@@ -27,6 +30,14 @@ export const router = createBrowserRouter([
     path: '/register',
     element: <RegisterPage />
   },
+  {
+    path: '/templates',
+    element: <HomePage />
+  },
+  {
+    path: '/pricing',
+    element: <PricingPage />
+  },
   {
     path: '/member',
     element: (
@@ -39,6 +50,14 @@ export const router = createBrowserRouter([
         path: '',
         element: <MemberPage />
       },
+      {
+        path: 'profile',
+        element: <ProfilePage />
+      },
+      {
+        path: 'recharge',
+        element: <RechargePage />
+      },
       {
         path: '*',
         element: <NotFoundPage />,

+ 2 - 1
src/server/data-source.ts

@@ -6,6 +6,7 @@ import process from 'node:process'
 import { UserEntity as User } from "./modules/users/user.entity"
 import { Role } from "./modules/users/role.entity"
 import { File } from "./modules/files/file.entity"
+import { PaymentEntity } from "./modules/payments/payment.entity"
 
 export const AppDataSource = new DataSource({
   type: "mysql",
@@ -15,7 +16,7 @@ export const AppDataSource = new DataSource({
   password: process.env.DB_PASSWORD || "",
   database: process.env.DB_DATABASE || "d8dai",
   entities: [
-    User, Role, File,
+    User, Role, File, PaymentEntity,
   ],
   migrations: [],
   synchronize: process.env.DB_SYNCHRONIZE !== "false",

+ 62 - 0
src/server/modules/payments/payment.entity.ts

@@ -0,0 +1,62 @@
+import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
+import { UserEntity } from '@/server/modules/users/user.entity';
+
+export enum PaymentStatus {
+  PENDING = 0,
+  COMPLETED = 1,
+  FAILED = 2,
+  CANCELLED = 3
+}
+
+export enum PaymentType {
+  RECHARGE = 'recharge',
+  CONSUME = 'consume',
+  REFUND = 'refund'
+}
+
+@Entity('payments')
+export class PaymentEntity {
+  @PrimaryGeneratedColumn({ unsigned: true, comment: '支付记录ID' })
+  id!: number;
+
+  @Column({ name: 'user_id', type: 'int', unsigned: true, comment: '用户ID' })
+  userId!: number;
+
+  @ManyToOne(() => UserEntity)
+  @JoinColumn({ name: 'user_id', referencedColumnName: 'id' })
+  user!: UserEntity;
+
+  @Column({ name: 'order_no', type: 'varchar', length: 64, unique: true, comment: '订单号' })
+  orderNo!: string;
+
+  @Column({ name: 'amount', type: 'decimal', precision: 10, scale: 2, comment: '支付金额' })
+  amount!: number;
+
+  @Column({ name: 'document_count', type: 'int', comment: '文档处理次数' })
+  documentCount!: number;
+
+  @Column({ name: 'payment_type', type: 'enum', enum: PaymentType, comment: '支付类型: recharge, consume, refund' })
+  paymentType!: PaymentType;
+
+  @Column({ name: 'status', type: 'int', default: PaymentStatus.PENDING, comment: '支付状态: 0-待支付,1-已完成,2-失败,3-已取消' })
+  status!: PaymentStatus;
+
+  @Column({ name: 'payment_method', type: 'varchar', length: 20, nullable: true, comment: '支付方式: alipay, wechat' })
+  paymentMethod!: string | null;
+
+  @Column({ name: 'transaction_id', type: 'varchar', length: 64, nullable: true, comment: '第三方交易ID' })
+  transactionId!: string | null;
+
+  @Column({ name: 'description', type: 'text', nullable: true, comment: '支付描述' })
+  description!: string | null;
+
+  @CreateDateColumn({ name: 'created_at', type: 'timestamp' })
+  createdAt!: Date;
+
+  @UpdateDateColumn({ name: 'updated_at', type: 'timestamp' })
+  updatedAt!: Date;
+
+  constructor(partial?: Partial<PaymentEntity>) {
+    Object.assign(this, partial);
+  }
+}

+ 18 - 0
src/server/modules/users/user.entity.ts

@@ -39,6 +39,24 @@ export class UserEntity {
   @Column({ name: 'is_deleted', type: 'int', default: DeleteStatus.NOT_DELETED, comment: '是否删除(0:未删除,1:已删除)' })
   isDeleted!: DeleteStatus;
 
+  @Column({ name: 'user_type', type: 'varchar', length: 20, default: 'basic', comment: '用户类型: basic, premium' })
+  userType!: string;
+
+  @Column({ name: 'remaining_count', type: 'int', default: 5, comment: '剩余文档处理次数' })
+  remainingCount!: number;
+
+  @Column({ name: 'total_usage', type: 'int', default: 0, comment: '总使用次数' })
+  totalUsage!: number;
+
+  @Column({ name: 'monthly_usage', type: 'int', default: 0, comment: '本月使用次数' })
+  monthlyUsage!: number;
+
+  @Column({ name: 'expire_at', type: 'timestamp', nullable: true, comment: '会员有效期' })
+  expireAt!: Date | null;
+
+  @Column({ name: 'last_used_at', type: 'timestamp', nullable: true, comment: '最后使用时间' })
+  lastUsedAt!: Date | null;
+
   @ManyToMany(() => Role)
   @JoinTable()
   roles!: Role[];

+ 29 - 5
src/server/modules/users/user.schema.ts

@@ -51,15 +51,39 @@ export const UserSchema = z.object({
     example: DeleteStatus.NOT_DELETED,
     description: '是否删除(0:未删除,1:已删除)'
   }),
+  userType: z.enum(['basic', 'premium']).default('basic').openapi({
+    example: 'basic',
+    description: '用户类型: basic-普通用户, premium-高级会员'
+  }),
+  remainingCount: z.number().int().min(0).default(5).openapi({
+    example: 100,
+    description: '剩余文档处理次数'
+  }),
+  totalUsage: z.number().int().min(0).default(0).openapi({
+    example: 50,
+    description: '总使用次数'
+  }),
+  monthlyUsage: z.number().int().min(0).default(0).openapi({
+    example: 10,
+    description: '本月使用次数'
+  }),
+  expireAt: z.coerce.date().nullable().optional().openapi({
+    example: '2024-12-31',
+    description: '会员有效期'
+  }),
+  lastUsedAt: z.coerce.date().nullable().optional().openapi({
+    example: '2024-01-15',
+    description: '最后使用时间'
+  }),
   roles: z.array(RoleSchema).optional().openapi({
     example: [
-      { 
-        id: 1, 
+      {
+        id: 1,
         name: 'admin',
-        description: '管理员', 
+        description: '管理员',
         permissions: ['user:create'],
-        createdAt: new Date(), 
-        updatedAt: new Date() 
+        createdAt: new Date(),
+        updatedAt: new Date()
       }
     ],
     description: '用户角色列表'