Vue2大型在线教育平台开发实战:从直播功能到课程管理全流程解析 | 前端架构设计

2025-08-19 0 747

发布日期:2024年9月10日

一、平台架构设计

本教程将构建一个完整的在线教育平台,包含以下核心模块:

  • 直播系统:WebRTC实时互动
  • 课程管理:多媒体课程展示
  • 学习系统:进度跟踪与笔记
  • 用户中心:多角色权限控制
  • 支付系统:课程购买与订阅

技术栈:Vue2 + Vuex + ElementUI + WebRTC + WebSocket

二、项目初始化与工程化

1. 项目创建与配置

# 使用Vue CLI创建项目
vue create edu-platform

# 安装核心依赖
cd edu-platform
npm install vuex element-ui socket.io-client webrtc-adapter
npm install -D sass sass-loader

2. 目录结构规划

src/
├── api/               # 接口服务
├── components/        # 公共组件
│   ├── live/          # 直播组件
│   └── course/        # 课程组件
├── directives/        # 自定义指令
├── filters/           # 全局过滤器
├── mixins/            # 混入对象
├── router/            # 路由配置
├── store/             # Vuex状态
│   ├── modules/       # 模块化状态
│   └── index.js       # 主入口
├── styles/            # 全局样式
├── utils/             # 工具函数
├── views/             # 页面组件
│   ├── live/          # 直播页面
│   └── course/        # 课程页面
├── App.vue
└── main.js

三、WebRTC直播系统

1. 直播房间组件

<template>
  <div class="live-room">
    <div class="video-container">
      <video ref="localVideo" autoplay muted></video>
      <video ref="remoteVideo" autoplay></video>
    </div>
    <div class="controls">
      <el-button @click="toggleCamera">{{ cameraOn ? '关闭' : '开启' }}摄像头</el-button>
      <el-button @click="toggleMic">{{ micOn ? '静音' : '取消静音' }}</el-button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      cameraOn: false,
      micOn: false,
      peerConnection: null,
      localStream: null
    }
  },
  methods: {
    async initStream() {
      try {
        this.localStream = await navigator.mediaDevices.getUserMedia({
          video: true,
          audio: true
        });
        this.$refs.localVideo.srcObject = this.localStream;
        this.cameraOn = true;
        this.micOn = true;
      } catch (err) {
        console.error('获取媒体设备失败:', err);
      }
    },
    toggleCamera() {
      if (this.localStream) {
        this.localStream.getVideoTracks()[0].enabled = !this.cameraOn;
        this.cameraOn = !this.cameraOn;
      }
    }
  },
  mounted() {
    this.initStream();
    this.initWebSocket();
  }
}
</script>

2. 信令服务交互

// utils/socket.js
import io from 'socket.io-client';

class SocketService {
  constructor() {
    this.socket = null;
  }
  
  connect(token) {
    this.socket = io(process.env.VUE_APP_SOCKET_URL, {
      auth: { token }
    });
    
    this.socket.on('connect', () => {
      console.log('Socket connected');
    });
    
    this.socket.on('disconnect', () => {
      console.log('Socket disconnected');
    });
  }
  
  joinRoom(roomId) {
    this.socket.emit('join', { roomId });
  }
  
  on(event, callback) {
    this.socket.on(event, callback);
  }
}

export const socket = new SocketService();

四、课程管理系统

1. 课程树形组件

<template>
  <div class="course-tree">
    <el-tree
      :data="chapters"
      node-key="id"
      :props="defaultProps"
      :expand-on-click-node="false"
      @node-click="handleNodeClick"
    >
      <span slot-scope="{ node, data }" class="tree-node">
        <span>{{ node.label }}</span>
        <span v-if="data.duration" class="duration">{{ formatDuration(data.duration) }}</span>
      </span>
    </el-tree>
  </div>
</template>

<script>
export default {
  props: {
    chapters: {
      type: Array,
      required: true
    }
  },
  data() {
    return {
      defaultProps: {
        children: 'sections',
        label: 'title'
      }
    }
  },
  methods: {
    handleNodeClick(data) {
      if (data.videoUrl) {
        this.$emit('play', data);
      }
    }
  }
}
</script>

2. 视频播放器封装

<template>
  <div class="video-player">
    <video
      ref="videoPlayer"
      :src="videoUrl"
      @timeupdate="handleTimeUpdate"
      @ended="handleVideoEnd"
    ></video>
    <div class="controls">
      <el-slider
        v-model="currentTime"
        :max="duration"
        @change="seekTo"
      ></el-slider>
      <div class="actions">
        <el-button @click="togglePlay">{{ playing ? '暂停' : '播放' }}</el-button>
        <span class="time">{{ formatTime(currentTime) }} / {{ formatTime(duration) }}</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    videoUrl: String
  },
  data() {
    return {
      playing: false,
      currentTime: 0,
      duration: 0
    }
  },
  methods: {
    togglePlay() {
      if (this.playing) {
        this.$refs.videoPlayer.pause();
      } else {
        this.$refs.videoPlayer.play();
      }
      this.playing = !this.playing;
    },
    seekTo(time) {
      this.$refs.videoPlayer.currentTime = time;
    }
  }
}
</script>

五、学习进度跟踪

1. 进度状态管理

// store/modules/progress.js
const state = {
  courseProgress: {}
};

