Skip to content

公司官网

使用 VitePress 构建专业的公司官网,全面展示企业形象、核心业务、品牌价值和发展历程。

项目概述

公司官网是企业数字化形象的核心载体,承载着品牌传播、业务展示、客户获取等重要功能。VitePress 凭借其现代化的技术架构和灵活的定制能力,为企业官网建设提供了理想的解决方案。

核心特性

  • 🎨 品牌展示 - 专业的视觉设计和品牌形象展示
  • 🏢 企业介绍 - 全面的公司信息和发展历程
  • 💼 业务展示 - 详细的产品服务和解决方案
  • 📈 成功案例 - 客户案例和项目展示
  • 👥 团队风采 - 企业文化和团队介绍
  • 📰 新闻资讯 - 企业动态和行业资讯
  • 📞 联系我们 - 多渠道联系方式和在线咨询
  • 🌍 全球化 - 多语言和多地区支持

技术架构

核心技术栈

json
{
  "framework": "VitePress",
  "language": "TypeScript",
  "styling": "SCSS + CSS Modules",
  "components": "Vue 3 Composition API",
  "animations": "GSAP + Lottie",
  "tools": [
    "Intersection Observer",
    "Swiper.js",
    "Chart.js",
    "Three.js"
  ]
}

项目结构

company-website/
├── docs/
│   ├── .vitepress/
│   │   ├── config.ts
│   │   ├── theme/
│   │   │   ├── index.ts
│   │   │   ├── Layout.vue
│   │   │   ├── components/
│   │   │   │   ├── Hero/
│   │   │   │   ├── About/
│   │   │   │   ├── Services/
│   │   │   │   ├── Cases/
│   │   │   │   └── Contact/
│   │   │   └── styles/
│   │   └── public/
│   │       ├── images/
│   │       ├── videos/
│   │       └── animations/
│   ├── about/
│   ├── services/
│   ├── cases/
│   ├── news/
│   ├── careers/
│   └── contact/
└── package.json

实现步骤

1. 项目初始化

bash
# 创建项目
npm create vitepress@latest company-website
cd company-website

# 安装依赖
npm install
npm install -D sass gsap lottie-web swiper chart.js three

2. 主题配置

typescript
// .vitepress/config.ts
import { defineConfig } from 'vitepress'

export default defineConfig({
  title: '创新科技集团',
  description: '引领行业创新,创造无限可能',
  
  head: [
    ['link', { rel: 'icon', href: '/favicon.ico' }],
    ['meta', { name: 'theme-color', content: '#3b82f6' }],
    ['meta', { property: 'og:type', content: 'website' }],
    ['meta', { property: 'og:locale', content: 'zh_CN' }],
    ['meta', { property: 'og:site_name', content: '创新科技集团' }],
    ['link', { rel: 'preconnect', href: 'https://fonts.googleapis.com' }],
    ['link', { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap' }]
  ],
  
  themeConfig: {
    logo: {
      light: '/logo-light.svg',
      dark: '/logo-dark.svg'
    },
    
    nav: [
      { text: '首页', link: '/' },
      {
        text: '关于我们',
        items: [
          { text: '公司简介', link: '/about/' },
          { text: '发展历程', link: '/about/history' },
          { text: '企业文化', link: '/about/culture' },
          { text: '资质荣誉', link: '/about/honors' }
        ]
      },
      {
        text: '产品服务',
        items: [
          { text: '解决方案', link: '/services/' },
          { text: '产品中心', link: '/services/products' },
          { text: '技术支持', link: '/services/support' }
        ]
      },
      { text: '成功案例', link: '/cases/' },
      { text: '新闻中心', link: '/news/' },
      { text: '人才招聘', link: '/careers/' },
      { text: '联系我们', link: '/contact/' }
    ],
    
    footer: {
      message: '© 2024 创新科技集团 版权所有',
      copyright: '京ICP备12345678号-1 | 京公网安备11010802012345号'
    }
  },
  
  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: `@import "@/styles/variables.scss";`
        }
      }
    }
  }
})

