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

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

Vue页面渲染系统

1. 概述

Vue页面渲染是AI UI生成系统的重要输出模块,负责将结构化的UI-DSL数据转换为可运行的Vue页面代码。本章详细介绍基于Jinja2模板引擎的Vue渲染系统实现,包括组件生成、UniApp框架支持、主题系统集成和代码优化技巧。

2. Vue渲染引擎架构

2.1 整体架构图

UI-DSL -> Vue渲染器 -> 模板引擎 -> Vue组件 -> 可运行代码
    ↓        ↓         ↓         ↓         ↓
JSON数据   VueRenderer  Jinja2   组件生成   最终Vue文件

2.2 核心渲染类

系统使用 VueRenderer 类封装Vue页面渲染逻辑:

class VueRenderer:
    """Vue页面渲染器 - 核心Vue渲染类"""
    
    def __init__(self, config_path: str = "config/model_config.yaml", 
                 tokens_path: str = "config/ui_tokens.json"):
        """初始化渲染器"""
        with open(config_path, 'r', encoding='utf-8') as f:
            self.config = yaml.safe_load(f)
        
        with open(tokens_path, 'r', encoding='utf-8') as f:
            self.tokens = json.load(f)
        
        # 加载Vue模板
        self.templates = self._load_templates()

3. 模板系统设计

3.1 基础模板结构

