FastUI电商应用:购物车与结账流程实现

FastUI电商应用:购物车与结账流程实现

【免费下载链接】FastUI Build better UIs faster. 【免费下载链接】FastUI 项目地址: https://gitcode.com/GitHub_Trending/fa/FastUI

概述

在当今电商时代,流畅的购物车和结账体验是提升转化率的关键因素。FastUI作为一个声明式的Python Web UI框架,为开发者提供了构建现代化电商界面的强大工具。本文将深入探讨如何使用FastUI实现完整的电商购物车和结账流程。

核心概念

FastUI电商架构

mermaid

技术栈组成

组件类型技术实现作用描述
前端组件FastUI React组件渲染购物车界面和结账表单
后端逻辑FastAPI + Pydantic处理业务逻辑和数据验证
数据模型Pydantic模型定义商品、订单、用户等数据结构
状态管理FastUI事件系统处理用户交互和页面跳转

购物车实现

商品数据模型

from pydantic import BaseModel, Field
from typing import List, Optional
from datetime import datetime

class Product(BaseModel):
    id: int = Field(title="商品ID")
    name: str = Field(title="商品名称")
    price: float = Field(title="价格", ge=0)
    stock: int = Field(title="库存", ge=0)
    description: Optional[str] = Field(title="商品描述")
    image_url: Optional[str] = Field(title="图片链接")

class CartItem(BaseModel):
    product: Product
    quantity: int = Field(title="数量", ge=1)
    added_at: datetime = Field(title="添加时间", default_factory=datetime.now)
    
    @property
    def total_price(self) -> float:
        return self.product.price * self.quantity

class ShoppingCart(BaseModel):
    items: List[CartItem] = Field(default_factory=list, title="购物车商品")
    user_id: Optional[int] = Field(title="用户ID")
    
    @property
    def total_items(self) -> int:
        return sum(item.quantity for item in self.items)
    
    @property
    def total_amount(self) -> float:
        return sum(item.total_price for item in self.items)
    
    def add_item(self, product: Product, quantity: int = 1):
        """添加商品到购物车"""
        for item in self.items:
            if item.product.id == product.id:
                item.quantity += quantity
                return
        self.items.append(CartItem(product=product, quantity=quantity))
    
    def remove_item(self, product_id: int):
        """从购物车移除商品"""
        self.items = [item for item in self.items if item.product.id != product_id]
    
    def update_quantity(self, product_id: int, quantity: int):
        """更新商品数量"""
        for item in self.items:
            if item.product.id == product_id:
                if quantity <= 0:
                    self.remove_item(product_id)
                else:
                    item.quantity = quantity
                return

购物车界面组件

from fastapi import APIRouter, Request
from fastui import AnyComponent, FastUI
from fastui import components as c
from fastui.events import GoToEvent, BackEvent
from fastui.components.display import DisplayLookup, DisplayMode

router = APIRouter()

# 模拟商品数据
products = [
    Product(id=1, name="智能手机", price=2999.0, stock=100, description="最新款智能手机"),
    Product(id=2, name="笔记本电脑", price=5999.0, stock=50, description="高性能笔记本电脑"),
    Product(id=3, name="无线耳机", price=399.0, stock=200, description="高品质无线耳机"),
]

# 用户购物车状态(实际应用中应使用数据库或缓存)
user_carts = {}

@router.get('/cart', response_model=FastUI, response_model_exclude_none=True)
async def cart_view(request: Request):
    """购物车页面"""
    # 获取或创建用户购物车
    session_id = request.cookies.get("session_id", "default")
    cart = user_carts.get(session_id, ShoppingCart())
    
    cart_components = []
    if cart.items:
        # 购物车商品列表
        cart_components.extend([
            c.Table(
                data=cart.items,
                columns=[
                    DisplayLookup(field='product.name', title='商品名称'),
                    DisplayLookup(field='product.price', title='单价', mode=DisplayMode.currency),
                    DisplayLookup(field='quantity', title='数量'),
                    DisplayLookup(field='total_price', title='小计', mode=DisplayMode.currency),
                    c.Display(
                        components=[
                            c.Button(
                                text="-",
                                on_click=GoToEvent(url=f'/api/cart/decrease/{item.product.id}'),
                                class_name='btn-sm btn-outline-secondary'
                            ),
                            c.Button(
                                text="+", 
                                on_click=GoToEvent(url=f'/api/cart/increase/{item.product.id}'),
                                class_name='btn-sm btn-outline-primary'
                            ),
                            c.Button(
                                text="删除",
                                on_click=GoToEvent(url=f'/api/cart/remove/{item.product.id}'),
                                class_name='btn-sm btn-outline-danger'
                            )
                        ]
                    )
                ]
            ),
            c.Div(
                components=[
                    c.Text(text=f"总计: {cart.total_amount:.2f}元", class_name='h4'),
                    c.Button(
                        text="去结算",
                        on_click=GoToEvent(url='/checkout'),
                        class_name='btn-primary btn-lg'
                    )
                ],
                class_name='text-end mt-4'
            )
        ])
    else:
        # 空购物车提示
        cart_components.append(
            c.Div(
                components=[
                    c.Heading(text="购物车为空", level=3),
                    c.Paragraph(text="您还没有添加任何商品到购物车"),
                    c.Button(
                        text="继续购物",
                        on_click=GoToEvent(url='/products'),
                        class_name='btn-primary'
                    )
                ],
                class_name='text-center py-5'
            )
        )
    
    return [
        c.Page(
            components=[
                c.Heading(text="购物车", level=2),
                *cart_components
            ]
        )
    ]

