← 返回
未分类

企业级双端可交互DEMO生成

Build high-fidelity, interactive enterprise H5 demos with pixel-perfect design quality. Use this skill when you need to create a mobile-first enterprise app demo, corporate WeChat style UI, multi-role system, voice input simulation, or any H5 prototype that requires corporate-grade visual quality. 触发词:做H5演示、H5原型、企业微信风格、员工端演示、管理后台演示、高管驾驶舱、 语音输入演示、可交互Demo、高保真原型、移动端企业应用
||Hi-Fi Demo Generator v2.0 – 基于Vue 3 CDN + localStorage的高速企业H5原型工具。一键生成员工/管理/高管多角色应用、企业微信风格UI、高保真交互演示。触发词:做H5演示、H5原型、企业微信风格、员工端演示、管理后台演示、高管驾驶舱、语音输入演示、可交互Demo、高保真原型、移动端企业应用。
user_bca39892
未分类 community v1.0.3 4 版本 100000 Key: 无需
★ 0
Stars
📥 92
下载
💾 0
安装
4
版本
#latest

概述

高保真可交互 Demo 生成器

生成具有像素级设计品质的企业级可交互原型。

> 一份构建专业 H5 Demo 的完整指南,帮助你打动客户和利益相关者。

何时使用此技能

当你需要创建以下内容时使用此技能:

  • 移动端企业应用 Demo — 员工自助、经理仪表盘、高管驾驶舱
  • 多角色系统 — 基于角色的视图和权限感知导航
  • 企业微信风格 UI — 类似微信的交互模式和视觉语言
  • 语音输入 Demo — 语音转操作的流程演示
  • 高管数据看板 — KPI 追踪、数据可视化、决策支持
  • 任何像素级 H5 原型 — 视觉质量对利益相关者说服力很重要

设计理念

高保真 Demo 的三大支柱

  1. 视觉保真度 — 每一个像素都很重要。使用一致的间距、字体和颜色系统。遵循成熟的设计模式(微信、Ant Design、Material)。
  1. 交互保真度 — 模拟真实的用户流程。包含加载状态、过渡效果、错误处理和原生感的微交互。
  1. 数据真实性 — 使用能讲述故事的真实 Demo 数据。避免"Lorem Ipsum"和通用占位符。

质量分级体系

| 等级 | 特点 | 适用场景 |

|------|------|----------|

| 线框图 | 基本布局、占位数据 | 概念讨论 |

| 中保真 | 已样式化 UI、最少交互 | 内部评审 |

| 高保真 | 像素级精确、完整交互、真实数据 | 客户演示、利益相关者汇报 |

本技能聚焦于高保真质量。

技术架构

推荐技术栈

Vue 3 CDN + Vue Router 4 + localStorage + Font Awesome 6

CDN 依赖(带国内镜像和离线方案)

<!-- Vue 3(优先使用国内 CDN) -->
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<!-- 国内镜像(备选) -->
<!-- <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.4.21/vue.global.prod.min.js"></script> -->

<!-- Vue Router -->
<script src="https://unpkg.com/vue-router@4/dist/vue-router.global.prod.js"></script>
<!-- 国内镜像(备选) -->
<!-- <script src="https://cdn.bootcdn.net/ajax/libs/vue-router/4.3.0/vue-router.global.prod.min.js"></script> -->

<!-- Font Awesome 图标库 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- 国内镜像(备选) -->
<!-- <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.5.1/css/all.min.css"> -->

离线环境解决方案

如果网络受限,可以将上述文件下载到本地 vendor/ 目录:

vendor/
├── vue.global.prod.js
├── vue-router.global.prod.js
└── fontawesome.all.min.css

然后修改引用:

<script src="vendor/vue.global.prod.js"></script>
<script src="vendor/vue-router.global.prod.js"></script>
<link rel="stylesheet" href="vendor/fontawesome.all.min.css">

技术栈优势

  • 零构建配置,文件直接浏览器打开即可运行
  • 可离线使用,适合客户现场演示
  • localStorage 实现数据持久化,无需后端
  • CSS Variables 设计令牌,一处修改全局生效

项目结构

project/
├── index.html              # 入口:角色选择页(可选)
├── h5.html                  # H5 移动端 SPA(多角色合一)
├── admin.html               # PC 管理端 SPA(可选)
├── exec.html                # 高管驾驶舱 SPA(可选)
├── css/
│   ├── variables.css        # 设计令牌(颜色/字号/间距/阴影/动效)
│   ├── base.css             # 全局重置、按钮、卡片、标签、动画
│   ├── h5.css               # H5 端专用样式
│   ├── admin.css            # PC 管理端专用样式
│   └── exec.css             # 高管端专用样式
└── js/
    ├── store.js             # 数据管理层(Store + DataManager + 种子数据)
    └── utils.js             # 工具函数(日期/格式化/AI模拟)

