HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • AI 训练手册

    • AI UI生成系统 - 完整学习手册
    • 项目概述与架构设计
    • 环境搭建与快速开始
    • 核心概念与术语
    • 数据生成系统
    • UI-DSL数据格式详解
    • 数据质量与评估
    • LoRA微调技术
    • 完整的模型训练流程
    • 模型推理与优化
    • PNG图片渲染实现
    • Vue页面渲染系统
    • 多主题支持架构
    • FastAPI服务设计
    • Docker部署实践
    • 生产环境运维
    • 项目实战案例
    • 性能优化指南
    • 扩展开发指南
    • API参考文档
    • 配置参数说明
    • 故障排查指南

项目实战案例

1. 概述

本章通过4个完整的实战案例,展示AI UI生成系统在实际项目中的应用。每个案例都包含从需求分析到最终实现的完整流程,涵盖UI生成、主题定制、组件扩展和性能优化等核心功能。通过这些案例,您可以深入理解系统的实际应用场景和最佳实践。

2. 案例1:电商首页完整生成

2.1 需求分析

业务场景:为某电商平台生成一个现代化的首页设计

功能要求:

  • 顶部导航栏(Logo、搜索、用户中心)
  • 轮播图展示区
  • 商品分类导航
  • 热门商品推荐
  • 底部导航栏

设计要求:

  • 使用obsidian-gold主题
  • 响应式布局
  • 现代化设计风格

2.2 实现步骤

步骤1:生成UI设计

# 案例1:电商首页生成
import json
import requests
from pathlib import Path

def generate_ecommerce_homepage():
    """生成电商首页UI设计"""
    
    # 1. 准备生成请求
    prompt = """
    设计一个电商首页,包含以下功能:
    1. 顶部导航栏:左侧显示品牌Logo,中间是搜索框,右侧是用户中心和购物车图标
    2. 轮播图区域:展示3-5张促销活动图片
    3. 商品分类导航:横向滚动的分类标签
    4. 热门商品推荐:2列网格布局展示商品卡片
    5. 底部导航栏:首页、分类、购物车、我的四个标签
    
    要求使用现代化设计风格,色彩搭配要专业。
    """
    
    # 2. 调用API生成UI-DSL
    api_url = "http://localhost:8000/generate-ui"
    request_data = {
        "prompt": prompt,
        "theme": "obsidian-gold",
        "page_type": "home_page",
        "use_model": True,
        "max_length": 512,
        "temperature": 0.7
    }
    
    response = requests.post(api_url, json=request_data)
    
    if response.status_code == 200:
        result = response.json()
        if result["success"]:
            dsl = result["dsl"]
            print(" UI设计生成成功")
            return dsl
        else:
            print(f" UI设计生成失败: {result['error_message']}")
            return None
    else:
        print(f" API调用失败: {response.status_code}")
        return None

# 执行生成
homepage_dsl = generate_ecommerce_homepage()
if homepage_dsl:
    # 保存DSL到文件
    with open("output/case1_homepage_dsl.json", "w", encoding="utf-8") as f:
        json.dump(homepage_dsl, f, ensure_ascii=False, indent=2)
    print("📁 DSL已保存到: output/case1_homepage_dsl.json")

步骤2:渲染为图片和Vue页面

def render_homepage_outputs(dsl):
    """渲染首页为图片和Vue页面"""
    
    # 1. 渲染为PNG图片
    render_url = "http://localhost:8000/render"
    render_request = {
        "dsl": dsl,
        "output_format": "both",  # 同时生成PNG和Vue
        "theme": "obsidian-gold",
        "optimize": True
    }
    
    response = requests.post(render_url, json=render_request)
    
    if response.status_code == 200:
        result = response.json()
        if result["success"]:
            print(" 渲染完成")
            print(f"📸 PNG图片: {result['files']['png']}")
            print(f" Vue页面: {result['files']['vue']}")
            return result["files"]
        else:
            print(f" 渲染失败: {result['error_message']}")
            return None
    else:
        print(f" 渲染API调用失败: {response.status_code}")
        return None

# 执行渲染
if homepage_dsl:
    output_files = render_homepage_outputs(homepage_dsl)

步骤3:生成的UI-DSL结构分析

{
  "page": {
    "name": "home_page",
    "theme": "obsidian-gold",
    "sections": [
      {
        "type": "topbar",
        "props": {
          "title": "电商首页",
          "logo": "🛍️ ShopMall",
          "actions": ["search", "user", "cart"]
        }
      },
      {
        "type": "carousel",
        "props": {
          "images": 4,
          "autoplay": true,
          "indicators": true
        }
      },
      {
        "type": "tabs",
        "props": {
          "items": ["热门", "新品", "特价", "品牌"],
          "active": 0
        }
      },
      {
        "type": "card-list",
        "props": {
          "columns": 2,
          "card": {
            "type": "product-card",
            "show_price": true,
            "show_rating": true
          }
        }
      },
      {
        "type": "tabbar",
        "props": {
          "items": [
            {"name": "首页", "icon": "home"},
            {"name": "分类", "icon": "category"},
            {"name": "购物车", "icon": "cart"},
            {"name": "我的", "icon": "user"}
          ]
        }
      }
    ]
  }
}

步骤4:自定义优化