3. 首页英雄区域

vue
<!-- .vitepress/theme/components/Hero/HeroSection.vue -->
<template>
  <section class="hero-section" ref="heroRef">
    <div class="hero-background">
      <canvas ref="canvasRef" class="hero-canvas"></canvas>
      <div class="hero-overlay"></div>
    </div>
    
    <div class="hero-content">
      <div class="container">
        <div class="hero-text">
          <h1 class="hero-title" ref="titleRef">
            <span class="title-line">引领行业创新</span>
            <span class="title-line highlight">创造无限可能</span>
          </h1>
          
          <p class="hero-subtitle" ref="subtitleRef">
            我们致力于通过前沿技术和创新思维,为客户提供卓越的解决方案,
            推动行业发展,共创美好未来。
          </p>
          
          <div class="hero-actions" ref="actionsRef">
            <a href="/services/" class="btn btn-primary">
              <span>了解服务</span>
              <i class="icon-arrow"></i>
            </a>
            <a href="/contact/" class="btn btn-outline">
              <span>联系我们</span>
            </a>
          </div>
        </div>
        
        <div class="hero-stats" ref="statsRef">
          <div class="stat-item" v-for="stat in stats" :key="stat.label">
            <div class="stat-number" :data-target="stat.value">0</div>
            <div class="stat-label">{{ stat.label }}</div>
          </div>
        </div>
      </div>
    </div>
    
    <div class="scroll-indicator" @click="scrollToNext">
      <div class="scroll-mouse">
        <div class="scroll-wheel"></div>
      </div>
      <span>向下滚动</span>
    </div>
  </section>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { gsap } from 'gsap'
import * as THREE from 'three'

const heroRef = ref()
const canvasRef = ref()
const titleRef = ref()
const subtitleRef = ref()
const actionsRef = ref()
const statsRef = ref()

const stats = ref([
  { label: '服务客户', value: 500, suffix: '+' },
  { label: '成功项目', value: 1200, suffix: '+' },
  { label: '团队成员', value: 200, suffix: '+' },
  { label: '行业经验', value: 15, suffix: '年' }
])

let scene, camera, renderer, particles

onMounted(() => {
  initThreeJS()
  initAnimations()
  initCounters()
})

onUnmounted(() => {
  if (renderer) {
    renderer.dispose()
  }
})

function initThreeJS() {
  // 创建 Three.js 场景
  scene = new THREE.Scene()
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
  renderer = new THREE.WebGLRenderer({ canvas: canvasRef.value, alpha: true })
  
  renderer.setSize(window.innerWidth, window.innerHeight)
  renderer.setPixelRatio(window.devicePixelRatio)
  
  // 创建粒子系统
  const geometry = new THREE.BufferGeometry()
  const positions = new Float32Array(1000 * 3)
  
  for (let i = 0; i < 1000; i++) {
    positions[i * 3] = (Math.random() - 0.5) * 100
    positions[i * 3 + 1] = (Math.random() - 0.5) * 100
    positions[i * 3 + 2] = (Math.random() - 0.5) * 100
  }
  
  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
  
  const material = new THREE.PointsMaterial({
    color: 0x3b82f6,
    size: 2,
    transparent: true,
    opacity: 0.6
  })
  
  particles = new THREE.Points(geometry, material)
  scene.add(particles)
  
  camera.position.z = 50
  
  animate()
}

function animate() {
  requestAnimationFrame(animate)
  
  if (particles) {
    particles.rotation.x += 0.001
    particles.rotation.y += 0.002
  }
  
  renderer.render(scene, camera)
}