结账流程实现

订单数据模型

from enum import Enum
from pydantic import EmailStr, validator

class OrderStatus(str, Enum):
    PENDING = "pending"  # 待支付
    PAID = "paid"        # 已支付
    SHIPPED = "shipped"  # 已发货
    COMPLETED = "completed"  # 已完成
    CANCELLED = "cancelled"  # 已取消

class PaymentMethod(str, Enum):
    ALIPAY = "alipay"    # 支付宝
    WECHAT = "wechat"    # 微信支付
    BANK = "bank"        # 银行卡
    CASH = "cash"        # 当面支付

class ShippingAddress(BaseModel):
    recipient: str = Field(title="收货人")
    phone: str = Field(title="联系号码")
    province: str = Field(title="省份")
    city: str = Field(title="城市")
    district: str = Field(title="区县")
    address: str = Field(title="详细地址")
    postal_code: str = Field(title="邮政编码")

class Order(BaseModel):
    id: str = Field(title="订单号")
    user_id: Optional[int] = Field(title="用户ID")
    items: List[CartItem] = Field(title="订单商品")
    total_amount: float = Field(title="订单总额", ge=0)
    shipping_address: ShippingAddress = Field(title="收货地址")
    payment_method: PaymentMethod = Field(title="支付方式")
    status: OrderStatus = Field(title="订单状态", default=OrderStatus.PENDING)
    created_at: datetime = Field(title="创建时间", default_factory=datetime.now)
    paid_at: Optional[datetime] = Field(title="支付时间")
    
    @validator('id')
    def validate_order_id(cls, v):
        if not v.startswith('ORD'):
            raise ValueError('订单号必须以ORD开头')
        return v

结账表单组件

class CheckoutForm(BaseModel):
    recipient: str = Field(title="收货人", min_length=2, max_length=50)
    phone: str = Field(title="手机号码", pattern=r'^1[3-9]\d{9}$')
    province: str = Field(title="省份")
    city: str = Field(title="城市")
    district: str = Field(title="区县")
    address: str = Field(title="详细地址", min_length=5, max_length=200)
    postal_code: str = Field(title="邮政编码", pattern=r'^\d{6}$')
    payment_method: PaymentMethod = Field(title="支付方式")
    notes: Optional[str] = Field(title="备注", max_length=500)

@router.get('/checkout', response_model=FastUI, response_model_exclude_none=True)
async def checkout_view(request: Request):
    """结账页面"""
    session_id = request.cookies.get("session_id", "default")
    cart = user_carts.get(session_id, ShoppingCart())
    
    if not cart.items:
        return [c.FireEvent(event=GoToEvent(url='/cart'))]
    
    return [
        c.Page(
            components=[
                c.Heading(text="订单结算", level=2),
                # 订单摘要
                c.Div(
                    components=[
                        c.Heading(text="订单信息", level=4),
                        c.Table(
                            data=cart.items,
                            columns=[
                                DisplayLookup(field='product.name', title='商品'),
                                DisplayLookup(field='quantity', title='数量'),
                                DisplayLookup(field='total_price', title='金额', mode=DisplayMode.currency),
                            ]
                        ),
                        c.Text(text=f"订单总额: {cart.total_amount:.2f}元", class_name='h5 text-end')
                    ],
                    class_name='mb-4 p-3 border rounded'
                ),
                # 收货信息表单
                c.Heading(text="收货信息", level=4),
                c.ModelForm(
                    model=CheckoutForm,
                    submit_url='/api/checkout/submit',
                    display_mode='page'
                )
            ]
        )
    ]

订单处理逻辑

from fastui.forms import fastui_form
import uuid

@router.post('/checkout/submit', response_model=FastUI, response_model_exclude_none=True)
async def checkout_submit(
    request: Request,
    form: Annotated[CheckoutForm, fastui_form(CheckoutForm)]
):
    """处理订单提交"""
    session_id = request.cookies.get("session_id", "default")
    cart = user_carts.get(session_id)
    
    if not cart or not cart.items:
        return [c.FireEvent(event=GoToEvent(url='/cart'))]
    
    # 生成订单
    order_id = f"ORD{str(uuid.uuid4())[:8].upper()}"
    shipping_address = ShippingAddress(**form.dict())
    
    order = Order(
        id=order_id,
        items=cart.items.copy(),
        total_amount=cart.total_amount,
        shipping_address=shipping_address,
        payment_method=form.payment_method
    )
    
    # 保存订单(实际应用中应保存到数据库)
    orders[order_id] = order
    
    # 清空购物车
    user_carts[session_id] = ShoppingCart()
    
    # 跳转到支付页面
    return [c.FireEvent(event=GoToEvent(url=f'/payment/{order_id}'))]