def optimize_homepage_dsl(dsl):
    """优化首页DSL"""
    
    # 1. 添加更多商品数据
    if "sections" in dsl["page"]:
        for section in dsl["page"]["sections"]:
            if section["type"] == "card-list":
                # 添加商品数据
                section["props"]["products"] = [
                    {
                        "id": 1,
                        "title": "iPhone 15 Pro",
                        "price": "¥7999",
                        "image": "/static/images/iphone15.jpg",
                        "rating": 4.8,
                        "sales": "1.2万+"
                    },
                    {
                        "id": 2,
                        "title": "MacBook Air M2",
                        "price": "¥8999",
                        "image": "/static/images/macbook.jpg",
                        "rating": 4.9,
                        "sales": "8千+"
                    },
                    {
                        "id": 3,
                        "title": "AirPods Pro",
                        "price": "¥1899",
                        "image": "/static/images/airpods.jpg",
                        "rating": 4.7,
                        "sales": "2.5万+"
                    },
                    {
                        "id": 4,
                        "title": "Apple Watch",
                        "price": "¥2999",
                        "image": "/static/images/watch.jpg",
                        "rating": 4.6,
                        "sales": "1.8万+"
                    }
                ]
    
    # 2. 添加轮播图数据
    for section in dsl["page"]["sections"]:
        if section["type"] == "carousel":
            section["props"]["banners"] = [
                {
                    "image": "/static/images/banner1.jpg",
                    "title": "春季大促销",
                    "subtitle": "全场8折起"
                },
                {
                    "image": "/static/images/banner2.jpg",
                    "title": "新品上市",
                    "subtitle": "iPhone 15系列"
                },
                {
                    "image": "/static/images/banner3.jpg",
                    "title": "会员专享",
                    "subtitle": "VIP特权优惠"
                }
            ]
    
    return dsl

# 优化DSL
if homepage_dsl:
    optimized_dsl = optimize_homepage_dsl(homepage_dsl)
    
    # 保存优化后的DSL
    with open("output/case1_optimized_dsl.json", "w", encoding="utf-8") as f:
        json.dump(optimized_dsl, f, ensure_ascii=False, indent=2)
    
    # 重新渲染
    final_outputs = render_homepage_outputs(optimized_dsl)

2.3 案例总结

实现效果:

  • 生成了完整的电商首页UI设计
  • 包含所有必需的功能模块
  • 使用obsidian-gold主题,视觉效果专业
  • 同时输出PNG图片和Vue页面
  • 添加了真实的商品数据

技术要点:

  • 使用AI模型生成基础UI结构
  • 通过API调用实现端到端流程
  • 自定义优化增强用户体验
  • 多格式输出满足不同需求

3. 案例2:商品详情页生成

3.1 需求分析

业务场景:为电商平台生成商品详情页

功能要求:

  • 商品图片轮播
  • 商品基本信息(标题、价格、评分)
  • 商品规格选择
  • 购买按钮
  • 商品详情描述
  • 用户评价展示

3.2 实现步骤

步骤1:生成商品详情页DSL

def generate_product_detail_page():
    """生成商品详情页"""
    
    prompt = """
    设计一个商品详情页,包含以下功能:
    1. 商品图片轮播:支持多张图片切换
    2. 商品信息区域:标题、价格、评分、销量
    3. 规格选择:颜色、尺寸、数量选择器
    4. 购买操作:立即购买、加入购物车按钮
    5. 商品详情:图文并茂的商品描述
    6. 用户评价:评分统计和用户评论列表
    
    要求布局清晰,信息层次分明,便于用户快速了解商品信息。
    """
    
    request_data = {
        "prompt": prompt,
        "theme": "silver-white",  # 使用银白主题,更适合商品展示
        "page_type": "product_detail",
        "use_model": True,
        "max_length": 600,
        "temperature": 0.6
    }
    
    response = requests.post("http://localhost:8000/generate-ui", json=request_data)
    
    if response.status_code == 200:
        result = response.json()
        if result["success"]:
            return result["dsl"]
    
    return None

# 生成商品详情页
product_dsl = generate_product_detail_page()

步骤2:自定义商品数据

def customize_product_data(dsl, product_info):
    """自定义商品数据"""
    
    # 商品基本信息
    product_data = {
        "id": product_info["id"],
        "title": product_info["title"],
        "price": product_info["price"],
        "original_price": product_info.get("original_price", ""),
        "rating": product_info["rating"],
        "reviews_count": product_info["reviews_count"],
        "sales": product_info["sales"],
        "images": product_info["images"],
        "specifications": product_info["specifications"],
        "description": product_info["description"],
        "reviews": product_info["reviews"]
    }
    
    # 更新DSL中的商品数据
    if "sections" in dsl["page"]:
        for section in dsl["page"]["sections"]:
            if section["type"] == "carousel":
                section["props"]["images"] = product_data["images"]
            elif section["type"] == "price":
                section["props"]["value"] = product_data["price"]
                section["props"]["original"] = product_data["original_price"]
            elif section["type"] == "seller":
                section["props"]["name"] = "官方旗舰店"
                section["props"]["trust"] = f"评分 {product_data['rating']} | 销量 {product_data['sales']}"
            elif section["type"] == "cta":
                section["props"]["buttons"] = ["立即购买", "加入购物车"]
    
    return dsl