function initAnimations() {
  const tl = gsap.timeline()
  
  tl.from(titleRef.value.querySelectorAll('.title-line'), {
    duration: 1,
    y: 100,
    opacity: 0,
    stagger: 0.2,
    ease: 'power3.out'
  })
  .from(subtitleRef.value, {
    duration: 0.8,
    y: 50,
    opacity: 0,
    ease: 'power2.out'
  }, '-=0.5')
  .from(actionsRef.value.children, {
    duration: 0.6,
    y: 30,
    opacity: 0,
    stagger: 0.1,
    ease: 'back.out(1.7)'
  }, '-=0.3')
  .from(statsRef.value.children, {
    duration: 0.8,
    y: 40,
    opacity: 0,
    stagger: 0.1,
    ease: 'power2.out'
  }, '-=0.4')
}

function initCounters() {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        animateCounters()
        observer.disconnect()
      }
    })
  })
  
  observer.observe(statsRef.value)
}

function animateCounters() {
  statsRef.value.querySelectorAll('.stat-number').forEach((el, index) => {
    const target = parseInt(el.dataset.target)
    const suffix = stats.value[index].suffix
    
    gsap.to({ value: 0 }, {
      duration: 2,
      value: target,
      ease: 'power2.out',
      onUpdate: function() {
        el.textContent = Math.round(this.targets()[0].value) + suffix
      }
    })
  })
}

function scrollToNext() {
  const nextSection = document.querySelector('.about-section')
  if (nextSection) {
    nextSection.scrollIntoView({ behavior: 'smooth' })
  }
}
</script>

<style lang="scss" scoped>
.hero-section {
  position: relative;
  height: 100vh;
  min-height: 800px;
  display: flex;
  align-items: center;
  overflow: hidden;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.hero-background {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
}

.hero-canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.hero-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.3);
}

.hero-content {
  position: relative;
  z-index: 2;
  width: 100%;
  color: white;
}

.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 20px;
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 60px;
  align-items: center;
}

.hero-text {
  max-width: 600px;
}

.hero-title {
  font-size: 4rem;
  font-weight: 800;
  line-height: 1.1;
  margin-bottom: 2rem;
  
  .title-line {
    display: block;
    overflow: hidden;
  }
  
  .highlight {
    background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
  }
}

.hero-subtitle {
  font-size: 1.25rem;
  line-height: 1.7;
  margin-bottom: 3rem;
  opacity: 0.9;
}

.hero-actions {
  display: flex;
  gap: 1.5rem;
  flex-wrap: wrap;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 1rem 2rem;
  border-radius: 50px;
  font-weight: 600;
  text-decoration: none;
  transition: all 0.3s ease;
  border: 2px solid transparent;
  
  &.btn-primary {
    background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
    color: white;
    
    &:hover {
      transform: translateY(-3px);
      box-shadow: 0 20px 40px rgba(59, 130, 246, 0.4);
    }
  }
  
  &.btn-outline {
    background: transparent;
    color: white;
    border-color: rgba(255, 255, 255, 0.5);
    
    &:hover {
      background: white;
      color: #1f2937;
      border-color: white;
    }
  }
}

.hero-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
}

.stat-item {
  text-align: center;
  padding: 1.5rem;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 16px;
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.2);
}

.stat-number {
  font-size: 2.5rem;
  font-weight: 800;
  color: #fbbf24;
  margin-bottom: 0.5rem;
}

.stat-label {
  font-size: 0.9rem;
  opacity: 0.8;
}

.scroll-indicator {
  position: absolute;
  bottom: 2rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
  transition: all 0.3s ease;
  
  &:hover {
    color: white;
    transform: translateX(-50%) translateY(-5px);
  }
}

.scroll-mouse {
  width: 24px;
  height: 40px;
  border: 2px solid currentColor;
  border-radius: 12px;
  position: relative;
}

.scroll-wheel {
  width: 4px;
  height: 8px;
  background: currentColor;
  border-radius: 2px;
  position: absolute;
  top: 6px;
  left: 50%;
  transform: translateX(-50%);
  animation: scroll 2s infinite;
}