核心生成工作流

第一步:创建 CSS 文件(加载顺序很重要!)

1.1 variables.css — 设计令牌优先

:root {
  /* === 品牌色 === */
  --color-primary: #07C160;           /* 微信绿 */
  --color-primary-light: #38D97A;
  --color-primary-dark: #06AD56;
  --color-primary-bg: rgba(7, 193, 96, 0.08);

  /* === 辅色 === */
  --color-secondary: #576B95;
  --color-secondary-bg: rgba(87, 107, 149, 0.08);

  /* === 功能色 === */
  --color-success: #07C160;
  --color-warning: #FAAD14;
  --color-warning-orange: #FA8C16;
  --color-danger: #FF4D4F;
  --color-info: #1890FF;

  /* === 预警三级色 === */
  --color-alert-yellow: #FAAD14;
  --color-alert-yellow-bg: rgba(250, 173, 20, 0.1);
  --color-alert-orange: #FA8C16;
  --color-alert-orange-bg: rgba(250, 140, 22, 0.1);
  --color-alert-red: #FF4D4F;
  --color-alert-red-bg: rgba(255, 77, 79, 0.1);

  /* === 文字色 === */
  --color-text-primary: #1A1A1A;
  --color-text-secondary: #666666;
  --color-text-tertiary: #999999;
  --color-text-placeholder: #CCCCCC;

  /* === 背景色 === */
  --color-bg-page: #F5F6FA;
  --color-bg-card: #FFFFFF;
  --color-bg-hover: #F7F8FA;
  --color-bg-overlay: rgba(0, 0, 0, 0.5);

  /* === 边框色 === */
  --color-border: #E8E8EC;
  --color-border-light: #F0F0F3;

  /* === 高管紫色系 === */
  --color-exec-primary: #6C5CE7;
  --color-exec-primary-light: #A29BFE;

  /* === 字体 === */
  --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC',
    'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;

  --font-size-h1: 24px;
  --font-size-h2: 20px;
  --font-size-h3: 17px;
  --font-size-body: 15px;
  --font-size-caption: 13px;
  --font-size-mini: 11px;

  /* === 间距(4px 基准) === */
  --space-xs: 4px;
  --space-sm: 8px;
  --space-md: 12px;
  --space-base: 16px;
  --space-lg: 20px;
  --space-xl: 24px;
  --space-2xl: 32px;

  /* === 圆角 === */
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --radius-xl: 18px;
  --radius-full: 9999px;

  /* === 阴影 === */
  --shadow-card: 0 1px 3px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.03);
  --shadow-card-hover: 0 8px 28px rgba(0,0,0,0.09), 0 2px 8px rgba(0,0,0,0.04);
  --shadow-float: 0 12px 36px rgba(0,0,0,0.15);

  /* === 动效 === */
  --duration-fast: 150ms;
  --duration-normal: 250ms;
  --duration-slow: 350ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* === 布局尺寸 === */
  --header-height: 56px;
  --tabbar-height: 50px;
  --sidebar-width: 220px;
}

1.2 base.css — 全局样式

在 variables.css 之后引入:

  • CSS 重置
  • 按钮系统(primary, secondary, danger, outline, sm, lg, block)
  • 卡片组件(带悬停效果)
  • 标签/徽章系统(primary, warning, danger, success, info)
  • 进度条(带颜色变体)
  • 动画关键帧(fadeIn, fadeInUp, slideInRight, pulse, shimmer 等)
  • Vue 过渡
  • 空状态
  • 滚动条美化

1.3 h5.css — 移动端专用样式

  • H5 应用容器
  • 固定导航栏(处理刘海 safe-area-inset-top)
  • 固定 TabBar(处理底部 safe-area-inset-bottom)
  • 页面容器(有无 TabBar 两种)
  • 桌面模拟器适配(min-width: 501px)
  • H5 专用卡片、标签、KPI 网格、预警栏

1.4 admin.css — PC 管理端样式

  • 侧边栏导航
  • 主内容区
  • 带面包屑的顶部栏
  • 数据表格
  • 表单组件

1.5 exec.css — 高管驾驶舱样式

  • 深色渐变侧边栏
  • KPI 仪表盘卡片
  • 图表容器
  • 高管专用字体

第二步:创建 JS 文件

2.1 store.js — 数据层(含完整种子数据)

const STORAGE_PREFIX = 'demo_';  // 每个项目使用唯一前缀