# 示例商品数据
iphone_product = {
    "id": "iphone15pro",
    "title": "Apple iPhone 15 Pro 256GB 深空黑色",
    "price": "¥7999",
    "original_price": "¥8999",
    "rating": 4.8,
    "reviews_count": 12580,
    "sales": "1.2万+",
    "images": [
        "/static/images/iphone15_1.jpg",
        "/static/images/iphone15_2.jpg",
        "/static/images/iphone15_3.jpg",
        "/static/images/iphone15_4.jpg"
    ],
    "specifications": {
        "color": ["深空黑色", "白色", "蓝色", "钛金色"],
        "storage": ["128GB", "256GB", "512GB", "1TB"],
        "quantity": 1
    },
    "description": """
    iPhone 15 Pro 采用钛金属设计,配备A17 Pro芯片,支持USB-C接口。
    全新Action按钮,可自定义功能。48MP主摄像头,支持5倍光学变焦。
    6.1英寸Super Retina XDR显示屏,支持ProMotion技术。
    """,
    "reviews": [
        {
            "user": "张三",
            "rating": 5,
            "comment": "手机很漂亮,拍照效果很好,系统流畅。",
            "date": "2024-01-15"
        },
        {
            "user": "李四",
            "rating": 4,
            "comment": "整体不错,就是价格有点贵。",
            "date": "2024-01-14"
        }
    ]
}

# 自定义商品数据
if product_dsl:
    customized_dsl = customize_product_data(product_dsl, iphone_product)
    
    # 保存DSL
    with open("output/case2_product_detail.json", "w", encoding="utf-8") as f:
        json.dump(customized_dsl, f, ensure_ascii=False, indent=2)

步骤3:渲染和测试

def render_and_test_product_page(dsl):
    """渲染并测试商品详情页"""
    
    # 渲染页面
    render_request = {
        "dsl": dsl,
        "output_format": "both",
        "theme": "silver-white",
        "optimize": True
    }
    
    response = requests.post("http://localhost:8000/render", json=render_request)
    
    if response.status_code == 200:
        result = response.json()
        if result["success"]:
            print(" 商品详情页渲染成功")
            
            # 测试页面功能
            test_product_page_features(dsl)
            
            return result["files"]
    
    return None

def test_product_page_features(dsl):
    """测试商品详情页功能"""
    
    print("\n🧪 功能测试:")
    
    # 检查必需组件
    required_components = ["carousel", "price", "seller", "cta"]
    found_components = []
    
    for section in dsl["page"]["sections"]:
        if section["type"] in required_components:
            found_components.append(section["type"])
    
    print(f" 找到组件: {', '.join(found_components)}")
    
    # 检查数据完整性
    has_images = any(s["type"] == "carousel" for s in dsl["page"]["sections"])
    has_price = any(s["type"] == "price" for s in dsl["page"]["sections"])
    has_buttons = any(s["type"] == "cta" for s in dsl["page"]["sections"])
    
    print(f" 图片轮播: {'是' if has_images else '否'}")
    print(f" 价格信息: {'是' if has_price else '否'}")
    print(f" 购买按钮: {'是' if has_buttons else '否'}")

# 执行渲染和测试
if product_dsl:
    customized_dsl = customize_product_data(product_dsl, iphone_product)
    output_files = render_and_test_product_page(customized_dsl)

3.3 案例总结

实现效果:

  • 生成了完整的商品详情页布局
  • 集成了真实的商品数据
  • 支持多种商品规格选择
  • 包含用户评价展示
  • 使用silver-white主题,适合商品展示

技术要点:

  • 针对特定页面类型优化提示词
  • 数据结构化集成真实业务数据
  • 功能完整性测试验证
  • 主题选择考虑业务场景

4. 案例3:自定义主题开发

4.1 需求分析

业务场景:为某科技公司开发专属主题

设计要求:

  • 科技感强的深色主题
  • 蓝色和绿色为主色调
  • 现代简约风格
  • 支持深色模式

4.2 实现步骤

步骤1:创建自定义主题配置

def create_custom_tech_theme():
    """创建科技主题配置"""
    
    tech_theme = {
        "name": "tech-dark",
        "display_name": "科技深色主题",
        "description": "专为科技公司设计的深色主题,采用蓝色和绿色配色方案",
        "colors": {
            "primary": "#00D4FF",      # 科技蓝
            "secondary": "#00FF88",    # 科技绿
            "background": "#0A0A0A",   # 深黑背景
            "surface": "#1A1A1A",      # 表面色
            "text": "#FFFFFF",         # 主文本
            "text_secondary": "#B0B0B0", # 次要文本
            "border": "#333333",       # 边框色
            "accent": "#FF6B35",       # 强调色
            "success": "#00FF88",      # 成功色
            "warning": "#FFB800",      # 警告色
            "error": "#FF4757"         # 错误色
        },
        "typography": {
            "font_family": "SF Pro Display, -apple-system, BlinkMacSystemFont, sans-serif",
            "font_size_base": "16px",
            "font_size_small": "14px",
            "font_size_large": "18px",
            "font_weight_normal": "400",
            "font_weight_bold": "600"
        },
        "spacing": {
            "xs": "4px",
            "sm": "8px",
            "md": "16px",
            "lg": "24px",
            "xl": "32px",
            "xxl": "48px"
        },
        "border_radius": {
            "small": "6px",
            "medium": "12px",
            "large": "18px",
            "round": "50%"
        },
        "shadows": {
            "small": "0 2px 8px rgba(0, 212, 255, 0.1)",
            "medium": "0 4px 16px rgba(0, 212, 255, 0.15)",
            "large": "0 8px 32px rgba(0, 212, 255, 0.2)"
        },
        "effects": {
            "glow": "0 0 20px rgba(0, 212, 255, 0.3)",
            "neon": "0 0 10px rgba(0, 255, 136, 0.5)"
        }
    }
    
    return tech_theme

