
简介面向教师群体的网格排版微信小程序页面源码包以内置格子化版面划分能力帮助教师快速生成教学课件、教案或演示材料的排版结构。源码整体基于微信小程序框架开发包含完整前端页面与基础交互逻辑适合学习小程序项目结构或二次开发定制。包体共128个文件1.08MB其中wxml定义页面骨架、wxss控制排版样式、js承载网格计算与二维码/加密等工具函数json用于项目配置png/jpg则为界面图标和预览素材。目前已有59人学习下载从内容预览看包内提供页面效果图、数据库指南及通用工具模块便于理解小程序从界面到数据的完整链路。对于关注教育工具开发、希望快速搭建网格排版类小程序页面的开发者这套源码可省去从零搭建的时间直接按需修改使用。1. 网格排版教师工具箱把教案排版做成可复用的小程序打开grid-template-columns: repeat(3, 1fr)这个 CSS 声明网格版式其实就站在了现代布局的地基上。教师工具箱这个微信小程序页面源码是把「网格排版 教师备课」这组组合拳打到小程序端老师选一个网格模板把课程素材、学生头像、作业二维码按格填进去生成可展示、可保存、可继续编辑的电子教案。它和普通图片模板最大的区别是网格参数是数据不是画死的像素所以换科目、换班级时不用重画图。压缩包里那六张 jpghfewm、laface、hfface、jsface、jsewm、laewm就是素材位和生成效果预览配合 weapp-qrcode.js、md5.js、base64.js 和 databaseGuide.js 这几个工具模块正好覆盖二维码生成、参数签名、编码传输和云数据库这四个高频需求。适合正在做教学工具、教育类小程序或者想研究小程序页面如何与网格布局结合的人直接拆包改。2. 页面结构与 WXML/WXSS 网格布局拆解2.1 压缩包内文件与页面注册关系先看包里的实际素材hfewm.jpg、jsewm.jpg、laewm.jpg这三张文件名以ewm结尾是「二维码」的拼音缩写laface.jpg、hfface.jpg、jsface.jpg以face结尾对应「老师」「画风/封面」「教师」的界面预览或头像占位图。也就是说这个项目在做两类核心页面一类是二维码生成页把课程地址编码成码另一类是带人物/头像网格的排版页。源码包里没有直接列出.wxml、.wxss、.js但微信小程序页面必须由四个文件组成所以实际工程结构至少是这样的pages/ ├── index/ │ ├── index.wxml │ ├── index.wxss │ ├── index.js │ └── index.json ├── grid/ │ ├── grid.wxml │ ├── grid.wxss │ ├── grid.js │ └── grid.json utils/ ├── weapp-qrcode.js ├── md5.js └── base64.js databaseGuide.js app.jsonapp.json是全局配置文件里面注册所有页面路径和窗口样式。拿到源码后第一步就是把pages数组里的路径补齐缺哪个页面建哪个目录否则真机预览直接报module not found。2.2 WXML 网格模板的构建方式网格排版在小程序里最优选是grid布局而不是flex 百分比因为网格天然支持「跨列、跨行、固定轨道宽度」。以教师工具箱的课堂素材页为例模板结构通常是这样view classgrid-board view classgrid-cell grid-cell--face wx:for{{faceList}} wx:keyid image src{{item.src}} modeaspectFill/image /view view classgrid-cell grid-cell--span2 wx:for{{qrList}} wx:keyid canvas canvas-idqrCanvas-{{item.id}} classqr-canvas/canvas /view /view这段 WXML 定义了两类单元格普通头像格和跨双列的二维码格。wx:for把数据数组渲染成重复视图wx:key必须设置为数据里唯一的字段这里用id否则列表项复用时会出样式错乱。canvas是二维码绘制的载体每个 canvas 的canvas-id要带上 item 的 id 才能区分多个二维码。grid布局对应的 WXSS 写法如下.grid-board { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16rpx; padding: 24rpx; background: #f7f8fa; border-radius: 16rpx; } .grid-cell { background: #ffffff; border-radius: 12rpx; overflow: hidden; } .grid-cell--face { height: 160rpx; } .grid-cell--face image { width: 100%; height: 100%; } .grid-cell--span2 { grid-column: span 2; }repeat(4, 1fr)表示把容器等分成 4 列每列宽度为剩余空间的一等分gap是行列间距grid-column: span 2让二维码单元格横跨两列形成非对称的视觉节奏。这种「跨列」能力是 flex 布局很难直接做到的也是网格排版在教案里好用的原因教师可以把某节课的素材图片放大占两格把二维码放旁边既不破坏整体对齐又突出了重点信息。2.3 数据驱动网格内容和响应式细节真正的网格排版不是把图片写死在 WXML 里而是通过data对象控制。教师工具箱的页面js中通常维护一份网格配置数据faceList和qrList都来自数据绑定。常见做法是给每个网格单元格配置rowSpan、colSpan、type三个字段然后渲染时动态拼接 classPage({ data: { gridConfig: [ { id: 1, type: face, src: /assets/laface.jpg, rowSpan: 1, colSpan: 1 }, { id: 2, type: qr, colSpan: 2, rowSpan: 1, courseUrl: https://course.example.com/lesson/101 } ] }, onLoad() { // 模拟从后端或云数据库加载模板配置 const config this.data.gridConfig.map((item) { if (item.type face) { item.classList grid-cell grid-cell--r${item.rowSpan} grid-cell--c${item.colSpan}; } return item; }); this.setData({ gridConfig: config }); } });这里的rowSpan/colSpan是网格的核心参数改数字就是改版式不用动布局代码。setData是小程序唯一的视图更新入口必须把修改后的完整数组交进去直接改this.data.gridConfig不会触发渲染。真机调试时如果发现界面没变先确认是否漏了setData。2.4 素材命名规则与换肤机制hfewm.jpg、laface.jpg、jsface.jpg这种命名实际上是素材角色的前缀约定la代表「老师」js代表「教师/教学」hf代表「画风」。按前缀把素材归到不同目录换肤时只需要替换图片资源路径或改用网络图片 URL。微信小程序的image组件支持webp和懒加载设置lazy-load{{true}}可以避免一次加载过多网格图片造成页面卡顿。素材量超过 10 张时我一般会改用https://域名托管并配置downloadFile合法域名避免包体超过 2MB 主包限制。3. 核心工具链weapp-qrcode.js 实战与 md5/base64 参数封装3.1 weapp-qrcode.js 生成课程二维码weapp-qrcode.js是纯前端二维码生成库不依赖后端接口它的核心原理是把文本内容按 QR 码规范编码成矩阵再逐点绘制到 canvas 上。在教师工具箱里它的典型用法是给每个班级生成独立的「作业入口码」或「课程资料码」。示例代码如下const drawQrcode require(../../utils/weapp-qrcode.js); Page({ data: { qrText: }, onLoad(options) { const courseId options.courseId || 20201222; const teacherId T1024; const ts Date.now(); // md5 签名防止课程参数被篡改 const token md5(courseId teacherId ts salt); const payload base64.encode(JSON.stringify({ courseId, teacherId, ts, token })); this.setData({ qrText: payload }); }, onReady() { drawQrcode({ width: 200, height: 200, canvasId: classQrcode, text: this.data.qrText, correctLevel: drawQrcode.qrcodeErrorCorrectLevel.H }); } });代码逻辑分三步构造携带课程信息的对象用md5生成签名再用base64把 JSON 压缩成适合二维码的短文本。生成二维码时width/height是输出尺寸单位是 px不是 rpx想要高清图就传两倍值然后在 WXSS 里缩一半correctLevel: H表示最高纠错级别容错率约 30%适合打印出来贴在作业本上有折痕或污损也能扫出。3.2 md5.js 做参数签名防篡改教学场景里二维码常被发到班级群如果直接把courseId明文放进码里学生改个参数就能访问别的课程资料。md5.js在这里的作用不是加密而是签名把courseId teacherId 时间戳 固定盐值拼接后计算哈希服务端收到参数后按同样的规则重算不一致就拒绝请求。这种方式能防「随手改参数」但防不了「完整重放」所以时间戳要限制在 5 分钟内有效。3.3 base64.js 解决 URL 传参乱码微信小程序的页面跳转使用wx.navigateTo的url参数传递这个参数有长度限制并且对特殊字符敏感。课程名称如果含中文、空格或不编码直接拼接就会出现参数被截断的问题。base64.js的封装原理是先用encodeURIComponent做一层安全编码再把结果 base64 化避免 URL 里出现%和的歧义。解析侧用对称的decode还原 JSON 对象这样在onLoad里拿到的就是一个干净的参数对象wx.navigateTo({ url: /pages/class/class?data${encodeURIComponent(base64.encode(JSON.stringify(payload)))} });接收端只需三行代码解包const compressed decodeURIComponent(options.data); const payload JSON.parse(base64.decode(compressed)); this.setData({ courseTitle: payload.title });3.4 工具函数封装进公共模块不建议每个页面单独require这三个库而是统一封装到utils/auth.js里页面只暴露一个方法getSecureUrl(type, params)const md5 require(./md5.js); const base64 require(./base64.js); const SECRET your-custom-salt; exports.getSecureUrl function (type, params) { const obj Object.assign({}, params, { type, ts: Date.now(), }); obj.token md5(JSON.stringify(obj) SECRET); return base64.encode(JSON.stringify(obj)); };这个封装把签名、编码、埋点字段都收拢到一个入口后续要换加密算法或加渠道号只改一个文件。md5计算前用JSON.stringify做序列化保证对象属性顺序一致否则前后端生成签名会不匹配。4. databaseGuide.js 与云开发数据库的对接设计4.1 databaseGuide.js 文件的作用databaseGuide.js在微信小程序工程里通常是「云开发数据库使用指南/示例」模块它演示了如何初始化云环境、创建集合、写入数据和查询数据。教师工具箱这个场景网格模板和课程素材不可能写死在前端必须保存在云端因此这个文件的核心就是打通「小程序前端 → 云数据库」的链路。标准初始化代码如下wx.cloud.init({ env: your-env-id, traceUser: true }); const db wx.cloud.database();env是云开发控制台的环境 IDtraceUser开启后会在数据库记录操作用户的 OpenID方便统计哪位老师上传了哪份教案。wx.cloud.database()返回数据库引用后续的增删改查都通过这个对象完成。4.2 网格模板集合的字段结构设计教师工具箱的数据库集合建议拆成三张表grid_templates网格模板、course_materials课程素材、teacher_profile教师信息。grid_templates的结构直接服务网格排版功能{ _id: tpl_001, name: 四列授课模板, cols: 4, rows: 6, cells: [ { x: 0, y: 0, type: face, colSpan: 1, rowSpan: 1 }, { x: 1, y: 0, type: qr, colSpan: 2, rowSpan: 2 }, { x: 3, y: 0, type: text, colSpan: 1, rowSpan: 1 } ], createTime: 2020-12-22 10:30:00 }cells数组里的x/y是单元格起点坐标colSpan/rowSpan控制跨度和高度前端拿到这个配置后可以动态生成 WXML 里的view网格。这样调整排版不用发版数据库里改一条记录所有老师刷新就能看到新版式。4.3 数据写入与条件查询实战教师上传新模板时通过add操作写入addTemplate(tplData) { const db wx.cloud.database(); return db.collection(grid_templates).add({ data: { ...tplData, createTime: db.serverDate() } }); }db.serverDate()是云数据库专用时间函数取的是服务器时间而不是用户手机本地时间避免因手机时钟不准导致排序错误。查询「某个老师最近用的 10 个模板」时要用组合条件async getRecentTemplates(teacherId, page 0) { const db wx.cloud.database(); const res await db.collection(grid_templates) .where({ teacherId }) .orderBy(createTime, desc) .skip(page * 10) .limit(10) .get(); return res.data; }orderBy必须与where组合使用skip实现分页偏移limit限制每页条数。云数据库默认单次get最多返回 20 条所以分页参数不能省。注意orderBy的字段需要提前在控制台建立索引否则查询量上来后会报-502005 database operation fail。4.4 权限规则与内容安全数据库集合的默认权限是「仅创建者可读写」但模板素材库属于公共资源需要改为「所有用户可读仅创建者可写」。在云开发控制台的「数据库 → 权限设置」里选择自定义规则{ read: true, write: doc._openid auth.openid }read: true表示所有登录用户都可读write表达式限制只有文档创建者_openid匹配才能修改。这样学生扫码只能读课程资料改不了模板内容老师上传的素材则归属到自己的 OpenID 下防止误删他人资源。5. 二次开发关键点模板参数同步、Canvas 存图与真机排错拿到压缩包后解压 zip 时先检查文件名是否乱码Windows 自带解压对中文文件名容易出现编码问题建议用支持 UTF-8 的解压工具如 Bandizip。解压后不要急着改代码先打开app.json确认页面注册路径与实际文件一致再用微信开发者工具「导入项目」填写自己的 AppID。没有 AppID 就选「测试号」但云开发wx.cloud.init会失败需要临时注释掉初始化逻辑。进阶调试时最常遇到的是两类问题。第一canvas 二维码生成后页面空白检查onReady里是否在 canvas 渲染完成后才调用绘制函数第二同一页面多个二维码只有最后一个显示drawQrcode绘制时 canvasId 必须唯一且绘制前调用wx.createSelectorQuery获取节点宽度作为 canvas 实际像素值。工具栏里「保存到相册」功能用wx.canvasToTempFilePath导出wx.canvasToTempFilePath({ canvasId: classQrcode, success(res) { wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: () wx.showToast({ title: 已保存, icon: success }) }); } });saveImageToPhotosAlbum需要用户授权第一次调用会弹窗用户拒绝后要在fail回调里调用wx.openSetting引导开启权限否则二次点击直接没反应。网格模板的跨页面同步可以用wx.setStorageSync保存模板 JSON下次进入页面时优先读取本地缓存再异步拉取云端最新版这样弱网环境下也能先展示旧模板。素材替换的小技巧是保持文件名前缀和尺寸不变直接覆盖同目录图片image组件默认带缓存开发者工具里勾选「真机调试不校验合法域名」并用版本号参数?v20201227强制刷新。最后把整个工程目录压缩回 zip 时记得排除node_modules和miniprogram_npm这样交出去的源码包体积小、还原度高换到别人电脑上用微信开发者工具重新构建就能跑起来。本文还有配套的精品资源点击获取