const Store = {
  get(key) {
    try {
      const data = localStorage.getItem(STORAGE_PREFIX + key);
      return data ? JSON.parse(data) : null;
    } catch (e) { return null; }
  },
  set(key, value) {
    try {
      localStorage.setItem(STORAGE_PREFIX + key, JSON.stringify(value));
      return true;
    } catch (e) { return false; }
  },
  remove(key) { localStorage.removeItem(STORAGE_PREFIX + key); },
  clear() {
    const keys = [];
    for (let i = 0; i < localStorage.length; i++) {
      const k = localStorage.key(i);
      if (k && k.startsWith(STORAGE_PREFIX)) keys.push(k);
    }
    keys.forEach(k => localStorage.removeItem(k));
  }
};

function genId(prefix = '') {
  return prefix + Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
}

// ========== 种子数据生成函数 ==========

function genDate(daysOffset = 0) {
  const d = new Date();
  d.setDate(d.getDate() + daysOffset);
  return d.toISOString().slice(0, 10);
}

function genTime() {
  const h = String(Math.floor(Math.random() * 12) + 8).padStart(2, '0');
  const m = String(Math.floor(Math.random() * 60)).padStart(2, '0');
  return `${h}:${m}`;
}

const DataManager = {
  init() {
    if (!Store.get('initialized')) {
      this.seedAll();
      Store.set('initialized', true);
    }
  },

  // 重置所有数据(从 Profile 页面调用)
  reset() {
    Store.clear();
    this.seedAll();
    Store.set('initialized', true);
    // 通知页面刷新
    window.dispatchEvent(new CustomEvent('data-reset'));
  },

  // ========== 完整的种子数据示例 ==========
  seedAll() {
    // 当前用户
    Store.set('current_user', {
      id: 'user001',
      name: '张明',
      role: 'employee',
      department: '产品研发部',
      avatar: '张'
    });

    // 任务列表(3 个任务,涵盖不同状态)
    Store.set('tasks', [
      {
        id: 'task001',
        title: '完成产品需求文档 V2.0',
        description: '整理第四季度需求,优化用户体验',
        progress: 75,
        status: 'in_progress',
        priority: 'high',
        deadline: genDate(3),
        assignee: '张明',
        createdAt: genDate(-5)
      },
      {
        id: 'task002',
        title: '用户调研报告整理',
        description: '汇总 20 份用户访谈记录',
        progress: 100,
        status: 'completed',
        priority: 'medium',
        deadline: genDate(-1),
        assignee: '张明',
        createdAt: genDate(-10)
      },
      {
        id: 'task003',
        title: '竞品分析更新',
        description: '跟踪竞品动态,更新功能对比表',
        progress: 30,
        status: 'in_progress',
        priority: 'low',
        deadline: genDate(7),
        assignee: '张明',
        createdAt: genDate(-2)
      }
    ]);

    // 报告列表(2 份报告)
    Store.set('reports', [
      {
        id: 'report001',
        title: 'Q4 产品规划报告',
        type: 'planning',
        score: 92,
        createdAt: genDate(-3) + ' ' + genTime(),
        author: '张明'
      },
      {
        id: 'report002',
        title: '用户增长分析月报',
        type: 'analysis',
        score: 85,
        createdAt: genDate(-7) + ' ' + genTime(),
        author: '张明'
      }
    ]);

    // 预警列表(3 条预警,黄/橙/红各一)
    Store.set('alerts', [
      {
        id: 'alert001',
        level: 'warning',  // yellow
        title: '项目进度预警',
        message: '产品需求文档距截止日期还有 3 天',
        createdAt: genDate(0) + ' ' + genTime()
      },
      {
        id: 'alert002',
        level: 'orange',   // orange
        title: '数据异常通知',
        message: '昨日活跃用户数较上周同期下降 15%',
        createdAt: genDate(-1) + ' ' + genTime()
      },
      {
        id: 'alert003',
        level: 'danger',   // red
        title: '紧急:服务器负载过高',
        message: 'CPU 使用率持续超过 90%,建议扩容',
        createdAt: genDate(0) + ' ' + genTime()
      }
    ]);

    // 审批列表(2 条待审批)
    Store.set('approvals', [
      {
        id: 'approval001',
        title: '差旅报销申请',
        amount: 2850.00,
        currency: '¥',
        applicant: '李华',
        department: '市场部',
        status: 'pending',
        type: 'expense',
        createdAt: genDate(-2) + ' ' + genTime()
      },
      {
        id: 'approval002',
        title: '请假申请',
        duration: '3天',
        startDate: genDate(5),
        applicant: '王芳',
        department: '产品部',
        status: 'pending',
        type: 'leave',
        createdAt: genDate(-1) + ' ' + genTime()
      }
    ]);
  },

  // ========== CRUD 操作 ==========
  getCurrentUser() { return Store.get('current_user'); },
  setCurrentUser(user) { Store.set('current_user', user); },

  getTasks() { return Store.get('tasks') || []; },
  saveTask(task) {
    const tasks = this.getTasks();
    const idx = tasks.findIndex(t => t.id === task.id);
    if (idx >= 0) tasks[idx] = task;
    else tasks.push(task);
    Store.set('tasks', tasks);
  },

  getReports() { return Store.get('reports') || []; },
  saveReport(report) {
    const reports = this.getReports();
    const idx = reports.findIndex(r => r.id === report.id);
    if (idx >= 0) reports[idx] = report;
    else reports.push(report);
    Store.set('reports', reports);
  },

  getAlerts() { return Store.get('alerts') || []; },
  getApprovals() { return Store.get('approvals') || []; }
};