def _load_templates(self) -> Dict[str, str]:
    """加载Vue模板"""
    return {
        "base": """
<template>
  <view class="page" :class="themeClass">
    {% for section in sections %}
    {% if section.type == 'topbar' %}
    <TopBar 
      :title="'{{ section.props.get('title', '') }}'"
      :logo="'{{ section.props.get('logo', '') }}'"
      :actions="{{ section.props.get('actions', []) }}"
    />
    {% elif section.type == 'tabs' %}
    <Tabs 
      :items="{{ section.props.get('items', []) }}"
      :active="{{ section.props.get('active', 0) }}"
      @change="onTabChange"
    />
    {% elif section.type == 'card-list' %}
    <CardList 
      :columns="{{ section.props.get('columns', 2) }}"
      :cards="productCards"
      :card-type="'{{ section.props.get('card', {}).get('type', 'product-card') }}'"
    />
    {% elif section.type == 'carousel' %}
    <Carousel 
      :images="carouselImages"
      :autoplay="true"
    />
    {% elif section.type == 'price' %}
    <Price 
      :value="'{{ section.props.get('value', '¥0') }}'"
      :original="'{{ section.props.get('original', '') }}'"
    />
    {% elif section.type == 'seller' %}
    <Seller 
      :name="'{{ section.props.get('name', '卖家') }}'"
      :trust="'{{ section.props.get('trust', '') }}'"
    />
    {% elif section.type == 'proof' %}
    <Proof 
      :items="{{ section.props.get('items', []) }}"
    />
    {% elif section.type == 'cta' %}
    <CTA 
      :buttons="{{ section.props.get('buttons', []) }}"
      @button-click="onButtonClick"
    />
    {% elif section.type == 'tabbar' %}
    <TabBar 
      :items="{{ section.props.get('items', []) }}"
      :active="activeTab"
      @change="onTabBarChange"
    />
    {% elif section.type == 'user-info' %}
    <UserInfo 
      :name="'{{ section.props.get('name', '用户名') }}'"
      :level="'{{ section.props.get('level', '') }}'"
      :avatar="true"
    />
    {% elif section.type == 'menu-list' %}
    <MenuList 
      :items="{{ section.props.get('items', []) }}"
      @item-click="onMenuClick"
    />
    {% elif section.type == 'form' %}
    <Form 
      :fields="{{ section.props.get('fields', []) }}"
      @submit="onFormSubmit"
    />
    {% elif section.type == 'filters' %}
    <Filters 
      :items="{{ section.props.get('items', []) }}"
      @filter-change="onFilterChange"
    />
    {% endif %}
    {% endfor %}
  </view>
</template>

<script>
export default {
  name: '{{ page_name }}',
  data() {
    return {
      theme: '{{ theme }}',
      activeTab: 0,
      productCards: [
        {
          id: 1,
          title: '商品标题1',
          price: '¥1999',
          image: '/static/images/product1.jpg',
          brand: '品牌A'
        },
        {
          id: 2,
          title: '商品标题2',
          price: '¥2999',
          image: '/static/images/product2.jpg',
          brand: '品牌B'
        }
      ],
      carouselImages: [
        '/static/images/banner1.jpg',
        '/static/images/banner2.jpg',
        '/static/images/banner3.jpg'
      ]
    }
  },
  computed: {
    themeClass() {
      return `theme-${this.theme}`
    }
  },
  methods: {
    onTabChange(index) {
      this.activeTab = index
      console.log('Tab changed:', index)
    },
    onTabBarChange(index) {
      this.activeTab = index
      console.log('TabBar changed:', index)
    },
    onButtonClick(button) {
      console.log('Button clicked:', button)
      // 处理按钮点击事件
      if (button === '立即下单') {
        this.goToOrder()
      } else if (button === '联系卖家') {
        this.contactSeller()
      }
    },
    onMenuClick(item) {
      console.log('Menu clicked:', item)
      // 处理菜单点击事件
    },
    onFormSubmit(formData) {
      console.log('Form submitted:', formData)
      // 处理表单提交
    },
    onFilterChange(filter) {
      console.log('Filter changed:', filter)
      // 处理筛选变化
    },
    goToOrder() {
      // 跳转到订单页面
      uni.navigateTo({
        url: '/pages/order/order'
      })
    },
    contactSeller() {
      // 联系卖家
      uni.showModal({
        title: '联系卖家',
        content: '是否要联系卖家?',
        success: (res) => {
          if (res.confirm) {
            // 执行联系卖家逻辑
          }
        }
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.page {
  min-height: 100vh;
  background-color: var(--bg-color);
  color: var(--text-color);
}

// 主题样式
.theme-obsidian-gold {
  --primary-color: #FFD700;
  --secondary-color: #B8860B;
  --bg-color: #0E0E0E;
  --surface-color: #1A1A1A;
  --text-color: #FFFFFF;
  --text-secondary-color: #CCCCCC;
  --border-color: #333333;
  --accent-color: #FF6B35;
  --success-color: #4CAF50;
  --warning-color: #FF9800;
  --error-color: #F44336;
}

.theme-silver-white {
  --primary-color: #6B7280;
  --secondary-color: #9CA3AF;
  --bg-color: #FFFFFF;
  --surface-color: #F9FAFB;
  --text-color: #111827;
  --text-secondary-color: #6B7280;
  --border-color: #E5E7EB;
  --accent-color: #3B82F6;
  --success-color: #10B981;
  --warning-color: #F59E0B;
  --error-color: #EF4444;
}

.theme-minimal {
  --primary-color: #000000;
  --secondary-color: #666666;
  --bg-color: #FFFFFF;
  --surface-color: #FFFFFF;
  --text-color: #000000;
  --text-secondary-color: #666666;
  --border-color: #E0E0E0;
  --accent-color: #007AFF;
  --success-color: #34C759;
  --warning-color: #FF9500;
  --error-color: #FF3B30;
}

// 组件样式
.topbar {
  height: 44px;
  background-color: var(--surface-color);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 16px;
  border-bottom: 1px solid var(--border-color);
}

.tabs {
  height: 40px;
  background-color: var(--surface-color);
  display: flex;
  align-items: center;
  padding: 0 16px;
}

.card-list {
  padding: 16px;
  display: grid;
  gap: 16px;
}

.card-list.columns-1 {
  grid-template-columns: 1fr;
}

.card-list.columns-2 {
  grid-template-columns: 1fr 1fr;
}

.card {
  background-color: var(--surface-color);
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.carousel {
  height: 200px;
  margin: 16px;
  border-radius: 8px;
  overflow: hidden;
}

.price {
  padding: 16px;
  background-color: var(--surface-color);
  margin: 16px;
  border-radius: 8px;
}

.seller {
  padding: 16px;
  background-color: var(--surface-color);
  margin: 16px;
  border-radius: 8px;
}

.proof {
  padding: 16px;
  background-color: var(--surface-color);
  margin: 16px;
  border-radius: 8px;
}

.cta {
  padding: 16px;
  display: flex;
  gap: 8px;
}

.button {
  flex: 1;
  height: 44px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  font-weight: 500;
  border: none;
  cursor: pointer;
}

.button.primary {
  background-color: var(--primary-color);
  color: var(--bg-color);
}

.button.secondary {
  background-color: var(--secondary-color);
  color: var(--text-color);
}

.tabbar {
  height: 60px;
  background-color: var(--surface-color);
  display: flex;
  align-items: center;
  justify-content: space-around;
  border-top: 1px solid var(--border-color);
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
}

.tabbar-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  cursor: pointer;
}

.tabbar-item.active {
  color: var(--primary-color);
}

.user-info {
  padding: 16px;
  background-color: var(--surface-color);
  margin: 16px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  gap: 16px;
}

.menu-list {
  padding: 16px;
}

.menu-item {
  height: 50px;
  background-color: var(--surface-color);
  margin-bottom: 8px;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 16px;
  cursor: pointer;
}

.form {
  padding: 16px;
}

.form-field {
  margin-bottom: 16px;
}

.form-field input {
  width: 100%;
  height: 44px;
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 0 16px;
  background-color: var(--surface-color);
  color: var(--text-color);
}

.filters {
  height: 40px;
  background-color: var(--surface-color);
  display: flex;
  align-items: center;
  padding: 0 16px;
  gap: 8px;
}

.filter-item {
  padding: 8px 16px;
  border: 1px solid var(--border-color);
  border-radius: 4px;
  cursor: pointer;
}
</style>
""",
        
        "components": """
// TopBar组件
<template>
  <view class="topbar">
    <view class="topbar-left">
      <text v-if="logo" class="logo">{{ logo }}</text>
      <text v-if="title" class="title">{{ title }}</text>
    </view>
    <view class="topbar-right">
      <text v-for="action in actions" :key="action" class="action">
        {{ getActionIcon(action) }}
      </text>
    </view>
  </view>
</template>

<script>
export default {
  name: 'TopBar',
  props: {
    title: String,
    logo: String,
    actions: Array
  },
  methods: {
    getActionIcon(action) {
      const icons = {
        'search': '',
        'bell': '🔔',
        'share': '📤',
        'favorite': '❤️',
        'settings': '️'
      }
      return icons[action] || '📱'
    }
  }
}
</script>

// Tabs组件
<template>
  <view class="tabs">
    <view 
      v-for="(item, index) in items" 
      :key="index"
      class="tab-item"
      :class="{ active: index === active }"
      @click="$emit('change', index)"
    >
      {{ item }}
    </view>
  </view>
</template>

<script>
export default {
  name: 'Tabs',
  props: {
    items: Array,
    active: Number
  }
}
</script>

// CardList组件
<template>
  <view class="card-list" :class="`columns-${columns}`">
    <view v-for="card in cards" :key="card.id" class="card">
      <image :src="card.image" class="card-image" />
      <text class="card-title">{{ card.title }}</text>
      <text class="card-price">{{ card.price }}</text>
    </view>
  </view>
</template>

<script>
export default {
  name: 'CardList',
  props: {
    columns: Number,
    cards: Array,
    cardType: String
  }
}
</script>

// 其他组件类似...
"""
    }