@keyframes scroll {
  0% { opacity: 0; transform: translateX(-50%) translateY(0); }
  50% { opacity: 1; }
  100% { opacity: 0; transform: translateX(-50%) translateY(12px); }
}

@media (max-width: 768px) {
  .container {
    grid-template-columns: 1fr;
    gap: 3rem;
    text-align: center;
  }
  
  .hero-title {
    font-size: 2.5rem;
  }
  
  .hero-subtitle {
    font-size: 1.1rem;
  }
  
  .hero-actions {
    justify-content: center;
  }
  
  .hero-stats {
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem;
  }
}
</style>

4. 关于我们组件

vue
<!-- .vitepress/theme/components/About/AboutSection.vue -->
<template>
  <section class="about-section" ref="sectionRef">
    <div class="container">
      <div class="section-header">
        <span class="section-tag">关于我们</span>
        <h2 class="section-title">专业团队,卓越服务</h2>
        <p class="section-subtitle">
          15年行业深耕,我们始终坚持以客户为中心,以创新为驱动,
          为全球客户提供专业的技术解决方案。
        </p>
      </div>
      
      <div class="about-content">
        <div class="about-text">
          <div class="text-block" v-for="(block, index) in textBlocks" :key="index">
            <h3>{{ block.title }}</h3>
            <p>{{ block.content }}</p>
          </div>
          
          <div class="about-features">
            <div class="feature-item" v-for="feature in features" :key="feature.title">
              <div class="feature-icon">
                <i :class="feature.icon"></i>
              </div>
              <div class="feature-content">
                <h4>{{ feature.title }}</h4>
                <p>{{ feature.description }}</p>
              </div>
            </div>
          </div>
        </div>
        
        <div class="about-visual">
          <div class="visual-container">
            <div class="timeline">
              <div class="timeline-item" v-for="(item, index) in timeline" :key="index">
                <div class="timeline-year">{{ item.year }}</div>
                <div class="timeline-content">
                  <h4>{{ item.title }}</h4>
                  <p>{{ item.description }}</p>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'

gsap.registerPlugin(ScrollTrigger)

const sectionRef = ref()

const textBlocks = ref([
  {
    title: '我们的使命',
    content: '通过技术创新推动行业进步,为客户创造价值,为社会贡献力量。我们相信技术的力量能够改变世界,让生活更美好。'
  },
  {
    title: '我们的愿景',
    content: '成为全球领先的技术解决方案提供商,以卓越的产品和服务赢得客户信任,推动数字化转型的浪潮。'
  }
])

const features = ref([
  {
    icon: 'icon-innovation',
    title: '技术创新',
    description: '持续投入研发,保持技术领先优势'
  },
  {
    icon: 'icon-quality',
    title: '品质保证',
    description: '严格的质量管控,确保产品可靠性'
  },
  {
    icon: 'icon-service',
    title: '专业服务',
    description: '7×24小时技术支持,快速响应'
  },
  {
    icon: 'icon-partnership',
    title: '合作共赢',
    description: '与客户建立长期战略合作关系'
  }
])

const timeline = ref([
  {
    year: '2009',
    title: '公司成立',
    description: '在北京成立,专注于企业级软件开发'
  },
  {
    year: '2012',
    title: '业务扩展',
    description: '拓展云计算和大数据业务领域'
  },
  {
    year: '2016',
    title: '技术突破',
    description: '推出自主研发的核心技术平台'
  },
  {
    year: '2020',
    title: '国际化',
    description: '业务拓展至海外市场,服务全球客户'
  },
  {
    year: '2024',
    title: 'AI赋能',
    description: '全面拥抱人工智能,引领行业变革'
  }
])

onMounted(() => {
  initAnimations()
})