# 创建主题
tech_theme = create_custom_tech_theme()

步骤2:集成主题到系统

def integrate_custom_theme(theme_config):
    """集成自定义主题到系统"""
    
    # 1. 更新ui_tokens.json
    tokens_path = "config/ui_tokens.json"
    
    with open(tokens_path, "r", encoding="utf-8") as f:
        tokens = json.load(f)
    
    # 添加新主题
    tokens["themes"][theme_config["name"]] = theme_config
    
    # 保存更新后的配置
    with open(tokens_path, "w", encoding="utf-8") as f:
        json.dump(tokens, f, ensure_ascii=False, indent=2)
    
    print(f" 主题 {theme_config['name']} 已集成到系统")
    
    # 2. 更新model_config.yaml
    config_path = "config/model_config.yaml"
    
    with open(config_path, "r", encoding="utf-8") as f:
        config = yaml.safe_load(f)
    
    # 添加新主题到可用主题列表
    if "ui" not in config:
        config["ui"] = {}
    if "themes" not in config["ui"]:
        config["ui"]["themes"] = []
    
    if theme_config["name"] not in config["ui"]["themes"]:
        config["ui"]["themes"].append(theme_config["name"])
    
    # 保存更新后的配置
    with open(config_path, "w", encoding="utf-8") as f:
        yaml.dump(config, f, default_flow_style=False, allow_unicode=True)
    
    print(f" 配置已更新")

# 集成主题
integrate_custom_theme(tech_theme)

步骤3:测试自定义主题

def test_custom_theme():
    """测试自定义主题"""
    
    # 1. 生成测试页面
    test_prompt = """
    设计一个科技公司的产品展示页面,包含:
    1. 顶部导航栏:公司Logo和产品菜单
    2. 产品展示区:展示最新科技产品
    3. 功能介绍:产品特性说明
    4. 联系信息:获取更多信息的方式
    
    要求体现科技感和专业性。
    """
    
    request_data = {
        "prompt": test_prompt,
        "theme": "tech-dark",  # 使用新创建的主题
        "page_type": "product_showcase",
        "use_model": True,
        "max_length": 512,
        "temperature": 0.7
    }
    
    response = requests.post("http://localhost:8000/generate-ui", json=request_data)
    
    if response.status_code == 200:
        result = response.json()
        if result["success"]:
            dsl = result["dsl"]
            print(" 使用自定义主题生成页面成功")
            
            # 2. 渲染页面
            render_request = {
                "dsl": dsl,
                "output_format": "both",
                "theme": "tech-dark",
                "optimize": True
            }
            
            render_response = requests.post("http://localhost:8000/render", json=render_request)
            
            if render_response.status_code == 200:
                render_result = render_response.json()
                if render_result["success"]:
                    print(" 自定义主题渲染成功")
                    print(f"📸 PNG图片: {render_result['files']['png']}")
                    print(f" Vue页面: {render_result['files']['vue']}")
                    
                    # 保存测试结果
                    with open("output/case3_tech_theme_test.json", "w", encoding="utf-8") as f:
                        json.dump(dsl, f, ensure_ascii=False, indent=2)
                    
                    return True
    
    print(" 自定义主题测试失败")
    return False

# 测试自定义主题
test_custom_theme()

步骤4:主题预览生成

def generate_theme_preview():
    """生成主题预览页面"""
    
    preview_html = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{tech_theme['display_name']} - 主题预览</title>
    <style>
        :root {{
            --primary-color: {tech_theme['colors']['primary']};
            --secondary-color: {tech_theme['colors']['secondary']};
            --background-color: {tech_theme['colors']['background']};
            --surface-color: {tech_theme['colors']['surface']};
            --text-color: {tech_theme['colors']['text']};
            --text-secondary-color: {tech_theme['colors']['text_secondary']};
            --border-color: {tech_theme['colors']['border']};
            --accent-color: {tech_theme['colors']['accent']};
            --success-color: {tech_theme['colors']['success']};
            --warning-color: {tech_theme['colors']['warning']};
            --error-color: {tech_theme['colors']['error']};
        }}
        
        body {{
            font-family: {tech_theme['typography']['font_family']};
            background-color: var(--background-color);
            color: var(--text-color);
            margin: 0;
            padding: 20px;
        }}
        
        .preview-container {{
            max-width: 1200px;
            margin: 0 auto;
        }}
        
        .theme-header {{
            text-align: center;
            margin-bottom: 40px;
        }}
        
        .theme-title {{
            font-size: 2.5em;
            color: var(--primary-color);
            margin-bottom: 10px;
            text-shadow: {tech_theme['effects']['glow']};
        }}
        
        .theme-description {{
            color: var(--text-secondary-color);
            font-size: 1.2em;
        }}
        
        .component-grid {{
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 30px;
            margin-bottom: 40px;
        }}
        
        .component-card {{
            background-color: var(--surface-color);
            border: 1px solid var(--border-color);
            border-radius: {tech_theme['border_radius']['medium']};
            padding: 20px;
            box-shadow: {tech_theme['shadows']['medium']};
        }}
        
        .component-title {{
            color: var(--secondary-color);
            margin-bottom: 15px;
            font-size: 1.3em;
        }}
        
        .button {{
            background-color: var(--primary-color);
            color: var(--background-color);
            border: none;
            padding: 12px 24px;
            border-radius: {tech_theme['border_radius']['small']};
            font-weight: {tech_theme['typography']['font_weight_bold']};
            cursor: pointer;
            margin: 5px;
            transition: all 0.3s ease;
        }}
        
        .button:hover {{
            box-shadow: {tech_theme['effects']['glow']};
            transform: translateY(-2px);
        }}
        
        .input {{
            background-color: var(--surface-color);
            border: 1px solid var(--border-color);
            color: var(--text-color);
            padding: 12px;
            border-radius: {tech_theme['border_radius']['small']};
            width: 100%;
            margin: 5px 0;
        }}
        
        .color-palette {{
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
            gap: 15px;
            margin-top: 20px;
        }}
        
        .color-item {{
            text-align: center;
        }}
        
        .color-swatch {{
            width: 80px;
            height: 80px;
            border-radius: {tech_theme['border_radius']['small']};
            margin: 0 auto 10px;
            border: 1px solid var(--border-color);
        }}
        
        .color-name {{
            font-size: 0.9em;
            color: var(--text-secondary-color);
        }}
    </style>