3.2 模板变量系统

# 模板变量配置
template_variables = {
    "page_name": "页面名称",
    "theme": "主题名称",
    "sections": "页面区块列表",
    "component_props": "组件属性",
    "event_handlers": "事件处理器"
}

# 模板过滤器
template_filters = {
    "safe": "安全HTML输出",
    "default": "默认值处理",
    "join": "数组连接",
    "upper": "大写转换",
    "lower": "小写转换"
}

4. 组件渲染实现

4.1 导航类组件渲染

4.1.1 TopBar组件渲染

def _render_topbar(self, props: Dict) -> str:
    """渲染顶部栏组件"""
    title = props.get("title", "")
    logo = props.get("logo", "")
    actions = props.get("actions", [])
    
    return f"""
        <TopBar 
          title="{title}"
          logo="{logo}"
          :actions="{actions}"
        />"""

4.1.2 Tabs组件渲染

def _render_tabs(self, props: Dict) -> str:
    """渲染标签页组件"""
    items = props.get("items", [])
    active = props.get("active", 0)
    
    return f"""
        <Tabs 
          :items="{items}"
          :active="{active}"
          @change="onTabChange"
        />"""

4.1.3 TabBar组件渲染

def _render_tabbar(self, props: Dict) -> str:
    """渲染底部导航栏组件"""
    items = props.get("items", [])
    
    return f"""
        <TabBar 
          :items="{items}"
          :active="activeTab"
          @change="onTabBarChange"
        />"""