const mutations = {
  UPDATE_PROGRESS(state, { courseId, sectionId, progress }) {
    if (!state.courseProgress[courseId]) {
      state.courseProgress[courseId] = {};
    }
    state.courseProgress[courseId][sectionId] = progress;
  }
};

const actions = {
  saveProgress({ commit }, payload) {
    return api.saveProgress(payload).then(() => {
      commit('UPDATE_PROGRESS', payload);
    });
  }
};

export default {
  namespaced: true,
  state,
  mutations,
  actions
};

2. 进度可视化组件

<template>
  <div class="progress-tracker">
    <div class="progress-bar">
      <div
        class="progress"
        :style="{ width: `${percentage}%` }"
      ></div>
    </div>
    <div class="stats">
      <span>已完成 {{ completedCount }} / {{ totalCount }} 节</span>
      <span>{{ percentage }}%</span>
    </div>
  </div>
</template>

<script>
import { mapGetters } from 'vuex';

export default {
  props: {
    courseId: {
      type: String,
      required: true
    }
  },
  computed: {
    ...mapGetters('progress', ['getCourseProgress']),
    progressData() {
      return this.getCourseProgress(this.courseId) || {};
    },
    completedCount() {
      return Object.values(this.progressData).filter(p => p >= 0.9).length;
    },
    totalCount() {
      return Object.keys(this.progressData).length;
    },
    percentage() {
      return this.totalCount > 0 
        ? Math.round((this.completedCount / this.totalCount) * 100)
        : 0;
    }
  }
}
</script>

六、多角色权限控制

1. 动态路由配置

// router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import { getToken } from '@/utils/auth';

Vue.use(Router);

const createRouter = () => new Router({
  mode: 'history',
  routes: [
    {
      path: '/login',
      component: () => import('@/views/Login.vue'),
      meta: { public: true }
    },
    {
      path: '/',
      component: () => import('@/layouts/MainLayout.vue'),
      children: [
        // 公共路由
        {
          path: '',
          component: () => import('@/views/Home.vue')
        }
      ]
    }
  ]
});

const router = createRouter();

// 动态添加路由
export function addRoutes(routes) {
  const mainRoute = router.options.routes.find(r => r.path === '/');
  routes.forEach(route => {
    mainRoute.children.push(route);
  });
  router.addRoutes([mainRoute]);
}

// 路由守卫
router.beforeEach((to, from, next) => {
  const isPublic = to.matched.some(record => record.meta.public);
  const isAuthenticated = getToken();
  
  if (!isPublic && !isAuthenticated) {
    return next('/login');
  }
  
  if (to.path === '/login' && isAuthenticated) {
    return next('/');
  }
  
  next();
});

export default router;

2. 权限指令实现

// directives/permission.js
import store from '@/store';

function checkPermission(el, binding) {
  const { value } = binding;
  const roles = store.getters && store.getters.roles;
  
  if (value && value instanceof Array) {
    if (value.length > 0) {
      const hasPermission = roles.some(role => {
        return value.includes(role);
      });
      
      if (!hasPermission) {
        el.parentNode && el.parentNode.removeChild(el);
      }
    }
  } else {
    throw new Error(`需要指定权限数组,如 v-permission="['admin']"`);
  }
}

export default {
  inserted(el, binding) {
    checkPermission(el, binding);
  },
  update(el, binding) {
    checkPermission(el, binding);
  }
};

七、支付系统集成

1. 支付组件实现

<template>
  <div class="payment-container">
    <el-radio-group v-model="paymentMethod">
      <el-radio label="wechat">微信支付</el-radio>
      <el-radio label="alipay">支付宝</el-radio>
    </el-radio-group>
    
    <div v-if="paymentMethod === 'wechat'" class="qrcode-wrapper">
      <div ref="qrcode"></div>
      <p>请使用微信扫码支付</p>
    </div>
    
    <el-button type="primary" @click="handleSubmit">确认支付</el-button>
  </div>
</template>

<script>
import QRCode from 'qrcodejs2';

export default {
  props: {
    orderId: {
      type: String,
      required: true
    },
    amount: {
      type: Number,
      required: true
    }
  },
  data() {
    return {
      paymentMethod: 'wechat',
      qrcode: null
    }
  },
  methods: {
    initQRCode(url) {
      this.$refs.qrcode.innerHTML = '';
      this.qrcode = new QRCode(this.$refs.qrcode, {
        text: url,
        width: 200,
        height: 200
      });
    },
    async handleSubmit() {
      try {
        const res = await this.$api.createPayment({
          orderId: this.orderId,
          amount: this.amount,
          method: this.paymentMethod
        });
        
        if (this.paymentMethod === 'wechat') {
          this.initQRCode(res.qrcodeUrl);
          this.checkPaymentStatus();
        } else {
          window.location.href = res.paymentUrl;
        }
      } catch (err) {
        this.$message.error(err.message);
      }
    }
  }
}
</script>

八、总结与扩展

通过本教程,您已经掌握了:

  1. WebRTC直播系统开发
  2. 课程管理系统实现
  3. 学习进度跟踪技术
  4. 多角色权限控制方案
  5. 支付系统集成方法

扩展学习方向:

  • 移动端适配优化
  • SSR服务端渲染
  • 微前端架构改造
  • 大数据可视化分析
Vue2大型在线教育平台开发实战:从直播功能到课程管理全流程解析 | 前端架构设计
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 vue2 Vue2大型在线教育平台开发实战:从直播功能到课程管理全流程解析 | 前端架构设计 https://www.taomawang.com/web/vue2/898.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务