@router.get('/payment/{order_id}', response_model=FastUI, response_model_exclude_none=True)
async def payment_view(order_id: str):
    """支付页面"""
    order = orders.get(order_id)
    if not order:
        return [c.FireEvent(event=GoToEvent(url='/'))]
    
    payment_methods = {
        PaymentMethod.ALIPAY: "支付宝支付",
        PaymentMethod.WECHAT: "微信支付",
        PaymentMethod.BANK: "银行卡支付",
        PaymentMethod.CASH: "当面支付"
    }
    
    return [
        c.Page(
            components=[
                c.Heading(text="支付订单", level=2),
                c.Div(
                    components=[
                        c.Text(text=f"订单号: {order.id}", class_name='h5'),
                        c.Text(text=f"订单金额: {order.total_amount:.2f}元", class_name='h5'),
                        c.Text(text=f"支付方式: {payment_methods[order.payment_method]}", class_name='h5')
                    ],
                    class_name='mb-4 p-3 border rounded'
                ),
                c.Div(
                    components=[
                        c.Button(
                            text="确认支付",
                            on_click=GoToEvent(url=f'/api/payment/confirm/{order_id}'),
                            class_name='btn-success btn-lg'
                        ),
                        c.Button(
                            text="取消订单",
                            on_click=GoToEvent(url=f'/api/order/cancel/{order_id}'),
                            class_name='btn-outline-secondary btn-lg ms-2'
                        )
                    ],
                    class_name='text-center'
                )
            ]
        )
    ]

完整电商流程集成

主应用集成

from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from fastui import prebuilt_html

app = FastAPI(title="FastUI电商示例")

# 注册路由
app.include_router(router, prefix="/api")

# 模拟数据存储
orders = {}
user_carts = {}

@app.get('/{path:path}')
async def html_landing() -> HTMLResponse:
    """服务React应用"""
    return HTMLResponse(prebuilt_html(title='FastUI电商示例'))

# 购物车操作API
@app.get('/api/cart/increase/{product_id}')
async def increase_cart_item(product_id: int, request: Request):
    session_id = request.cookies.get("session_id", "default")
    cart = user_carts.get(session_id, ShoppingCart())
    
    product = next((p for p in products if p.id == product_id), None)
    if product:
        cart.add_item(product)
        user_carts[session_id] = cart
    
    return [c.FireEvent(event=GoToEvent(url='/cart'))]

@app.get('/api/cart/decrease/{product_id}')
async def decrease_cart_item(product_id: int, request: Request):
    session_id = request.cookies.get("session_id", "default")
    cart = user_carts.get(session_id)
    
    if cart:
        for item in cart.items:
            if item.product.id == product_id:
                if item.quantity > 1:
                    item.quantity -= 1
                else:
                    cart.remove_item(product_id)
                break
        user_carts[session_id] = cart
    
    return [c.FireEvent(event=GoToEvent(url='/cart'))]

@app.get('/api/cart/remove/{product_id}')
async def remove_cart_item(product_id: int, request: Request):
    session_id = request.cookies.get("session_id", "default")
    cart = user_carts.get(session_id)
    
    if cart:
        cart.remove_item(product_id)
        user_carts[session_id] = cart
    
    return [c.FireEvent(event=GoToEvent(url='/cart'))]

最佳实践和优化建议

性能优化策略

mermaid

安全考虑

  1. 输入验证:使用Pydantic进行严格的数据验证
  2. XSS防护:FastUI自动处理HTML转义
  3. CSRF保护:实现令牌验证机制
  4. 数据加密:敏感信息加密存储

扩展功能建议

功能模块实现方式业务价值
优惠券系统折扣计算组件提升转化率
库存管理实时库存检查避免超卖
订单追踪物流状态组件提升用户体验
会员体系积分和等级系统增加用户粘性

总结

通过FastUI构建电商购物车和结账流程,开发者可以享受到声明式编程带来的诸多好处:

  1. 开发效率:后端定义UI,减少前后端沟通成本
  2. 类型安全:Pydantic和TypeScript提供完整的类型检查
  3. 维护性:清晰的业务逻辑分离,便于维护和扩展
  4. 一致性:统一的组件库确保用户体验的一致性

FastUI为电商应用开发提供了一种现代化的解决方案,特别适合需要快速迭代和高质量代码的电商项目。通过本文介绍的实现方式,您可以快速构建出功能完善、用户体验优秀的电商购物系统。

【免费下载链接】FastUI Build better UIs faster. 【免费下载链接】FastUI 项目地址: https://gitcode.com/GitHub_Trending/fa/FastUI

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值