</head>
<body>
    <div class="preview-container">
        <div class="theme-header">
            <h1 class="theme-title">{tech_theme['display_name']}</h1>
            <p class="theme-description">{tech_theme['description']}</p>
        </div>
        
        <div class="component-grid">
            <div class="component-card">
                <h3 class="component-title">按钮组件</h3>
                <button class="button">主要按钮</button>
                <button class="button" style="background-color: var(--secondary-color);">次要按钮</button>
                <button class="button" style="background-color: var(--accent-color);">强调按钮</button>
            </div>
            
            <div class="component-card">
                <h3 class="component-title">输入框组件</h3>
                <input type="text" class="input" placeholder="请输入内容">
                <input type="email" class="input" placeholder="请输入邮箱">
                <input type="password" class="input" placeholder="请输入密码">
            </div>
            
            <div class="component-card">
                <h3 class="component-title">颜色调色板</h3>
                <div class="color-palette">
                    <div class="color-item">
                        <div class="color-swatch" style="background-color: var(--primary-color);"></div>
                        <div class="color-name">主色调</div>
                    </div>
                    <div class="color-item">
                        <div class="color-swatch" style="background-color: var(--secondary-color);"></div>
                        <div class="color-name">辅助色</div>
                    </div>
                    <div class="color-item">
                        <div class="color-swatch" style="background-color: var(--accent-color);"></div>
                        <div class="color-name">强调色</div>
                    </div>
                    <div class="color-item">
                        <div class="color-swatch" style="background-color: var(--success-color);"></div>
                        <div class="color-name">成功色</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>
"""
    
    # 保存预览页面
    with open("output/case3_tech_theme_preview.html", "w", encoding="utf-8") as f:
        f.write(preview_html)
    
    print(" 主题预览页面已生成: output/case3_tech_theme_preview.html")

# 生成主题预览
generate_theme_preview()

4.3 案例总结

实现效果:

  • 创建了完整的自定义主题配置
  • 集成到系统配置中
  • 生成了主题预览页面
  • 测试验证主题效果
  • 支持科技感的视觉效果

技术要点:

  • 主题配置的完整结构设计
  • 颜色系统的科学搭配
  • 视觉效果的特殊处理
  • 系统集成的标准流程

5. 案例4:新组件扩展开发

5.1 需求分析

业务场景:为电商系统添加商品对比功能组件

功能要求:

  • 支持最多4个商品对比
  • 对比维度:价格、规格、评分、功能
  • 可视化对比表格
  • 添加/移除商品操作

5.2 实现步骤

步骤1:设计组件结构

def design_comparison_component():
    """设计商品对比组件结构"""
    
    comparison_component = {
        "type": "product-comparison",
        "name": "商品对比组件",
        "description": "支持多商品对比的表格组件",
        "props": {
            "max_items": 4,
            "show_actions": True,
            "comparison_fields": [
                "price",
                "specifications", 
                "rating",
                "features",
                "warranty"
            ],
            "style": {
                "table_style": "bordered",
                "highlight_differences": True,
                "responsive": True
            }
        },
        "data_structure": {
            "products": [
                {
                    "id": "product_id",
                    "name": "商品名称",
                    "image": "商品图片URL",
                    "price": "价格",
                    "specifications": {
                        "color": "颜色",
                        "storage": "存储",
                        "size": "尺寸"
                    },
                    "rating": 4.5,
                    "features": ["特性1", "特性2"],
                    "warranty": "保修信息"
                }
            ]
        }
    }
    
    return comparison_component

# 设计组件
comparison_component = design_comparison_component()

步骤2:实现渲染逻辑

def implement_comparison_renderer():
    """实现商品对比组件渲染器"""
    
    # PNG图片渲染器
    def render_comparison_to_image(props, theme_colors, image_width=800):
        """渲染商品对比组件为PNG图片"""
        from PIL import Image, ImageDraw, ImageFont
        
        # 计算图片高度
        products = props.get("products", [])
        fields = props.get("comparison_fields", [])
        
        header_height = 60
        row_height = 80
        image_height = header_height + len(fields) * row_height + 20
        
        # 创建图片
        image = Image.new("RGB", (image_width, image_height), theme_colors["background"])
        draw = ImageDraw.Draw(image)
        
        # 加载字体
        try:
            title_font = ImageFont.truetype("arial.ttf", 16)
            text_font = ImageFont.truetype("arial.ttf", 12)
        except:
            title_font = ImageFont.load_default()
            text_font = ImageFont.load_default()
        
        # 绘制表头
        col_width = image_width // (len(products) + 1)
        
        # 绘制字段名列
        draw.rectangle([0, 0, col_width, header_height], 
                      fill=theme_colors["surface"], 
                      outline=theme_colors["border"])
        draw.text((10, 20), "对比项目", fill=theme_colors["text"], font=title_font)
        
        # 绘制商品列
        for i, product in enumerate(products):
            x = col_width * (i + 1)
            draw.rectangle([x, 0, x + col_width, header_height], 
                          fill=theme_colors["primary"], 
                          outline=theme_colors["border"])
            draw.text((x + 10, 20), product["name"][:10], 
                     fill=theme_colors["background"], font=title_font)
        
        # 绘制对比行
        for row_idx, field in enumerate(fields):
            y = header_height + row_idx * row_height
            
            # 绘制字段名
            draw.rectangle([0, y, col_width, y + row_height], 
                          fill=theme_colors["surface"], 
                          outline=theme_colors["border"])
            draw.text((10, y + 20), field, fill=theme_colors["text"], font=text_font)
            
            # 绘制商品数据
            for i, product in enumerate(products):
                x = col_width * (i + 1)
                draw.rectangle([x, y, x + col_width, y + row_height], 
                              fill=theme_colors["background"], 
                              outline=theme_colors["border"])
                
                # 根据字段类型显示数据
                if field == "price":
                    value = product.get("price", "N/A")
                elif field == "rating":
                    value = f"{product.get('rating', 0)}/5"
                elif field == "specifications":
                    specs = product.get("specifications", {})
                    value = f"{specs.get('color', 'N/A')} | {specs.get('storage', 'N/A')}"
                else:
                    value = str(product.get(field, "N/A"))
                
                draw.text((x + 10, y + 20), str(value)[:15], 
                         fill=theme_colors["text"], font=text_font)
        
        return image
    
    # Vue组件渲染器
    def render_comparison_to_vue(props, theme_name):
        """渲染商品对比组件为Vue代码"""
        
        vue_template = f"""