4.2 内容展示类组件渲染

4.2.1 CardList组件渲染

def _render_card_list(self, props: Dict) -> str:
    """渲染卡片列表组件"""
    columns = props.get("columns", 2)
    card_type = props.get("card", {}).get("type", "product-card")
    
    return f"""
        <CardList 
          :columns="{columns}"
          :cards="productCards"
          card-type="{card_type}"
        />"""

4.2.2 Carousel组件渲染

def _render_carousel(self, props: Dict) -> str:
    """渲染轮播图组件"""
    images = props.get("images", 3)
    
    return f"""
        <Carousel 
          :images="carouselImages"
          :autoplay="true"
        />"""

4.2.3 Price组件渲染

def _render_price(self, props: Dict) -> str:
    """渲染价格区域组件"""
    value = props.get("value", "¥0")
    original = props.get("original", "")
    
    return f"""
        <Price 
          value="{value}"
          original="{original}"
        />"""

4.3 交互类组件渲染

4.3.1 CTA组件渲染

def _render_cta(self, props: Dict) -> str:
    """渲染行动按钮组件"""
    buttons = props.get("buttons", [])
    
    return f"""
        <CTA 
          :buttons="{buttons}"
          @button-click="onButtonClick"
        />"""

4.3.2 Form组件渲染

def _render_form(self, props: Dict) -> str:
    """渲染表单组件"""
    fields = props.get("fields", [])
    
    return f"""
        <Form 
          :fields="{fields}"
          @submit="onFormSubmit"
        />"""

4.4 用户信息类组件渲染

4.4.1 UserInfo组件渲染

def _render_user_info(self, props: Dict) -> str:
    """渲染用户信息组件"""
    name = props.get("name", "用户名")
    level = props.get("level", "")
    
    return f"""
        <UserInfo 
          name="{name}"
          level="{level}"
          :avatar="true"
        />"""

4.4.2 MenuList组件渲染

def _render_menu_list(self, props: Dict) -> str:
    """渲染菜单列表组件"""
    items = props.get("items", [])
    
    return f"""
        <MenuList 
          :items="{items}"
          @item-click="onMenuClick"
        />"""

5. 主题系统集成

5.1 主题颜色获取

def _get_theme_colors(self, theme: str) -> Dict[str, str]:
    """获取主题颜色配置"""
    return self.tokens["themes"].get(theme, self.tokens["themes"]["obsidian-gold"])["colors"]

5.2 主题样式生成

def _generate_theme_styles(self, theme: str) -> str:
    """生成主题样式"""
    theme_colors = self._get_theme_colors(theme)
    
    css_variables = []
    for color_name, color_value in theme_colors.items():
        css_variables.append(f"  --{color_name.replace('_', '-')}: {color_value};")
    
    css_variables_str = "\n".join(css_variables)
    
    return f"""
.theme-{theme} {{
{css_variables_str}
}}"""

5.3 动态主题切换

def _generate_theme_switcher(self) -> str:
    """生成主题切换器"""
    return """
// 主题切换方法
switchTheme(themeName) {
  this.theme = themeName;
  // 保存主题偏好
  uni.setStorageSync('selected_theme', themeName);
},

// 初始化主题
initTheme() {
  const savedTheme = uni.getStorageSync('selected_theme');
  if (savedTheme) {
    this.theme = savedTheme;
  }
}"""

6. 事件处理系统

6.1 事件处理器生成

def _generate_event_handlers(self, sections: List[Dict]) -> str:
    """生成事件处理器"""
    handlers = []
    
    for section in sections:
        section_type = section.get("type")
        
        if section_type == "tabs":
            handlers.append("""
    onTabChange(index) {
      this.activeTab = index;
      console.log('Tab changed:', index);
    }""")
        
        elif section_type == "tabbar":
            handlers.append("""
    onTabBarChange(index) {
      this.activeTab = index;
      console.log('TabBar changed:', index);
    }""")
        
        elif section_type == "cta":
            handlers.append("""
    onButtonClick(button) {
      console.log('Button clicked:', button);
      if (button === '立即下单') {
        this.goToOrder();
      } else if (button === '联系卖家') {
        this.contactSeller();
      }
    }""")
        
        elif section_type == "menu-list":
            handlers.append("""
    onMenuClick(item) {
      console.log('Menu clicked:', item);
      // 处理菜单点击事件
    }""")
        
        elif section_type == "form":
            handlers.append("""
    onFormSubmit(formData) {
      console.log('Form submitted:', formData);
      // 处理表单提交
    }""")
        
        elif section_type == "filters":
            handlers.append("""
    onFilterChange(filter) {
      console.log('Filter changed:', filter);
      // 处理筛选变化
    }""")
    
    return "".join(handlers)