function initAnimations() {
  gsap.fromTo('.section-header > *', 
    { y: 50, opacity: 0 },
    {
      y: 0,
      opacity: 1,
      duration: 0.8,
      stagger: 0.2,
      ease: 'power2.out',
      scrollTrigger: {
        trigger: sectionRef.value,
        start: 'top 80%'
      }
    }
  )
  
  gsap.fromTo('.text-block',
    { x: -50, opacity: 0 },
    {
      x: 0,
      opacity: 1,
      duration: 0.8,
      stagger: 0.3,
      ease: 'power2.out',
      scrollTrigger: {
        trigger: '.about-text',
        start: 'top 80%'
      }
    }
  )
  
  gsap.fromTo('.feature-item',
    { y: 30, opacity: 0 },
    {
      y: 0,
      opacity: 1,
      duration: 0.6,
      stagger: 0.1,
      ease: 'back.out(1.7)',
      scrollTrigger: {
        trigger: '.about-features',
        start: 'top 80%'
      }
    }
  )
  
  gsap.fromTo('.timeline-item',
    { x: 50, opacity: 0 },
    {
      x: 0,
      opacity: 1,
      duration: 0.8,
      stagger: 0.2,
      ease: 'power2.out',
      scrollTrigger: {
        trigger: '.timeline',
        start: 'top 80%'
      }
    }
  )
}
</script>

<style lang="scss" scoped>
.about-section {
  padding: 120px 0;
  background: #f8fafc;
}

.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 20px;
}

.section-header {
  text-align: center;
  margin-bottom: 80px;
}

.section-tag {
  display: inline-block;
  padding: 8px 20px;
  background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
  color: white;
  border-radius: 20px;
  font-size: 0.9rem;
  font-weight: 600;
  margin-bottom: 1rem;
}

.section-title {
  font-size: 3rem;
  font-weight: 800;
  color: #1f2937;
  margin-bottom: 1.5rem;
  line-height: 1.2;
}

.section-subtitle {
  font-size: 1.2rem;
  color: #6b7280;
  line-height: 1.7;
  max-width: 600px;
  margin: 0 auto;
}

.about-content {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 80px;
  align-items: start;
}

.text-block {
  margin-bottom: 3rem;
  
  h3 {
    font-size: 1.5rem;
    font-weight: 700;
    color: #1f2937;
    margin-bottom: 1rem;
  }
  
  p {
    color: #6b7280;
    line-height: 1.7;
    font-size: 1.1rem;
  }
}

.about-features {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
}

.feature-item {
  display: flex;
  gap: 1rem;
  padding: 1.5rem;
  background: white;
  border-radius: 12px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
  transition: all 0.3s ease;
  
  &:hover {
    transform: translateY(-5px);
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
  }
}

.feature-icon {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 1.5rem;
}

.feature-content {
  h4 {
    font-size: 1.1rem;
    font-weight: 600;
    color: #1f2937;
    margin-bottom: 0.5rem;
  }
  
  p {
    color: #6b7280;
    font-size: 0.9rem;
    line-height: 1.5;
  }
}

.visual-container {
  position: relative;
  background: white;
  border-radius: 20px;
  padding: 3rem;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}