<template>
  <view class="product-comparison theme-{theme_name}">
    <view class="comparison-header">
      <text class="comparison-title">商品对比</text>
      <text class="comparison-subtitle">最多支持4个商品对比</text>
    </view>
    
    <view class="comparison-table">
      <view class="table-header">
        <view class="field-column">对比项目</view>
        <view 
          v-for="(product, index) in products" 
          :key="product.id"
          class="product-column"
        >
          <image :src="product.image" class="product-image" />
          <text class="product-name">{{ product.name }}</text>
          <button 
            class="remove-btn"
            @click="removeProduct(index)"
          >
            移除
          </button>
        </view>
      </view>
      
      <view 
        v-for="field in comparisonFields" 
        :key="field"
        class="table-row"
      >
        <view class="field-name">{{ getFieldName(field) }}</view>
        <view 
          v-for="product in products" 
          :key="product.id"
          class="field-value"
        >
          {{ getFieldValue(product, field) }}
        </view>
      </view>
    </view>
    
    <view class="comparison-actions">
      <button 
        class="add-product-btn"
        @click="addProduct"
        :disabled="products.length >= maxItems"
      >
        添加商品对比
      </button>
      <button class="clear-all-btn" @click="clearAll">
        清空对比
      </button>
    </view>
  </view>
</template>

<script>
export default {{
  name: 'ProductComparison',
  props: {{
    maxItems: {{
      type: Number,
      default: 4
    }},
    comparisonFields: {{
      type: Array,
      default: () => ['price', 'specifications', 'rating', 'features']
    }}
  }},
  data() {{
    return {{
      products: {json.dumps(props.get('products', []), ensure_ascii=False, indent=2)}
    }}
  }},
  methods: {{
    getFieldName(field) {{
      const fieldNames = {{
        'price': '价格',
        'specifications': '规格',
        'rating': '评分',
        'features': '特性',
        'warranty': '保修'
      }}
      return fieldNames[field] || field
    }},
    
    getFieldValue(product, field) {{
      if (field === 'price') {{
        return product.price || 'N/A'
      }} else if (field === 'rating') {{
        return `${{product.rating || 0}}/5`
      }} else if (field === 'specifications') {{
        const specs = product.specifications || {{}}
        return `${{specs.color || 'N/A'}} | ${{specs.storage || 'N/A'}}`
      }} else if (field === 'features') {{
        return (product.features || []).join(', ')
      }} else {{
        return product[field] || 'N/A'
      }}
    }},
    
    addProduct() {{
      if (this.products.length < this.maxItems) {{
        // 跳转到商品选择页面
        uni.navigateTo({{
          url: '/pages/product/select?action=compare'
        }})
      }}
    }},
    
    removeProduct(index) {{
      this.products.splice(index, 1)
    }},
    
    clearAll() {{
      this.products = []
    }}
  }}
}}
</script>

<style lang="scss" scoped>
.product-comparison {{
  padding: 20px;
  background-color: var(--background-color);
}}

.comparison-header {{
  text-align: center;
  margin-bottom: 20px;
}}