6.2 业务逻辑方法

def _generate_business_methods(self) -> str:
    """生成业务逻辑方法"""
    return """
    // 业务逻辑方法
    goToOrder() {
      // 跳转到订单页面
      uni.navigateTo({
        url: '/pages/order/order'
      });
    },
    
    contactSeller() {
      // 联系卖家
      uni.showModal({
        title: '联系卖家',
        content: '是否要联系卖家?',
        success: (res) => {
          if (res.confirm) {
            // 执行联系卖家逻辑
          }
        }
      });
    },
    
    goToProductDetail(productId) {
      // 跳转到商品详情页
      uni.navigateTo({
        url: `/pages/product/detail?id=${productId}`
      });
    },
    
    addToCart(productId) {
      // 添加到购物车
      uni.showToast({
        title: '已添加到购物车',
        icon: 'success'
      });
    }"""

7. 数据绑定系统

7.1 响应式数据生成

def _generate_reactive_data(self, sections: List[Dict]) -> str:
    """生成响应式数据"""
    data_items = [
        "theme: 'obsidian-gold'",
        "activeTab: 0"
    ]
    
    # 根据组件类型生成相应的数据
    for section in sections:
        section_type = section.get("type")
        
        if section_type == "card-list":
            data_items.append("""
      productCards: [
        {
          id: 1,
          title: '商品标题1',
          price: '¥1999',
          image: '/static/images/product1.jpg',
          brand: '品牌A'
        },
        {
          id: 2,
          title: '商品标题2',
          price: '¥2999',
          image: '/static/images/product2.jpg',
          brand: '品牌B'
        }
      ]""")
        
        elif section_type == "carousel":
            data_items.append("""
      carouselImages: [
        '/static/images/banner1.jpg',
        '/static/images/banner2.jpg',
        '/static/images/banner3.jpg'
      ]""")
        
        elif section_type == "form":
            data_items.append("""
      formData: {
        title: '',
        price: '',
        description: '',
        images: []
      }""")
    
    return ",\n".join(data_items)

7.2 计算属性生成

def _generate_computed_properties(self) -> str:
    """生成计算属性"""
    return """
    computed: {
      themeClass() {
        return `theme-${this.theme}`;
      },
      
      filteredProducts() {
        // 根据筛选条件过滤商品
        return this.productCards.filter(product => {
          // 筛选逻辑
          return true;
        });
      },
      
      totalPrice() {
        // 计算总价
        return this.productCards.reduce((total, product) => {
          return total + parseFloat(product.price.replace('¥', ''));
        }, 0);
      }
    }"""

8. 生命周期管理

8.1 生命周期钩子

def _generate_lifecycle_hooks(self) -> str:
    """生成生命周期钩子"""
    return """
    // 生命周期钩子
    onLoad(options) {
      // 页面加载时执行
      console.log('页面加载', options);
      this.initTheme();
    },
    
    onShow() {
      // 页面显示时执行
      console.log('页面显示');
    },
    
    onReady() {
      // 页面初次渲染完成时执行
      console.log('页面渲染完成');
    },
    
    onHide() {
      // 页面隐藏时执行
      console.log('页面隐藏');
    },
    
    onUnload() {
      // 页面卸载时执行
      console.log('页面卸载');
    }"""

8.2 页面初始化

def _generate_initialization(self) -> str:
    """生成初始化方法"""
    return """
    // 初始化方法
    initTheme() {
      const savedTheme = uni.getStorageSync('selected_theme');
      if (savedTheme) {
        this.theme = savedTheme;
      }
    },
    
    loadData() {
      // 加载页面数据
      this.loadProducts();
      this.loadUserInfo();
    },
    
    loadProducts() {
      // 加载商品数据
      // 模拟API调用
      setTimeout(() => {
        this.productCards = [
          // 商品数据
        ];
      }, 1000);
    },
    
    loadUserInfo() {
      // 加载用户信息
      // 模拟API调用
    }"""