.timeline {
  position: relative;
  
  &::before {
    content: '';
    position: absolute;
    left: 20px;
    top: 0;
    bottom: 0;
    width: 2px;
    background: linear-gradient(to bottom, #3b82f6, #1d4ed8);
  }
}

.timeline-item {
  position: relative;
  padding-left: 60px;
  margin-bottom: 2.5rem;
  
  &::before {
    content: '';
    position: absolute;
    left: 11px;
    top: 8px;
    width: 18px;
    height: 18px;
    background: #3b82f6;
    border-radius: 50%;
    border: 4px solid white;
    box-shadow: 0 0 0 4px #3b82f6;
  }
  
  &:last-child {
    margin-bottom: 0;
  }
}

.timeline-year {
  font-size: 1.2rem;
  font-weight: 800;
  color: #3b82f6;
  margin-bottom: 0.5rem;
}

.timeline-content {
  h4 {
    font-size: 1.1rem;
    font-weight: 600;
    color: #1f2937;
    margin-bottom: 0.5rem;
  }
  
  p {
    color: #6b7280;
    font-size: 0.9rem;
    line-height: 1.5;
  }
}

@media (max-width: 768px) {
  .about-content {
    grid-template-columns: 1fr;
    gap: 3rem;
  }
  
  .section-title {
    font-size: 2rem;
  }
  
  .about-features {
    grid-template-columns: 1fr;
  }
  
  .visual-container {
    padding: 2rem;
  }
}
</style>

高级功能

1. 数据可视化

vue
<!-- .vitepress/theme/components/Charts/BusinessChart.vue -->
<template>
  <div class="chart-container">
    <canvas ref="chartRef"></canvas>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import Chart from 'chart.js/auto'

const chartRef = ref()

const props = defineProps({
  type: {
    type: String,
    default: 'line'
  },
  data: {
    type: Object,
    required: true
  },
  options: {
    type: Object,
    default: () => ({})
  }
})

onMounted(() => {
  new Chart(chartRef.value, {
    type: props.type,
    data: props.data,
    options: {
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        legend: {
          position: 'bottom'
        }
      },
      ...props.options
    }
  })
})
</script>

<style scoped>
.chart-container {
  position: relative;
  height: 400px;
  width: 100%;
}
</style>

2. 3D 展示效果

vue
<!-- .vitepress/theme/components/3D/ProductShowcase.vue -->
<template>
  <div class="showcase-container" ref="containerRef">
    <canvas ref="canvasRef"></canvas>
    <div class="showcase-controls">
      <button @click="rotateLeft">向左旋转</button>
      <button @click="rotateRight">向右旋转</button>
      <button @click="resetView">重置视角</button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as THREE from 'three'

const containerRef = ref()
const canvasRef = ref()

let scene, camera, renderer, model, controls

onMounted(() => {
  initThreeJS()
  loadModel()
  animate()
})

onUnmounted(() => {
  if (renderer) {
    renderer.dispose()
  }
})

function initThreeJS() {
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0xf0f0f0)
  
  camera = new THREE.PerspectiveCamera(
    75,
    containerRef.value.clientWidth / containerRef.value.clientHeight,
    0.1,
    1000
  )
  
  renderer = new THREE.WebGLRenderer({
    canvas: canvasRef.value,
    antialias: true
  })
  
  renderer.setSize(
    containerRef.value.clientWidth,
    containerRef.value.clientHeight
  )
  
  // 添加光源
  const ambientLight = new THREE.AmbientLight(0x404040, 0.6)
  scene.add(ambientLight)
  
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
  directionalLight.position.set(1, 1, 1)
  scene.add(directionalLight)
  
  camera.position.z = 5
}

function loadModel() {
  // 创建一个简单的几何体作为示例
  const geometry = new THREE.BoxGeometry(2, 2, 2)
  const material = new THREE.MeshPhongMaterial({
    color: 0x3b82f6,
    shininess: 100
  })
  
  model = new THREE.Mesh(geometry, material)
  scene.add(model)
}

function animate() {
  requestAnimationFrame(animate)
  
  if (model) {
    model.rotation.y += 0.005
  }
  
  renderer.render(scene, camera)
}

function rotateLeft() {
  if (model) {
    model.rotation.y -= 0.2
  }
}

function rotateRight() {
  if (model) {
    model.rotation.y += 0.2
  }
}

function resetView() {
  if (model) {
    model.rotation.set(0, 0, 0)
  }
  camera.position.set(0, 0, 5)
}
</script>

<style scoped>
.showcase-container {
  position: relative;
  width: 100%;
  height: 500px;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}

.showcase-controls {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 10px;
}

.showcase-controls button {
  padding: 8px 16px;
  background: rgba(255, 255, 255, 0.9);
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.3s ease;
}