.comparison-title {{
  font-size: 24px;
  font-weight: bold;
  color: var(--text-color);
}}

.comparison-subtitle {{
  font-size: 14px;
  color: var(--text-secondary-color);
}}

.comparison-table {{
  border: 1px solid var(--border-color);
  border-radius: 8px;
  overflow: hidden;
}}

.table-header {{
  display: flex;
  background-color: var(--surface-color);
}}

.field-column {{
  flex: 1;
  padding: 15px;
  font-weight: bold;
  color: var(--text-color);
  border-right: 1px solid var(--border-color);
}}

.product-column {{
  flex: 1;
  padding: 15px;
  text-align: center;
  border-right: 1px solid var(--border-color);
}}

.product-image {{
  width: 60px;
  height: 60px;
  border-radius: 4px;
  margin-bottom: 10px;
}}

.product-name {{
  font-size: 14px;
  color: var(--text-color);
  margin-bottom: 10px;
}}

.remove-btn {{
  background-color: var(--error-color);
  color: white;
  border: none;
  padding: 5px 10px;
  border-radius: 4px;
  font-size: 12px;
}}

.table-row {{
  display: flex;
  border-top: 1px solid var(--border-color);
}}

.field-name {{
  flex: 1;
  padding: 15px;
  background-color: var(--surface-color);
  color: var(--text-color);
  border-right: 1px solid var(--border-color);
}}

.field-value {{
  flex: 1;
  padding: 15px;
  color: var(--text-color);
  border-right: 1px solid var(--border-color);
}}

.comparison-actions {{
  margin-top: 20px;
  text-align: center;
}}

.add-product-btn {{
  background-color: var(--primary-color);
  color: var(--background-color);
  border: none;
  padding: 12px 24px;
  border-radius: 6px;
  margin-right: 10px;
}}