2.2 utils.js — 工具函数(含 AI 模拟实现)

const Utils = {
  // 打字机效果(用于 AI 模拟)
  typeWriter(text, callback, speed = 30) {
    let i = 0;
    const timer = setInterval(() => {
      if (i < text.length) {
        callback(text.slice(0, i + 1), i === text.length - 1);
        i++;
      } else {
        clearInterval(timer);
      }
    }, speed);
    return timer;
  },

  // Promise 延迟
  sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); },

  // ========== 内置 AI 模拟实现 ==========
  // 基于关键词匹配返回预设场景

  // 预设场景定义
  scenarios: [
    {
      keywords: ['报销', '差旅', '发票', '费用'],
      intent: 'expense',
      icon: 'fa-receipt',
      color: '#07C160',
      title: '差旅报销',
      subtitle: '已识别为差旅费用类请求',
      result: {
        type: 'form',
        title: '新建差旅报销',
        fields: ['出发地', '目的地', '日期范围', '金额', '事由']
      },
      actions: [
        { label: '填写报销单', type: 'primary', action: 'fill_expense' },
        { label: '查看历史', type: 'outline', action: 'view_history' }
      ]
    },
    {
      keywords: ['请假', '休息', '度假', '假期'],
      intent: 'leave',
      icon: 'fa-plane',
      color: '#1890FF',
      title: '请假申请',
      subtitle: '已识别为请假类请求',
      result: {
        type: 'form',
        title: '新建请假申请',
        fields: ['请假类型', '开始日期', '结束日期', '事由']
      },
      actions: [
        { label: '填写请假单', type: 'primary', action: 'fill_leave' },
        { label: '查看剩余假期', type: 'outline', action: 'view_leave_balance' }
      ]
    },
    {
      keywords: ['数据', '分析', '报表', '统计'],
      intent: 'report',
      icon: 'fa-chart-bar',
      color: '#722ED1',
      title: '数据查询',
      subtitle: '已识别为数据查询类请求',
      result: {
        type: 'list',
        title: '相关数据报告',
        items: ['用户增长趋势', '收入分析报告', '运营周报']
      },
      actions: [
        { label: '查看报告', type: 'primary', action: 'view_reports' },
        { label: '生成新报告', type: 'outline', action: 'create_report' }
      ]
    },
    {
      keywords: ['任务', '待办', '分配', '指派'],
      intent: 'task',
      icon: 'fa-tasks',
      color: '#FAAD14',
      title: '任务管理',
      subtitle: '已识别为任务管理类请求',
      result: {
        type: 'task_panel',
        title: '我的任务',
        counts: { pending: 3, inProgress: 2, completed: 8 }
      },
      actions: [
        { label: '查看任务列表', type: 'primary', action: 'view_tasks' },
        { label: '创建新任务', type: 'outline', action: 'create_task' }
      ]
    },
    {
      keywords: ['审批', '通过', '拒绝', '确认'],
      intent: 'approval',
      icon: 'fa-check-double',
      color: '#FA8C16',
      title: '审批流程',
      subtitle: '已识别为审批类请求',
      result: {
        type: 'approval_panel',
        title: '待我审批',
        count: 5
      },
      actions: [
        { label: '查看待审批', type: 'primary', action: 'view_approvals' },
        { label: '审批历史', type: 'outline', action: 'view_approval_history' }
      ]
    }
  ],

  // AI 场景模拟
  simulateAI(input) {
    const lowerInput = input.toLowerCase();

    // 遍历场景,匹配关键词
    for (const scenario of this.scenarios) {
      for (const keyword of scenario.keywords) {
        if (lowerInput.includes(keyword)) {
          return { ...scenario, matchedKeyword: keyword };
        }
      }
    }

    // 默认场景(未匹配到任何关键词)
    return {
      intent: 'general',
      icon: 'fa-robot',
      color: '#576B95',
      title: '智能助手',
      subtitle: '正在处理您的请求...',
      result: {
        type: 'text',
        content: `已收到您的请求:"${input}",正在分析中...`
      },
      actions: [
        { label: '重新描述', type: 'outline', action: 'rephrase' }
      ]
    };
  },

  // 报告/数据集评分
  scoreDataset(content) {
    // 模拟评分逻辑
    const standard = Math.floor(Math.random() * 15) + 80;
    const completion = Math.floor(Math.random() * 20) + 75;
    const quality = Math.floor(Math.random() * 20) + 78;
    return {
      standard,
      completion,
      quality,
      total: Math.round((standard + completion + quality) / 3)
    };
  },

  // 数字动画
  animateNumber(el, target, duration = 800) {
    const start = parseInt(el.textContent) || 0;
    const startTime = performance.now();
    function update(currentTime) {
      const elapsed = currentTime - startTime;
      const progress = Math.min(elapsed / duration, 1);
      const eased = 1 - Math.pow(1 - progress, 3);
      el.textContent = Math.round(start + (target - start) * eased);
      if (progress < 1) requestAnimationFrame(update);
    }
    requestAnimationFrame(update);
  },

  // 进度环 SVG
  progressRingSVG(progress, size = 72, stroke = 6, color = '#07C160') {
    const r = (size - stroke) / 2;
    const c = Math.PI * 2 * r;
    const offset = c * (1 - progress / 100);
    return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
      <circle cx="${size/2}" cy="${size/2}" r="${r}" fill="none" stroke="#F0F0F3" stroke-width="${stroke}"/>
      <circle cx="${size/2}" cy="${size/2}" r="${r}" fill="none" stroke="${color}" stroke-width="${stroke}"
        stroke-linecap="round" stroke-dasharray="${c}" stroke-dashoffset="${offset}"
        transform="rotate(-90 ${size/2} ${size/2})"
        style="transition: stroke-dashoffset 0.8s cubic-bezier(0.22,1,0.36,1)"/>
      <text x="${size/2}" y="${size/2}" text-anchor="middle" dominant-baseline="central"
        font-size="${size/4}" font-weight="700" fill="${color}">${progress}%</text>
    </svg>`;
  }
};

window.Utils = Utils;

第三步:创建 h5.html(含完整路由守卫)

HTML 结构

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <title>应用名称</title>
  <!-- CSS 文件(按顺序加载) -->
  <link rel="stylesheet" href="css/variables.css">
  <link rel="stylesheet" href="css/base.css">
  <link rel="stylesheet" href="css/h5.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
  <style>[v-cloak] { display: none !important; }</style>
</head>
<body>
  <div id="app" v-cloak>
    <!-- 角色选择遮罩 -->
    <div v-if="!role" class="role-select-overlay">
      <div class="role-select-title">应用名称</div>
      <div class="role-select-subtitle">请选择您的角色</div>
      <div class="role-select-cards">
        <div class="role-select-card employee" @click="selectRole('employee')">
          <div class="role-icon"><i class="fas fa-user"></i></div>
          <div class="role-info">
            <div class="role-name">员工</div>
            <div class="role-desc">员工自助服务入口</div>
          </div>
        </div>
        <div class="role-select-card manager" @click="selectRole('manager')">
          <div class="role-icon"><i class="fas fa-user-tie"></i></div>
          <div class="role-info">
            <div class="role-name">经理</div>
            <div class="role-desc">团队管理入口</div>
          </div>
        </div>
      </div>
    </div>

    <!-- 主应用 -->
    <div v-else class="h5-app">
      <router-view></router-view>

      <!-- TabBar -->
      <div v-if="currentPageMeta?.showTabbar !== false" class="h5-tabbar">
        <div v-for="tab in tabs" :key="tab.path"
             class="h5-tabbar-item" :class="{ active: isActive(tab.path) }"
             @click="navigate(tab.path)">
          <i class="h5-tabbar-item-icon" :class="tab.icon"></i>
          <span class="h5-tabbar-item-label">{{ tab.label }}</span>
          <span v-if="tab.badge" class="badge">{{ tab.badge }}</span>
        </div>
      </div>
    </div>
  </div>

  <!-- JS 依赖 -->
  <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
  <script src="https://unpkg.com/vue-router@4/dist/vue-router.global.prod.js"></script>
  <script src="js/store.js"></script>
  <script src="js/utils.js"></script>
  <script>
    // 初始化数据
    DataManager.init();

    // ========== TabBar 配置 ==========
    const tabs = [
      { path: '/', icon: 'fas fa-home', label: '首页' },
      { path: '/tasks', icon: 'fas fa-list-check', label: '任务' },
      { path: '/reports', icon: 'fas fa-file-lines', label: '报告' },
      { path: '/approvals', icon: 'fas fa-check-double', label: '审批', badge: null },
      { path: '/profile', icon: 'fas fa-user', label: '我的' }
    ];

    // ========== 页面组件(内联示例)==========

    const HomePage = {
      setup() {
        const tasks = ref(DataManager.getTasks());
        const alerts = ref(DataManager.getAlerts());

        // 监听数据重置
        onMounted(() => {
          window.addEventListener('data-reset', () => {
            tasks.value = DataManager.getTasks();
            alerts.value = DataManager.getAlerts();
          });
        });

        return { tasks, alerts };
      },
      template: `
        <div class="h5-page">
          <div class="h5-navbar">
            <span class="h5-navbar-title">首页</span>
          </div>

          <!-- 预警栏 -->
          <div v-for="alert in alerts.slice(0, 1)" :key="alert.id"
               class="h5-alert-bar"
               :class="'h5-alert-' + alert.level">
            <i :class="'fas fa-exclamation-circle'"></i>
            <span>{{ alert.title }}</span>
          </div>

          <!-- 任务卡片 -->
          <div class="section-title">我的任务</div>
          <div v-for="task in tasks" :key="task.id" class="card" style="margin-bottom:12px">
            <div class="card-body">
              <div style="display:flex;justify-content:space-between;align-items:center">
                <span style="font-weight:600">{{ task.title }}</span>
                <span class="tag" :class="'tag-' + (task.status === 'completed' ? 'success' : 'default')">
                  {{ task.status === 'completed' ? '已完成' : '进行中' }}
                </span>
              </div>
              <div class="progress-bar" style="margin-top:8px">
                <div class="progress-bar-fill" :style="{ width: task.progress + '%' }"></div>
              </div>
            </div>
          </div>
        </div>
      `
    };

    // 更多页面组件...

    // ========== 路由配置(含权限守卫)==========
    const routes = [
      { path: '/', component: HomePage, meta: { title: '首页', showTabbar: true, role: 'employee' } },
      { path: '/tasks', component: { template: '<div class="h5-page"><div class="h5-navbar"><span class="h5-navbar-title">任务</span></div><div class="empty-state"><i class="fas fa-tasks empty-state-icon"></i><div class="empty-state-text">任务列表</div></div></div>' }, meta: { title: '任务', showTabbar: true, role: 'employee' } },
      { path: '/reports', component: { template: '<div class="h5-page"><div class="h5-navbar"><span class="h5-navbar-title">报告</span></div><div class="empty-state"><i class="fas fa-file-lines empty-state-icon"></i><div class="empty-state-text">报告列表</div></div></div>' }, meta: { title: '报告', showTabbar: true, role: 'employee' } },
      { path: '/approvals', component: { template: '<div class="h5-page"><div class="h5-navbar"><span class="h5-navbar-title">审批</span></div><div class="empty-state"><i class="fas fa-check-double empty-state-icon"></i><div class="empty-state-text">审批列表</div></div></div>' }, meta: { title: '审批', showTabbar: true, role: 'employee' } },
      { path: '/profile', component: { template: '<div class="h5-page"><div class="h5-navbar"><span class="h5-navbar-title">我的</span></div><div class="card" style="margin-top:16px"><div class="card-body"><div style="text-align:center;margin-bottom:16px"><div style="width:64px;height:64px;background:var(--color-primary-bg);color:var(--color-primary);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:24px;margin:0 auto">{{ DataManager.getCurrentUser().avatar }}</div><div style="font-weight:600;margin-top:8px">{{ DataManager.getCurrentUser().name }}</div><div style="color:var(--color-text-secondary);font-size:13px">{{ DataManager.getCurrentUser().department }}</div></div><button class="btn btn-outline btn-block" @click="handleReset"><i class="fas fa-redo"></i> 重置演示数据</button></div></div></div>' }, meta: { title: '我的', showTabbar: true, role: 'employee' } }
    ];

    const router = createRouter({
      history: createWebHashHistory(),
      routes
    });

    // ========== 完整的路由守卫(权限控制)==========
    router.beforeEach((to, from, next) => {
      const role = localStorage.getItem('demo_role');

      // 如果没有选择角色,只能访问根路径
      if (!role) {
        if (to.path === '/') {
          next();
        } else {
          next('/');
        }
        return;
      }

      // 如果已选择角色但访问选择页,重定向
      if (to.path === '/' && role) {
        next();
        return;
      }

      // 检查路由权限
      const routeRole = to.meta.role;
      if (routeRole && routeRole !== role) {
        // 角色不匹配,重定向到首页
        next('/');
        return;
      }

      next();
    });

    // ========== Vue 应用 ==========
    const app = createApp({
      setup() {
        const role = ref(localStorage.getItem('demo_role') || '');
        const currentPageMeta = ref({});

        const selectRole = (r) => {
          localStorage.setItem('demo_role', r);
          role.value = r;
        };

        const isActive = (path) => {
          return router.currentRoute.value.path === path;
        };

        const navigate = (path) => {
          router.push(path);
        };

        // 监听路由变化更新 TabBar 状态
        watch(() => router.currentRoute.value, (route) => {
          currentPageMeta.value = route.meta || {};
        }, { immediate: true });

        // 重置数据
        const handleReset = () => {
          if (confirm('确定要重置所有演示数据吗?')) {
            DataManager.reset();
            alert('数据已重置');
          }
        };

        return { role, currentPageMeta, selectRole, isActive, navigate, tabs, handleReset, DataManager };
      }
    });

    app.use(router);
    app.mount('#app');
  </script>
</body>
</html>

通用组件模式

带进度条的卡片

<div class="card" style="margin-bottom:12px">
  <div class="card-body">
    <div style="display:flex;justify-content:space-between;align-items:center">
      <span style="font-weight:600">{{ item.title }}</span>
      <span class="tag" :class="statusTagClass">{{ statusText }}</span>
    </div>
    <div class="progress-bar" style="margin-top:8px">
      <div class="progress-bar-fill" :style="{ width: item.progress + '%' }"></div>
    </div>
    <div style="display:flex;justify-content:space-between;font-size:12px;color:#999;margin-top:6px">
      <span><i class="fas fa-clock"></i> {{ item.deadline }}</span>
      <span>{{ item.progress }}%</span>
    </div>
  </div>
</div>

AI 结果卡片

<div class="card">
  <div class="card-body">
    <div style="display:flex;align-items:center;gap:12px;margin-bottom:16px">
      <div :style="{ width:'40px',height:'40px',borderRadius:'10px',background:`${color}20`,display:'flex',alignItems:'center',justifyContent:'center' }">
        <i :class="icon" :style="{ color, fontSize:'20px' }"></i>
      </div>
      <div style="flex:1">
        <div style="font-weight:600">{{ title }}</div>
        <div style="font-size:12px;color:#999">{{ subtitle }}</div>
      </div>
    </div>
    <div style="display:flex;gap:8px;flex-wrap:wrap">
      <button v-for="action in actions" :key="action.label"
              class="btn btn-sm" :class="action.type || 'btn-outline'">
        {{ action.label }}
      </button>
    </div>
  </div>
</div>

穿透查看弹窗

const openDetail = (item, type) => {
  selectedItem.value = item;
  selectedItemType.value = type;
  showDetailModal.value = true;
  // 自动生成 AI 分析
  generateAIAnalysis(type, item);
};

const closeDetail = () => {
  showDetailModal.value = false;
  selectedItem.value = null;
  aiAnalysis.value = null;
};

预警横幅

<div class="h5-alert-bar" :class="'h5-alert-' + alert.level">
  <i :class="'fas fa-exclamation-circle'"></i>
  <span>{{ alert.message }}</span>
</div>

桌面模拟器支持

H5 应用会自动适配桌面端,带有手机模拟器边框:

@media (min-width: 501px) {
  body {
    background: #1a1a2e;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    overflow: hidden;  /* 防止双滚动条 */
  }

  .h5-app {
    width: 390px;
    height: 844px;
    border-radius: 40px;
    box-shadow: 0 0 0 8px #333, 0 0 0 10px #555;
    overflow: hidden;  /* 改为 hidden,内容在内部滚动 */
    position: relative;
    padding-top: 44px;
    padding-bottom: 34px;
  }

  /* 模拟器刘海 */
  .h5-app::before {
    content: '';
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    width: 120px;
    height: 28px;
    background: #333;
    border-radius: 0 0 16px 16px;
    z-index: 101;
  }

  /* 模拟器下巴指示条 */
  .h5-app::after {
    content: '';
    position: absolute;
    bottom: 8px;
    left: 50%;
    transform: translateX(-50%);
    width: 140px;
    height: 5px;
    background: #555;
    border-radius: 3px;
    z-index: 101;
  }

  /* TabBar 在桌面端模拟器内定位 */
  .h5-tabbar {
    position: fixed;
    bottom: 34px;
    left: calc(50% - 195px);
    width: 390px;
    border-radius: 0 0 40px 40px;
  }

  /* 页面内容内部滚动 */
  .h5-page {
    height: calc(100% - 44px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }

  /* 隐藏页面滚动条但保持滚动功能 */
  .h5-page::-webkit-scrollbar {
    width: 0;
    height: 0;
  }

  /* 弹窗定位修复 */
  .h5-modal-overlay {
    position: absolute !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 84px !important;
  }
}

快速预览

生成文件后,可以选择以下方式预览:

方式一:直接打开(仅 H5)

直接用浏览器打开 h5.html 文件即可。

方式二:本地 HTTP 服务器

# Python 3
cd /path/to/project
python -m http.server 8080

# Node.js (npx)
npx serve .

# PHP
php -S localhost:8080

然后访问 http://localhost:8080/h5.html

方式三:VS Code Live Server 插件

安装 Live Server 插件,右键点击 h5.html → "Open with Live Server"

PC 管理端(可选)

admin.html 是独立的 PC 管理后台 SPA,可选择性实现。如果暂时不需要,可以忽略此文件。

最小可用模板

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>管理后台</title>
  <link rel="stylesheet" href="css/variables.css">
  <link rel="stylesheet" href="css/base.css">
  <link rel="stylesheet" href="css/admin.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body>
  <div class="admin-layout">
    <!-- 侧边栏 -->
    <aside class="admin-sidebar">
      <div class="admin-sidebar-header">
        <div class="admin-sidebar-logo">
          <div class="admin-sidebar-logo-icon"><i class="fas fa-cube"></i></div>
          <span>管理后台</span>
        </div>
      </div>
      <nav class="admin-sidebar-nav">
        <div class="admin-nav-group">
          <div class="admin-nav-group-title">概览</div>
          <div class="admin-nav-item active">
            <i class="admin-nav-item-icon fas fa-chart-pie"></i>
            <span class="admin-nav-item-label">数据看板</span>
          </div>
        </div>
      </nav>
    </aside>

    <!-- 主内容区 -->
    <main class="admin-main">
      <header class="admin-header">
        <div class="admin-header-left">
          <div class="admin-header-title">数据看板</div>
        </div>
        <div class="admin-header-right">
          <div class="admin-header-action">
            <i class="fas fa-bell"></i>
          </div>
          <div class="admin-header-avatar">管</div>
        </div>
      </header>
      <div class="admin-content">
        <div style="padding: 24px;">
          <h2>欢迎使用管理后台</h2>
          <p style="color: var(--color-text-secondary); margin-top: 8px;">
            这是一个最小可用的 PC 管理后台模板。
          </p>
        </div>
      </div>
    </main>
  </div>
</body>
</html>

质量检查清单

功能检查

  • [ ] 所有导航链接正常工作
  • [ ] TabBar 在桌面模拟器正确显示
  • [ ] AI 模拟动画流畅播放
  • [ ] 数据在页面刷新后持久化
  • [ ] 重置按钮清除所有数据
  • [ ] 角色切换正确刷新视图
  • [ ] 路由守卫正确拦截越权访问

视觉检查

  • [ ] 移动端响应式布局正常
  • [ ] 桌面端模拟器居中显示
  • [ ] 动画流畅(60fps)
  • [ ] 字体和图标正确加载
  • [ ] 无控制台错误
  • [ ] 无双滚动条问题

H5 专项

  • [ ] 安全区域(刘海/下巴)正确适配
  • [ ] 无 TabBar 页面底部内容不被遮挡
  • [ ] 弹窗在模拟器内正确定位
  • [ ] 触摸反馈正常

数据质量

  • [ ] 无 "Lorem Ipsum" 或占位符文本
  • [ ] 所有数据看起来真实可信
  • [ ] 日期为当前或近期日期
  • [ ] 名称和金额看起来真实

设计系统参考

完整的颜色系统、字体规范、间距系统、组件规格、动画关键帧等详见:

references/design-system.md

最佳实践总结

  1. CSS 加载顺序:variables.css → base.css → h5.css(或 admin.css/exec.css)
  2. 设计令牌:始终使用 CSS 变量,绝不硬编码颜色
  3. 角色选择:使用遮罩层模式,将角色保存到 localStorage
  4. 数据层:Store + DataManager 模式,包含 seed/reset 功能
  5. AI 模拟:使用关键词匹配实现意图识别
  6. 动画:优先使用 CSS transition 而非 JS 动画
  7. 桌面支持:带刘海/下巴指示条的手机模拟器边框
  8. 安全区域:使用 env() 处理 iOS 安全区域
  9. 穿透查看:点击 → 详情弹窗 → AI 分析流程
  10. 数据叙事:创建能讲述引人入胜故事的数据

版本历史

共 4 个版本

  • v1.0.3 更新描述 当前
    2026-04-26 15:06 安全 安全
  • v1.0.2 通用性重构
    2026-04-26 11:02 安全 安全
  • v1.0.1 移除历史项目信息,通用性更新
    2026-04-26 10:32 安全 安全
  • v1.0.0 Initial release
    2026-04-26 10:01 安全 安全

安全检测

腾讯云安全 (Keen)

安全,无风险
查看报告

腾讯云安全 (Sanbu)

安全,无风险
查看报告

🔗 相关推荐

dev-programming

Mcporter

steipete
使用 mcporter CLI 直接列出、配置、认证及调用 MCP 服务器/工具(支持 HTTP 或 stdio),涵盖临时服务器、配置编辑及 CLI/类型生成功能。
★ 198 📥 68,169
dev-programming

CodeConductor.ai

larsonreever
AI驱动平台,提供快速全栈开发、智能体、工作流自动化及低代码AI集成的可扩展产品创建。
★ 80 📥 182,940
dev-programming

Github

steipete
使用 `gh` CLI 与 GitHub 交互,通过 `gh issue`、`gh pr`、`gh run` 和 `gh api` 管理议题、PR、CI 运行及高级查询。
★ 686 📥 330,812