.showcase-controls button:hover {
  background: white;
  transform: translateY(-2px);
}
</style>

3. 动画效果库

vue
<!-- .vitepress/theme/components/Animations/LottiePlayer.vue -->
<template>
  <div ref="animationRef" class="lottie-container"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import lottie from 'lottie-web'

const animationRef = ref()

const props = defineProps({
  animationData: {
    type: Object,
    required: true
  },
  loop: {
    type: Boolean,
    default: true
  },
  autoplay: {
    type: Boolean,
    default: true
  }
})

let animation

onMounted(() => {
  animation = lottie.loadAnimation({
    container: animationRef.value,
    renderer: 'svg',
    loop: props.loop,
    autoplay: props.autoplay,
    animationData: props.animationData
  })
})

onUnmounted(() => {
  if (animation) {
    animation.destroy()
  }
})

defineExpose({
  play: () => animation?.play(),
  pause: () => animation?.pause(),
  stop: () => animation?.stop()
})
</script>

<style scoped>
.lottie-container {
  width: 100%;
  height: 100%;
}
</style>

部署与优化

1. 构建优化

javascript
// .vitepress/config.ts
export default defineConfig({
  vite: {
    build: {
      rollupOptions: {
        output: {
          manualChunks: {
            'three': ['three'],
            'gsap': ['gsap'],
            'chart': ['chart.js']
          }
        }
      }
    },
    optimizeDeps: {
      include: ['three', 'gsap', 'chart.js', 'lottie-web']
    }
  }
})

2. PWA 支持

javascript
// .vitepress/config.ts
import { VitePWA } from 'vite-plugin-pwa'

export default defineConfig({
  vite: {
    plugins: [
      VitePWA({
        registerType: 'autoUpdate',
        workbox: {
          globPatterns: ['**/*.{js,css,html,ico,png,svg,jpg,jpeg}']
        },
        manifest: {
          name: '创新科技集团',
          short_name: '创新科技',
          description: '引领行业创新,创造无限可能',
          theme_color: '#3b82f6',
          background_color: '#ffffff',
          display: 'standalone',
          icons: [
            {
              src: '/icons/icon-192x192.png',
              sizes: '192x192',
              type: 'image/png'
            },
            {
              src: '/icons/icon-512x512.png',
              sizes: '512x512',
              type: 'image/png'
            }
          ]
        }
      })
    ]
  }
})

3. 性能监控

javascript
// .vitepress/theme/utils/performance.js
export class PerformanceMonitor {
  constructor() {
    this.metrics = {}
    this.init()
  }
  
  init() {
    // 监控页面加载性能
    window.addEventListener('load', () => {
      this.measurePageLoad()
    })
    
    // 监控用户交互
    this.observeUserInteractions()
    
    // 监控资源加载
    this.observeResourceLoading()
  }
  
  measurePageLoad() {
    const perfData = performance.getEntriesByType('navigation')[0]
    
    this.metrics.pageLoad = {
      dns: perfData.domainLookupEnd - perfData.domainLookupStart,
      tcp: perfData.connectEnd - perfData.connectStart,
      request: perfData.responseStart - perfData.requestStart,
      response: perfData.responseEnd - perfData.responseStart,
      dom: perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart,
      load: perfData.loadEventEnd - perfData.loadEventStart,
      total: perfData.loadEventEnd - perfData.fetchStart
    }
    
    this.sendMetrics('page_load', this.metrics.pageLoad)
  }
  
  observeUserInteractions() {
    ['click', 'scroll', 'keydown'].forEach(event => {
      document.addEventListener(event, (e) => {
        this.trackInteraction(event, e.target)
      }, { passive: true })
    })
  }
  
  observeResourceLoading() {
    const observer = new PerformanceObserver((list) => {
      list.getEntries().forEach(entry => {
        if (entry.entryType === 'resource') {
          this.trackResourceLoad(entry)
        }
      })
    })
    
    observer.observe({ entryTypes: ['resource'] })
  }
  
