尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

微信小程序云开发实战:在线视频教育+作品集展示全栈解决方案

微信小程序云开发实战:在线视频教育+作品集展示全栈解决方案 最近在开发一个面向教育领域的微信小程序时遇到了一个典型需求如何在一个小程序内既集成在线视频学习功能又能让学员便捷地展示自己的学习成果作品集。市面上要么是纯视频平台要么是静态作品展示将两者深度结合并提供完整源码参考的资料并不多见。本文将分享一个完整的“在线视频教育作品集展示”微信小程序实战项目从项目构思、技术选型、前后端开发到部署上线的全流程进行拆解并提供核心源码。无论你是想学习微信小程序全栈开发还是需要为教育机构快速搭建一个轻量级学习平台这篇文章都能提供一条清晰的路径。1. 项目背景与核心概念1.1 什么是“在线视频教育作品集展示”小程序这类小程序主要服务于技能培训、艺术教育、IT编程等注重实践成果的领域。其核心功能模块通常包含两部分在线视频教育系统提供课程分类、视频点播、学习进度跟踪、讲师介绍等功能构成学习的主线。作品集展示平台为学员提供一个上传、管理、展示个人作业、项目成果或创意作品的空间形成学习的闭环和成果输出。将两者结合小程序不仅能传递知识更能激发学员的创作和分享热情增强学习社区的互动性与粘性同时也为教育机构提供了检验教学成果和进行口碑营销的窗口。1.2 为什么选择微信小程序触达便捷依托微信生态用户无需下载安装扫码或搜索即可使用降低了使用门槛。开发成本相对较低相比于原生App小程序开发周期短技术栈统一前端主要为WXML/WXSS/JS且腾讯云提供了一系列开箱即用的后端能力如云开发。生态成熟拥有完善的支付、登录、消息订阅、内容安全等API非常适合教育这类需要用户身份和轻度交互的场景。易于分享作品集可以很方便地分享给微信好友或朋友圈利于传播。1.3 技术栈选型说明本项目采用微信小程序原生框架MINA框架进行前端开发后端则提供了两种主流方案供选择以适应不同开发者的需求和资源方案A推荐给全栈初学者/快速原型微信小程序云开发。使用云函数、云数据库、云存储构建无服务器后端无需自备服务器和域名前后端均在微信开发者工具内完成运维简单。方案B推荐给已有后端服务的团队Spring Boot MyBatis-Plus MySQL。这是一个经典的Java后端技术栈结构清晰易于扩展适合需要复杂业务逻辑或与现有系统整合的场景。本文将重点阐述方案A云开发的实现因为其与小程序集成度最高、上手最快并在最后会简要对比方案B的关键差异点。2. 环境准备与项目初始化2.1 基础环境准备注册微信小程序账号访问微信公众平台注册一个小程序账号获取唯一的AppID。安装开发者工具下载并安装最新版本的微信开发者工具。开通云开发服务如果采用方案A在微信开发者工具中创建云开发环境获得环境IDenv。云开发提供免费的资源额度足够初期使用。2.2 创建小程序项目打开微信开发者工具选择“新建项目”项目名称例如EduPortfolioMiniProgram。目录选择你的本地开发目录。AppID填写你从公众平台获取的AppID。开发模式选择“小程序”。后端服务选择“微信云开发”这将自动生成云开发相关初始化代码。点击“新建”即可。项目创建成功后目录结构大致如下EduPortfolioMiniProgram/ ├── cloudfunctions/ # 云函数目录方案A核心 │ └── ... ├── miniprogram/ # 小程序前端页面目录 │ ├── pages/ # 页面文件 │ ├── components/ # 自定义组件 │ ├── images/ # 静态图片资源 │ ├── app.js # 小程序入口文件 │ ├── app.json # 全局配置 │ ├── app.wxss # 全局样式 │ └── ... ├── project.config.json # 项目配置文件 └── ...2.3 初始化云开发环境方案A在app.js的onLaunch生命周期函数中你会看到自动生成的云开发初始化代码。确保env字段替换为你自己的云环境ID。// app.js App({ onLaunch: function () { if (!wx.cloud) { console.error(请使用 2.2.3 或以上的基础库以使用云能力); } else { wx.cloud.init({ // 此处请填入环境 ID, 环境 ID 可打开云控制台查看 env: your-env-id, traceUser: true, // 记录用户访问 }); } // ... 其他初始化代码 } });在开发者工具中点击左上角“云开发”图标开通并进入云控制台。在这里你可以管理云数据库、云存储和云函数。3. 数据库设计与核心配置3.1 云数据库集合设计方案A在云开发控制台的“数据库”中创建以下核心集合类似于MySQL中的表courses课程表_id: ObjectId 主键title: String 课程标题coverUrl: String 封面图云存储地址teacher: String 讲师姓名intro: String 课程简介videoList: Array 视频列表每个元素包含name章节名和fileID视频文件云存储IDcreateTime: Date 创建时间portfolios作品集表_id: ObjectIdopenid: String 上传者用户的openid自动关联title: String 作品标题description: String 作品描述images: Array 作品图片云存储ID数组videoFileID: String 作品视频云存储ID可选category: String 作品分类如编程、设计、绘画likes: Number 点赞数comments: Array 评论列表createTime: Dateuser-study-progress用户学习进度表_id: ObjectIdopenid: StringcourseId: String 对应courses._idlastWatchedVideoIndex: Number 上次观看的视频索引progress: Number 整体进度百分比updateTime: Date3.2 关键小程序配置app.jsonapp.json文件需要配置页面路径、窗口样式以及用到的权限。{ pages: [ pages/index/index, // 首页 pages/course/list/list, // 课程列表页 pages/course/detail/detail, // 课程详情/播放页 pages/portfolio/upload/upload, // 作品上传页 pages/portfolio/gallery/gallery, // 作品集画廊页 pages/portfolio/detail/detail, // 作品详情页 pages/my/my // 个人中心页 ], window: { navigationBarTitleText: 学艺坊, navigationBarBackgroundColor: #4a90e2, navigationBarTextStyle: white }, tabBar: { color: #999, selectedColor: #4a90e2, list: [ { pagePath: pages/index/index, text: 首页, iconPath: images/tab-home.png, selectedIconPath: images/tab-home-active.png }, { pagePath: pages/course/list/list, text: 课程, iconPath: images/tab-course.png, selectedIconPath: images/tab-course-active.png }, { pagePath: pages/portfolio/gallery/gallery, text: 作品, iconPath: images/tab-portfolio.png, selectedIconPath: images/tab-portfolio-active.png }, { pagePath: pages/my/my, text: 我的, iconPath: images/tab-my.png, selectedIconPath: images/tab-my-active.png } ] }, requiredPrivateInfos: [chooseImage, chooseVideo, uploadFile], // 需要用户授权相册/相机 permission: { scope.userLocation: { desc: 你的位置信息将用于展示同城学员 } }, usingComponents: {}, sitemapLocation: sitemap.json }4. 核心功能模块实现4.1 在线视频播放模块这是教育系统的核心。微信小程序提供了video组件但直接使用云存储的文件ID需要先获取临时链接。1. 课程列表页 (pages/course/list/list)// pages/course/list/list.js Page({ data: { courseList: [] }, onLoad() { this.fetchCourseList(); }, async fetchCourseList() { const db wx.cloud.database(); try { const res await db.collection(courses).orderBy(createTime, desc).get(); this.setData({ courseList: res.data }); } catch (err) { console.error(获取课程列表失败:, err); wx.showToast({ title: 加载失败, icon: none }); } }, // 跳转到课程详情页 goToDetail(e) { const id e.currentTarget.dataset.id; wx.navigateTo({ url: /pages/course/detail/detail?id${id} }); } });对应的WXML使用wx:for循环渲染课程卡片。2. 课程详情与视频播放页 (pages/course/detail/detail)关键点在于获取视频的临时播放地址。// pages/course/detail/detail.js Page({ data: { course: null, videoSrc: , currentVideoIndex: 0 }, onLoad(options) { this.courseId options.id; this.loadCourseDetail(); this.loadUserProgress(); // 加载用户学习进度 }, async loadCourseDetail() { const db wx.cloud.database(); try { const res await db.collection(courses).doc(this.courseId).get(); const course res.data; this.setData({ course }); // 加载第一个视频 if (course.videoList course.videoList.length 0) { this.loadVideoByIndex(0); } } catch (err) { console.error(获取课程详情失败:, err); } }, async loadVideoByIndex(index) { const fileID this.data.course.videoList[index].fileID; try { // 获取视频文件的临时链接有效期为2小时 const { tempFileURL } await wx.cloud.getTempFileURL({ fileList: [fileID] }); this.setData({ videoSrc: tempFileURL[0].tempFileURL, currentVideoIndex: index }); // 更新学习进度到云数据库 this.updateStudyProgress(index); } catch (err) { console.error(获取视频链接失败:, err); } }, // 更新学习进度到云函数或直接操作数据库 async updateStudyProgress(videoIndex) { const db wx.cloud.database(); const _ db.command; await db.collection(user-study-progress).where({ _openid: {openid}, // 云数据库会自动填充_openid字段 courseId: this.courseId }).update({ data: { lastWatchedVideoIndex: videoIndex, progress: Math.round(((videoIndex 1) / this.data.course.videoList.length) * 100), updateTime: new Date() } }); }, // 切换章节 onVideoChange(e) { const index e.detail.value; // 假设通过picker选择 this.loadVideoByIndex(index); } });!-- pages/course/detail/detail.wxml -- view classcontainer video src{{videoSrc}} controls autoplay/video view classcourse-info text classtitle{{course.title}}/text text讲师{{course.teacher}}/text text{{course.intro}}/text /view picker range{{course.videoList}} range-keyname value{{currentVideoIndex}} bindchangeonVideoChange view当前播放{{course.videoList[currentVideoIndex].name}}/view /picker /view4.2 作品集上传与展示模块1. 作品上传页 (pages/portfolio/upload/upload)涉及多图上传、视频上传和表单提交。// pages/portfolio/upload/upload.js Page({ data: { title: , description: , category: 编程, categoryList: [编程, 设计, 绘画, 音乐, 其他], images: [], // 本地临时路径 uploadedImageUrls: [], // 上传后的云文件ID数组 videoPath: , // 本地临时视频路径 uploadedVideoFileID: }, // 选择图片 chooseImage() { wx.chooseImage({ count: 9 - this.data.images.length, // 最多9张 sizeType: [compressed], success: (res) { this.setData({ images: this.data.images.concat(res.tempFilePaths) }); } }); }, // 选择视频 chooseVideo() { wx.chooseVideo({ sourceType: [album, camera], compressed: true, maxDuration: 60, // 最长60秒 success: (res) { this.setData({ videoPath: res.tempFilePath }); } }); }, // 上传文件到云存储 async uploadFiles() { const uploadTasks []; // 上传图片 for (let filePath of this.data.images) { const cloudPath portfolio/images/${Date.now()}-${Math.random()}.jpg; uploadTasks.push(wx.cloud.uploadFile({ cloudPath, filePath, })); } // 上传视频 if (this.data.videoPath) { const cloudPath portfolio/videos/${Date.now()}.mp4; uploadTasks.push(wx.cloud.uploadFile({ cloudPath, filePath: this.data.videoPath, })); } try { const results await Promise.all(uploadTasks); const imageFileIDs results.slice(0, this.data.images.length).map(r r.fileID); const videoFileID this.data.videoPath ? results[results.length - 1].fileID : ; this.setData({ uploadedImageUrls: imageFileIDs, uploadedVideoFileID: videoFileID }); return true; } catch (err) { console.error(文件上传失败:, err); wx.showToast({ title: 文件上传失败, icon: none }); return false; } }, // 提交表单到数据库 async submitPortfolio() { const { title, description, category, uploadedImageUrls, uploadedVideoFileID } this.data; if (!title.trim()) { wx.showToast({ title: 请输入作品标题, icon: none }); return; } wx.showLoading({ title: 发布中... }); // 1. 先上传文件 const uploadSuccess await this.uploadFiles(); if (!uploadSuccess) { wx.hideLoading(); return; } // 2. 再写入数据库 const db wx.cloud.database(); try { await db.collection(portfolios).add({ data: { title, description, category, images: uploadedImageUrls, videoFileID: uploadedVideoFileID, likes: 0, comments: [], createTime: new Date() } }); wx.hideLoading(); wx.showToast({ title: 发布成功 }); setTimeout(() { wx.navigateBack(); // 返回上一页 }, 1500); } catch (err) { wx.hideLoading(); console.error(发布失败:, err); wx.showToast({ title: 发布失败, icon: none }); } } });2. 作品集画廊页 (pages/portfolio/gallery/gallery)实现瀑布流或网格布局展示作品支持按分类筛选。// pages/portfolio/gallery/gallery.js Page({ data: { portfolioList: [], category: all, // 当前选中分类 categoryList: [all, 编程, 设计, 绘画, 音乐, 其他] }, onLoad() { this.loadPortfolios(); }, async loadPortfolios() { const db wx.cloud.database(); const _ db.command; let query db.collection(portfolios).orderBy(createTime, desc); // 分类筛选 if (this.data.category ! all) { query query.where({ category: this.data.category }); } try { const res await query.get(); // 获取作品封面图第一张图的临时URL const portfoliosWithCover await Promise.all(res.data.map(async item { if (item.images item.images.length 0) { const { tempFileURL } await wx.cloud.getTempFileURL({ fileList: [item.images[0]] }); item.coverUrl tempFileURL[0].tempFileURL; } return item; })); this.setData({ portfolioList: portfoliosWithCover }); } catch (err) { console.error(加载作品集失败:, err); } }, // 分类切换 onCategoryChange(e) { const category e.currentTarget.dataset.category; this.setData({ category }, () { this.loadPortfolios(); // 重新加载 }); } });4.3 个人中心与学习进度在“我的”页面可以展示用户信息、已学课程、我的作品等。// pages/my/my.js Page({ data: { userInfo: {}, myCourses: [], myPortfolios: [] }, onShow() { this.getUserInfo(); this.loadMyData(); }, getUserInfo() { // 使用微信登录能力 wx.getUserProfile({ desc: 用于展示头像和昵称, success: (res) { this.setData({ userInfo: res.userInfo }); } }); }, async loadMyData() { const db wx.cloud.database(); // 获取我的学习进度关联课程信息 const progressRes await db.collection(user-study-progress) .where({ _openid: {openid} }) .orderBy(updateTime, desc) .get(); // 获取我的作品 const portfolioRes await db.collection(portfolios) .where({ _openid: {openid} }) .orderBy(createTime, desc) .get(); this.setData({ myCourses: progressRes.data, myPortfolios: portfolioRes.data }); } });5. 后端方案BSpring Boot关键点对比如果选择自建后端方案B前端小程序需要与后端API进行通信。主要改动如下移除云开发初始化app.js中不再需要wx.cloud.init。配置服务器域名在微信公众平台配置request合法域名指向你的Spring Boot服务器地址如https://api.yourdomain.com。网络请求使用wx.request调用后端API。// 示例登录 wx.login({ success: (res) { const code res.code; wx.request({ url: https://api.yourdomain.com/auth/login, method: POST, data: { code }, success: (res) { // 存储后端返回的token wx.setStorageSync(token, res.data.token); } }); } }); // 示例获取课程列表 wx.request({ url: https://api.yourdomain.com/course/list, method: GET, header: { Authorization: Bearer ${wx.getStorageSync(token)} }, success: (res) { this.setData({ courseList: res.data }); } });文件上传图片和视频需要上传到你的后端服务器或第三方对象存储如OSS、COS后端再返回访问URL给前端。后端API设计Spring Boot需要提供相应的Controller处理用户认证、课程CRUD、作品集CRUD、文件上传等接口并连接MySQL数据库。6. 常见问题与排查思路问题现象常见原因解决思路云开发初始化失败1. 未开通云开发。2.env环境ID填写错误。3. 基础库版本过低。1. 在开发者工具开通云开发。2. 检查app.js中的env是否与控制台环境ID一致。3. 在project.config.json中设置libVersion: latest。chooseImage或chooseVideo无反应1. 未在app.json中声明权限。2. 用户拒绝了授权。1. 确保app.json的requiredPrivateInfos包含chooseImage和chooseVideo。2. 引导用户去“设置-隐私-相册/相机”中打开权限或使用wx.openSetting引导用户开启。视频无法播放1. 视频链接失效云存储临时链接过期。2. 视频格式或编码不支持。3. 服务器域名未配置方案B。1. 每次播放前重新调用wx.cloud.getTempFileURL获取新链接。2. 确保视频为H.264编码MP4格式。3. 在公众平台配置downloadFile和request合法域名。上传文件大小超限云开发或微信API有文件大小限制。云开发云存储单个文件上限为200MB。对于大视频建议先压缩或使用方案B配合分片上传。数据库查询权限错误云数据库权限设置过于严格。检查云数据库集合的权限规则。开发阶段可设置为“所有用户可读仅创建者可读写”上线后需根据业务细化规则。真机预览时无法请求数据1. 未开启“不校验合法域名”。2. 服务器域名未备案或未配置。1. 开发者工具右上角详情-本地设置勾选“不校验合法域名...”。2. 正式上线前必须在公众平台配置所有请求的服务器域名HTTPS。getUserProfile接口返回失败用户点击了拒绝按钮。做好友好的失败处理提示用户需要授权才能使用完整功能并提供再次授权的入口。7. 最佳实践与工程建议云开发环境隔离至少创建两个云环境dev-开发,prod-生产使用不同的环境ID。避免开发数据污染线上数据。数据库索引优化对于频繁查询的字段如portfolios表的category、createTime应在云控制台或MySQL中建立索引提升查询性能。图片与视频处理压缩上传前使用小程序API或前端库对图片进行压缩。CDN加速云存储和OSS都自带CDN确保资源加载速度。视频封面上传视频时可考虑使用云函数异步生成封面图避免在前端处理消耗用户流量和时间。权限与安全云数据库权限生产环境务必使用自定义安全规则遵循最小权限原则。例如作品集表可设置为“所有人可读仅创建者可写可删”。内容安全对用户上传的图片、视频、文本描述使用微信提供的security.mediaCheckAsync和security.msgSecCheck接口进行内容安全检测防范违规内容。防刷与限流对于点赞、评论等交互接口在云函数或后端API中实现简单的频率限制如同一用户10秒内只能点赞一次。用户体验优化骨架屏在列表数据加载前使用骨架屏Skeleton Screen提升感知速度。下拉刷新与上拉加载列表页务必实现onPullDownRefresh和onReachBottom提供流畅的浏览体验。本地缓存对于不常变的课程分类、首页配置等数据可以使用wx.setStorage进行本地缓存减少网络请求。代码结构组件化将通用的课程卡片、作品卡片、视频播放器控件等抽离成自定义组件提高代码复用性和可维护性。状态管理对于跨多个页面的用户状态如登录态可以使用getApp().globalData或引入轻量级状态管理库如mobx-miniprogram。错误统一处理封装网络请求函数在其中统一处理错误提示和登录态失效的重定向。部署与发布体验版测试开发完成后上传为体验版邀请真实用户测试。提交审核确保小程序符合微信平台运营规范特别是教育类内容需具备相关资质虚拟支付需使用合规方式。监控与统计利用微信小程序后台的统计功能或接入云开发的监控关注用户访问、错误日志和性能数据。这个项目涵盖了微信小程序开发中的多个核心技能点云开发、组件使用、用户交互、文件上传、视频播放、列表渲染、前后端交互等。通过将在线视频学习与作品集展示相结合它不仅仅是一个demo更是一个具备实际应用场景的完整案例。你可以在此基础上继续扩展评论社区、积分系统、直播连麦等功能构建更丰富的在线教育生态。
返回列表