9. 主渲染流程

9.1 渲染主函数

def render(self, dsl: Dict[str, Any]) -> str:
    """渲染UI-DSL为Vue页面"""
    # 获取页面信息
    page = dsl.get("page", {})
    page_name = page.get("name", "GeneratedPage")
    theme = page.get("theme", "obsidian-gold")
    sections = page.get("sections", [])
    
    # 使用Jinja2模板渲染
    template = Template(self.templates["base"])
    
    return template.render(
        page_name=page_name,
        theme=theme,
        sections=sections
    )

9.2 渲染流程优化

class VueRenderOptimizer:
    """Vue渲染优化器"""
    
    def __init__(self):
        self.optimization_config = {
            "minify_output": True,
            "remove_comments": True,
            "optimize_imports": True,
            "cache_templates": True
        }
    
    def optimize_vue_code(self, vue_code: str) -> str:
        """优化Vue代码"""
        optimized_code = vue_code
        
        # 移除注释
        if self.optimization_config["remove_comments"]:
            optimized_code = self._remove_comments(optimized_code)
        
        # 压缩空白
        if self.optimization_config["minify_output"]:
            optimized_code = self._minify_whitespace(optimized_code)
        
        return optimized_code
    
    def _remove_comments(self, code: str) -> str:
        """移除注释"""
        import re
        # 移除单行注释
        code = re.sub(r'//.*$', '', code, flags=re.MULTILINE)
        # 移除多行注释
        code = re.sub(r'/\*.*?\*/', '', code, flags=re.DOTALL)
        return code
    
    def _minify_whitespace(self, code: str) -> str:
        """压缩空白"""
        import re
        # 压缩多个空白字符
        code = re.sub(r'\s+', ' ', code)
        # 移除不必要的空白
        code = re.sub(r'\s*([{}();,=])\s*', r'\1', code)
        return code

10. 组件库集成

10.1 组件导入系统

def _generate_component_imports(self, sections: List[Dict]) -> str:
    """生成组件导入"""
    used_components = set()
    
    for section in sections:
        section_type = section.get("type")
        if section_type:
            used_components.add(section_type)
    
    imports = []
    for component in used_components:
        imports.append(f"import {component} from '@/components/{component}.vue';")
    
    return "\n".join(imports)

10.2 组件注册

def _generate_component_registration(self, sections: List[Dict]) -> str:
    """生成组件注册"""
    used_components = set()
    
    for section in sections:
        section_type = section.get("type")
        if section_type:
            used_components.add(section_type)
    
    components = {}
    for component in used_components:
        components[component] = component
    
    return f"""
    components: {{
        {', '.join([f'{comp}: {comp}' for comp in used_components])}
    }}"""

11. 渲染脚本使用

11.1 命令行渲染

# 基础渲染命令
python render/render_to_vue.py \
    --dsl output/ui_design.json \
    --output output/ui_design.vue \
    --config config/model_config.yaml \
    --tokens config/ui_tokens.json

# 自定义框架渲染
python render/render_to_vue.py \
    --dsl output/ui_design.json \
    --output output/ui_design.vue \
    --framework uniapp

# 批量渲染
python render/render_to_vue.py \
    --dsl-dir output/dsl/ \
    --output-dir output/vue/ \
    --batch

11.2 渲染脚本主函数

def main():
    """主函数"""
    parser = argparse.ArgumentParser(description="UI-DSL Vue页面渲染")
    parser.add_argument("--dsl", type=str, required=True,
                       help="UI-DSL文件路径")
    parser.add_argument("--output", type=str, required=True,
                       help="输出Vue文件路径")
    parser.add_argument("--config", type=str, default="config/model_config.yaml",
                       help="配置文件路径")
    parser.add_argument("--tokens", type=str, default="config/ui_tokens.json",
                       help="UI令牌文件路径")
    parser.add_argument("--framework", type=str, default="uniapp",
                       help="Vue框架类型")
    parser.add_argument("--optimize", action="store_true",
                       help="优化输出代码")
    
    args = parser.parse_args()
    
    # 加载DSL
    with open(args.dsl, 'r', encoding='utf-8') as f:
        dsl = json.load(f)
    
    # 创建渲染器
    renderer = VueRenderer(args.config, args.tokens)
    
    # 渲染Vue页面
    vue_content = renderer.render(dsl)
    
    # 优化代码(如果启用)
    if args.optimize:
        optimizer = VueRenderOptimizer()
        vue_content = optimizer.optimize_vue_code(vue_content)
    
    # 保存Vue文件
    output_path = Path(args.output)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(vue_content)
    
    logger.info(f"Vue页面已保存到: {output_path}")