  trackInteraction(type, element) {
    const data = {
      type,
      element: element.tagName,
      timestamp: Date.now()
    }
    
    this.sendMetrics('user_interaction', data)
  }
  
  trackResourceLoad(entry) {
    const data = {
      name: entry.name,
      duration: entry.duration,
      size: entry.transferSize,
      type: entry.initiatorType
    }
    
    this.sendMetrics('resource_load', data)
  }
  
  sendMetrics(type, data) {
    // 发送到分析服务
    if (typeof gtag !== 'undefined') {
      gtag('event', type, {
        event_category: 'Performance',
        event_label: JSON.stringify(data),
        value: data.duration || data.total
      })
    }
  }
}

// 初始化性能监控
export function initPerformanceMonitoring() {
  if (typeof window !== 'undefined') {
    new PerformanceMonitor()
  }
}

最佳实践

1. 代码组织

.vitepress/theme/
├── components/
│   ├── common/          # 通用组件
│   ├── layout/          # 布局组件
│   ├── business/        # 业务组件
│   └── ui/             # UI 组件
├── composables/         # 组合式函数
├── utils/              # 工具函数
├── styles/             # 样式文件
└── types/              # 类型定义

2. 状态管理

javascript
// .vitepress/theme/composables/useGlobalState.js
import { ref, reactive } from 'vue'

const globalState = reactive({
  user: null,
  theme: 'light',
  language: 'zh-CN'
})

export function useGlobalState() {
  const setUser = (user) => {
    globalState.user = user
  }
  
  const setTheme = (theme) => {
    globalState.theme = theme
    document.documentElement.setAttribute('data-theme', theme)
  }
  
  const setLanguage = (lang) => {
    globalState.language = lang
  }
  
  return {
    state: globalState,
    setUser,
    setTheme,
    setLanguage
  }
}

3. 错误处理

javascript
// .vitepress/theme/utils/errorHandler.js
export class ErrorHandler {
  static init() {
    // 全局错误捕获
    window.addEventListener('error', this.handleError)
    window.addEventListener('unhandledrejection', this.handlePromiseRejection)
    
    // Vue 错误处理
    if (typeof app !== 'undefined') {
      app.config.errorHandler = this.handleVueError
    }
  }
  
  static handleError(event) {
    console.error('Global Error:', event.error)
    this.reportError({
      type: 'javascript',
      message: event.error.message,
      stack: event.error.stack,
      filename: event.filename,
      lineno: event.lineno,
      colno: event.colno
    })
  }
  
  static handlePromiseRejection(event) {
    console.error('Unhandled Promise Rejection:', event.reason)
    this.reportError({
      type: 'promise',
      message: event.reason.message || event.reason,
      stack: event.reason.stack
    })
  }
  
  static handleVueError(error, instance, info) {
    console.error('Vue Error:', error, info)
    this.reportError({
      type: 'vue',
      message: error.message,
      stack: error.stack,
      info
    })
  }
  
  static reportError(errorInfo) {
    // 发送错误报告到监控服务
    if (typeof gtag !== 'undefined') {
      gtag('event', 'exception', {
        description: errorInfo.message,
        fatal: false
      })
    }
  }
}

示例项目

完整的示例项目可以在 GitHub 上查看。

在线演示

功能特色

  • ✅ 现代化设计
  • ✅ 3D 视觉效果
  • ✅ 动画交互
  • ✅ 数据可视化
  • ✅ 性能优化
  • ✅ PWA 支持
  • ✅ 错误监控
  • ✅ 多语言支持

通过这个案例,你可以学习如何使用 VitePress 构建功能丰富的公司官网,包括3D展示、动画效果、数据可视化等高级功能,打造令人印象深刻的企业形象展示平台。

vitepress开发指南