.clear-all-btn {{
  background-color: var(--secondary-color);
  color: var(--text-color);
  border: none;
  padding: 12px 24px;
  border-radius: 6px;
}}
</style>
"""
        
        return vue_template
    
    return render_comparison_to_image, render_comparison_to_vue

# 实现渲染器
render_to_image, render_to_vue = implement_comparison_renderer()

步骤3:集成到系统

def integrate_comparison_component():
    """集成商品对比组件到系统"""
    
    # 1. 更新组件配置
    component_config = {
        "product-comparison": {
            "name": "商品对比组件",
            "description": "支持多商品对比的表格组件",
            "category": "interactive",
            "props": {
                "max_items": {
                    "type": "number",
                    "default": 4,
                    "description": "最大对比商品数量"
                },
                "comparison_fields": {
                    "type": "array",
                    "default": ["price", "specifications", "rating", "features"],
                    "description": "对比字段列表"
                },
                "products": {
                    "type": "array",
                    "default": [],
                    "description": "商品数据列表"
                }
            },
            "renderers": {
                "image": "render_comparison_to_image",
                "vue": "render_comparison_to_vue"
            }
        }
    }
    
    # 2. 更新ui_tokens.json
    tokens_path = "config/ui_tokens.json"
    with open(tokens_path, "r", encoding="utf-8") as f:
        tokens = json.load(f)
    
    if "components" not in tokens:
        tokens["components"] = {}
    
    tokens["components"].update(component_config)
    
    with open(tokens_path, "w", encoding="utf-8") as f:
        json.dump(tokens, f, ensure_ascii=False, indent=2)
    
    print(" 商品对比组件已集成到系统")
    
    # 3. 更新渲染器代码
    update_renderer_code()
    
    return component_config

def update_renderer_code():
    """更新渲染器代码"""
    
    # 更新UIRenderer类
    renderer_update = '''
    def _render_product_comparison(self, props: Dict) -> str:
        """渲染商品对比组件"""
        max_items = props.get("max_items", 4)
        products = props.get("products", [])
        fields = props.get("comparison_fields", ["price", "specifications", "rating"])
        
        # 生成对比表格
        table_html = f"""
        <div class="product-comparison">
            <h3>商品对比</h3>
            <table class="comparison-table">
                <thead>
                    <tr>
                        <th>对比项目</th>
                        {''.join([f'<th>{p.get("name", "商品")}</th>' for p in products])}
                    </tr>
                </thead>
                <tbody>
                    {''.join([f'''
                    <tr>
                        <td>{field}</td>
                        {''.join([f'<td>{self._get_field_value(p, field)}</td>' for p in products])}
                    </tr>
                    ''' for field in fields])}
                </tbody>
            </table>
        </div>
        """
        
        return table_html
    
    def _get_field_value(self, product: Dict, field: str) -> str:
        """获取商品字段值"""
        if field == "price":
            return product.get("price", "N/A")
        elif field == "rating":
            return f"{product.get('rating', 0)}/5"
        elif field == "specifications":
            specs = product.get("specifications", {})
            return f"{specs.get('color', 'N/A')} | {specs.get('storage', 'N/A')}"
        else:
            return str(product.get(field, "N/A"))
    '''
    
    print(" 渲染器代码已更新")

# 集成组件
component_config = integrate_comparison_component()

步骤4:测试新组件

def test_comparison_component():
    """测试商品对比组件"""
    
    # 准备测试数据
    test_products = [
        {
            "id": "iphone15",
            "name": "iPhone 15 Pro",
            "image": "/static/images/iphone15.jpg",
            "price": "¥7999",
            "specifications": {
                "color": "深空黑色",
                "storage": "256GB",
                "size": "6.1英寸"
            },
            "rating": 4.8,
            "features": ["A17 Pro芯片", "48MP摄像头", "USB-C接口"],
            "warranty": "1年保修"
        },
        {
            "id": "iphone14",
            "name": "iPhone 14 Pro",
            "image": "/static/images/iphone14.jpg",
            "price": "¥6999",
            "specifications": {
                "color": "深空黑色",
                "storage": "256GB",
                "size": "6.1英寸"
            },
            "rating": 4.7,
            "features": ["A16芯片", "48MP摄像头", "Lightning接口"],
            "warranty": "1年保修"
        },
        {
            "id": "samsung_s23",
            "name": "Samsung Galaxy S23",
            "image": "/static/images/s23.jpg",
            "price": "¥5999",
            "specifications": {
                "color": "幻影黑",
                "storage": "256GB",
                "size": "6.1英寸"
            },
            "rating": 4.6,
            "features": ["Snapdragon 8 Gen 2", "50MP摄像头", "USB-C接口"],
            "warranty": "1年保修"
        }
    ]
    
    # 创建测试DSL
    test_dsl = {
        "page": {
            "name": "product_comparison_test",
            "theme": "obsidian-gold",
            "sections": [
                {
                    "type": "topbar",
                    "props": {
                        "title": "商品对比测试",
                        "logo": "🛍️",
                        "actions": ["back"]
                    }
                },
                {
                    "type": "product-comparison",
                    "props": {
                        "max_items": 4,
                        "comparison_fields": ["price", "specifications", "rating", "features"],
                        "products": test_products
                    }
                }
            ]
        }
    }
    
    # 测试渲染
    print("🧪 测试商品对比组件...")
    
    # 测试PNG渲染
    try:
        theme_colors = {
            "background": "#0E0E0E",
            "surface": "#1A1A1A", 
            "text": "#FFFFFF",
            "border": "#333333",
            "primary": "#FFD700"
        }
        
        image = render_to_image(test_dsl["page"]["sections"][1]["props"], theme_colors)
        image.save("output/case4_comparison_test.png")
        print(" PNG渲染测试成功")
    except Exception as e:
        print(f" PNG渲染测试失败: {e}")
    
    # 测试Vue渲染
    try:
        vue_code = render_to_vue(test_dsl["page"]["sections"][1]["props"], "obsidian-gold")
        with open("output/case4_comparison_test.vue", "w", encoding="utf-8") as f:
            f.write(vue_code)
        print(" Vue渲染测试成功")
    except Exception as e:
        print(f" Vue渲染测试失败: {e}")
    
    # 保存测试DSL
    with open("output/case4_comparison_dsl.json", "w", encoding="utf-8") as f:
        json.dump(test_dsl, f, ensure_ascii=False, indent=2)
    
    print(" 商品对比组件测试完成")

# 测试组件
test_comparison_component()

4.4 案例总结

实现效果:

  • 设计了完整的商品对比组件结构
  • 实现了PNG和Vue两种渲染方式
  • 集成到系统配置中
  • 通过功能测试验证
  • 支持灵活的数据配置

技术要点:

  • 新组件的设计模式
  • 多格式渲染器实现
  • 系统集成标准流程
  • 组件测试验证方法

6. 案例总结与最佳实践

6.1 案例总结

通过4个实战案例,我们展示了AI UI生成系统在不同场景下的应用:

  1. 电商首页生成:展示了完整的端到端流程
  2. 商品详情页:演示了数据集成和功能测试
  3. 自定义主题开发:展示了主题扩展能力
  4. 新组件扩展:演示了系统扩展机制

6.2 最佳实践

6.2.1 提示词优化

# 好的提示词示例
good_prompt = """
设计一个{page_type},包含以下功能:
1. {feature_1}:{description_1}
2. {feature_2}:{description_2}
3. {feature_3}:{description_3}

要求:
- 使用{theme}主题
- 布局{layout_style}
- 体现{design_style}风格
"""

# 避免的提示词
bad_prompt = "设计一个页面"  # 太简单

6.2.2 数据结构化

# 推荐的数据结构
structured_data = {
    "products": [
        {
            "id": "unique_id",
            "name": "商品名称",
            "price": "价格",
            "specifications": {
                "color": "颜色",
                "storage": "存储"
            },
            "metadata": {
                "created_at": "2024-01-01",
                "updated_at": "2024-01-01"
            }
        }
    ]
}

6.2.3 错误处理

def robust_api_call(url, data, max_retries=3):
    """健壮的API调用"""
    for attempt in range(max_retries):
        try:
            response = requests.post(url, json=data, timeout=30)
            if response.status_code == 200:
                return response.json()
            else:
                print(f"API调用失败,状态码: {response.status_code}")
        except requests.exceptions.RequestException as e:
            print(f"API调用异常: {e}")
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)  # 指数退避
    
    return None

6.3 扩展建议

  1. 性能优化:实现缓存机制,减少重复计算
  2. 用户体验:添加进度指示器和错误提示
  3. 功能扩展:支持更多组件类型和主题
  4. 集成能力:提供更多API接口和SDK

通过掌握这些实战案例,您可以深入理解AI UI生成系统的实际应用,并能够根据具体需求进行定制化开发。每个案例都提供了完整的代码实现和最佳实践,可以作为您项目开发的参考模板。

Prev
生产环境运维
Next
性能优化指南