if __name__ == "__main__":
    main()

12. 代码质量保证

12.1 代码验证

class VueCodeValidator:
    """Vue代码验证器"""
    
    def __init__(self):
        self.validation_rules = {
            "syntax_check": True,
            "component_check": True,
            "event_check": True,
            "data_check": True
        }
    
    def validate_vue_code(self, vue_code: str) -> Dict[str, bool]:
        """验证Vue代码"""
        validation_results = {
            "syntax_valid": False,
            "components_valid": False,
            "events_valid": False,
            "data_valid": False
        }
        
        # 语法检查
        if self._check_syntax(vue_code):
            validation_results["syntax_valid"] = True
        
        # 组件检查
        if self._check_components(vue_code):
            validation_results["components_valid"] = True
        
        # 事件检查
        if self._check_events(vue_code):
            validation_results["events_valid"] = True
        
        # 数据检查
        if self._check_data(vue_code):
            validation_results["data_valid"] = True
        
        return validation_results
    
    def _check_syntax(self, vue_code: str) -> bool:
        """检查语法"""
        # 基本的Vue语法检查
        required_sections = ["<template>", "<script>", "<style>"]
        return all(section in vue_code for section in required_sections)
    
    def _check_components(self, vue_code: str) -> bool:
        """检查组件"""
        # 检查组件使用是否正确
        return True  # 简化实现
    
    def _check_events(self, vue_code: str) -> bool:
        """检查事件"""
        # 检查事件绑定是否正确
        return True  # 简化实现
    
    def _check_data(self, vue_code: str) -> bool:
        """检查数据"""
        # 检查数据定义是否正确
        return True  # 简化实现

12.2 代码格式化

class VueCodeFormatter:
    """Vue代码格式化器"""
    
    def __init__(self):
        self.formatting_config = {
            "indent_size": 2,
            "max_line_length": 100,
            "preserve_quotes": True
        }
    
    def format_vue_code(self, vue_code: str) -> str:
        """格式化Vue代码"""
        # 基本的代码格式化
        lines = vue_code.split('\n')
        formatted_lines = []
        indent_level = 0
        
        for line in lines:
            stripped_line = line.strip()
            
            # 减少缩进
            if stripped_line.startswith('</') or stripped_line.endswith('}'):
                indent_level = max(0, indent_level - 1)
            
            # 添加缩进
            formatted_line = '  ' * indent_level + stripped_line
            formatted_lines.append(formatted_line)
            
            # 增加缩进
            if stripped_line.endswith('{') or stripped_line.endswith('>'):
                indent_level += 1
        
        return '\n'.join(formatted_lines)

13. 渲染最佳实践

13.1 性能优化建议

# Vue渲染性能优化建议
performance_recommendations = {
    "template": {
        "use_v_if": True,
        "avoid_v_show": True,
        "optimize_loops": True
    },
    "script": {
        "lazy_loading": True,
        "code_splitting": True,
        "tree_shaking": True
    },
    "style": {
        "scoped_styles": True,
        "css_modules": True,
        "critical_css": True
    }
}

13.2 代码质量保证

# Vue代码质量保证策略
quality_assurance = {
    "linting": {
        "eslint": True,
        "vue_lint": True,
        "prettier": True
    },
    "testing": {
        "unit_tests": True,
        "component_tests": True,
        "e2e_tests": True
    },
    "validation": {
        "syntax_validation": True,
        "component_validation": True,
        "event_validation": True
    }
}

通过掌握这些Vue页面渲染技术和优化策略,您可以构建一个高效、可维护、可扩展的Vue页面渲染系统。Vue渲染是将结构化设计转换为可运行前端代码的关键环节,需要精心设计和持续优化。

Prev
PNG图片渲染实现
Next
多主题支持架构