简介:这是一份面向高校计算机专业学生及微信小程序初学者的期末大作业实战项目,聚焦电商类应用开发能力培养,帮助学习者系统掌握小程序核心开发流程与典型业务实现。资源为ZIP压缩包,大小2.18MB,包含完整可运行的小程序源码工程(含pages、components、app.js等标准目录结构),涵盖首页轮播图(swiper组件)、底部五栏导航(navigator+页面路由)、商品分类标签交互、优惠券管理、购物车增删改查及个人中心基础模块,代码结构清晰、注释充分,便于理解页面生命周期、数据绑定、wx.request网络请求等关键知识点。目前已有2931人学习下载,适合用于课程设计参考、自学复现或二次开发练习,能直接获取一个功能完备、界面简洁、逻辑完整的童装商城原型,显著降低入门门槛并提升工程实践认知。
1. 微信小程序期末大作业——为什么“天使童装商城”是教学场景下最典型的练手项目?
很多计算机专业学生在做微信小程序期末大作业时,常陷入两个极端:要么选“天气预报”“待办清单”这类功能单薄、缺乏业务纵深的项目,答辩时被问一句“用户怎么下单?库存怎么扣?”就卡壳;要么盲目堆砌“AI推荐”“AR试穿”,结果连基础的商品列表渲染都报Cannot read property 'map' of undefined。而“天使童装商城”恰恰卡在黄金平衡点——它不依赖外部硬件或复杂算法,却完整覆盖小程序开发主干链路:用户登录态管理、多级分类导航、商品卡片渲染与点击跳转、购物车增删改查、订单提交模拟、本地缓存持久化。更重要的是,它天然适配教学节奏:首页轮播图+新品推荐模块可练 WXML 布局与 WXSS 样式调试;商品详情页的图片懒加载+规格选择可深入bindtap事件绑定与>"tabBar": { "list": [ { "pagePath": "pages/index/index", "text": "首页", "iconPath": "assets/icons/home.png", "selectedIconPath": "assets/icons/home-active.png" }, { "pagePath": "pages/cart/cart", "text": "购物车", "iconPath": "assets/icons/cart.png", "selectedIconPath": "assets/icons/cart-active.png" } ] }
此处iconPath必须为相对路径,且图片需放入assets/icons/目录,否则真机调试时 tabBar 图标不显示。
2.3 首页轮播图与商品瀑布流:WXML 布局与数据绑定的关键实践
首页index.wxml的核心结构需严格遵循微信小程序的渲染规则。轮播图使用<swiper>组件时,必须设置indicator-dots="true"和autoplay="true",但禁止直接在swiper-item内写wx:for—— 这会导致 swiper 内部渲染异常。正确做法是将轮播图数据bannerList存于index.js的data中,并在 WXML 中用wx:for渲染swiper-item:
<!-- pages/index/index.wxml --> <swiper indicator-dots="{{true}}" autoplay="{{true}}" interval="3000" duration="500"> <swiper-item wx:for="{{bannerList}}" wx:key="id"> <image src="{{item.imgUrl}}" mode="aspectFill" class="banner-img" /> </swiper-item> </swiper> <!-- 商品瀑布流:使用 flex 布局实现两列等宽 --> <view class="goods-grid"> <view class="goods-item" wx:for="{{goodsList}}" wx:key="id" bindtap="goToDetail">// pages/index/index.js Page({ data: { bannerList: [ { id: 1, imgUrl: '/assets/banners/banner1.jpg' }, { id: 2, imgUrl: '/assets/banners/banner2.jpg' } ], goodsList: [ { id: 101, name: '纯棉婴儿连体衣', price: 89.00, cover: '/assets/goods/101.jpg' }, { id: 102, name: '卡通儿童防晒帽', price: 45.50, cover: '/assets/goods/102.jpg' } ] }, // 点击商品跳转详情页 goToDetail(e) { const id = e.currentTarget.dataset.id; wx.navigateTo({ url: `/pages/detail/detail?id=${id}` }); } });注意:
wx:for的wx:key必须为唯一字段(如id),若用index会导致列表更新时视图错乱;bindtap事件中><!-- pages/detail/detail.wxml --> <view class="spec-group"> <text class="spec-label">尺码:</text> <radio-group bindchange="onSizeChange"> <label wx:for="{{sizeList}}" wx:key="value"> <radio value="{{item.value}}" checked="{{item.value === selectedSpecs.size}}" /> <text>{{item.text}}</text> </label> </radio-group> </view> <view class="spec-group"> <text class="spec-label">颜色:</text> <radio-group bindchange="onColorChange"> <label wx:for="{{colorList}}" wx:key="value"> <radio value="{{item.value}}" checked="{{item.value === selectedSpecs.color}}" /> <text>{{item.text}}</text> </label> </radio-group> </view> <text class="current-price">¥{{currentPrice}}</text>// pages/detail/detail.js Page({ data: { sizeList: [{ value: 'S', text: '90cm' }, { value: 'M', text: '100cm' }, { value: 'L', text: '110cm' }], colorList: [{ value: 'pink', text: '粉色' }, { value: 'blue', text: '蓝色' }], selectedSpecs: { size: 'S', color: 'pink' }, basePrice: 89.00, specPriceMap: { 'S-pink': 0, 'S-blue': 2.00, 'M-pink': 5.00, 'M-blue': 7.00, 'L-pink': 10.00, 'L-blue': 12.00 } }, onSizeChange(e) { const size = e.detail.value; this.setData({ 'selectedSpecs.size': size, currentPrice: this.calculatePrice(size, this.data.selectedSpecs.color) }); }, onColorChange(e) { const color = e.detail.value; this.setData({ 'selectedSpecs.color': color, currentPrice: this.calculatePrice(this.data.selectedSpecs.size, color) }); }, calculatePrice(size, color) { const key = `${size}-${color}`; return (this.data.basePrice + (this.data.specPriceMap[key] || 0)).toFixed(2); } });提示:
radio-group的bindchange事件中,e.detail.value是选中的value字符串,而非整个对象。this.setData更新currentPrice时,必须用toFixed(2)保证价格显示为两位小数,否则89.0会显示为89。3.2 购物车持久化:
wx.setStorageSync的安全封装与并发风险规避购物车数据必须本地持久化,否则用户退出小程序后购物车清空。但直接调用
wx.setStorageSync('cart', cartList)存在两个风险:一是cartList为数组时,若某项quantity为 0,未过滤会导致无效数据;二是多页面(详情页加购、购物车页修改)同时写入时,可能因异步执行顺序导致数据覆盖。解决方案是封装storage.js,增加原子操作与数据清洗:// utils/storage.js function setCart(cartList) { // 过滤 quantity <= 0 的项,并深拷贝避免引用污染 const cleanedCart = cartList .filter(item => item.quantity > 0) .map(item => ({ ...item })); try { wx.setStorageSync('cart', cleanedCart); } catch (e) { console.error('购物车存储失败:', e); } } function getCart() { try { const cart = wx.getStorageSync('cart'); return Array.isArray(cart) ? cart : []; } catch (e) { console.error('购物车读取失败:', e); return []; } } // 导出为模块 module.exports = { setCart, getCart };在购物车页
cart.js中,每次修改数量后立即调用setCart:// pages/cart/cart.js const storage = require('../../utils/storage.js'); Page({ data: { cartList: [] }, onLoad() { this.loadCart(); }, loadCart() { const cart = storage.getCart(); this.setData({ cartList: cart }); }, // 修改商品数量 changeQuantity(e) { const { id, delta } = e.currentTarget.dataset; // delta 为 +1 或 -1 const cart = storage.getCart(); const itemIndex = cart.findIndex(item => item.id == id); // 注意 == 兼容字符串ID if (itemIndex !== -1) { cart[itemIndex].quantity += delta; if (cart[itemIndex].quantity < 1) { cart.splice(itemIndex, 1); // 删除数量为0的商品 } storage.setCart(cart); this.setData({ cartList: cart }); } } });注意:
cart.findIndex中使用==而非===,是因为e.currentTarget.dataset.id是字符串,而商品 ID 可能为数字,强制类型转换可避免匹配失败。4. 真机调试与抓包验证:用 Charles 抓取 PC 端微信小程序网络请求
4.1 配置 Charles 代理并安装根证书
“微信小程序抓包”和“charles抓包电脑端微信小程序”是学生调试接口时的刚需。PC 端微信(非微信开发者工具)的小程序运行在独立 WebView 中,需通过系统代理捕获其 HTTPS 流量。关键步骤如下:
- Charles 设置代理端口:打开 Charles → Proxy → Proxy Settings → 勾选 “Enable transparent HTTP proxying”,端口设为
8888;- Windows/macOS 系统代理指向 Charles:网络设置中,HTTP/HTTPS 代理地址设为
127.0.0.1:8888;- 安装 Charles 根证书:在 Charles → Help → SSL Proxying → Install Charles Root Certificate,按向导完成安装;
- 启用 SSL Proxying:在 Charles 左侧结构树中,右键
wechat.exe进程 → Enable SSL Proxying;- PC 微信中打开小程序:启动 PC 版微信 → 打开“天使童装商城”小程序 → 在 Charles 中观察
https://api.angelkids.com/类请求。提示:若 Charles 中无请求,检查 PC 微信是否启用了“使用系统代理”(微信设置 → 通用设置 → 勾选“使用系统代理”);若提示证书错误,需在 Windows 证书管理器中将 Charles 根证书导入“受信任的根证书颁发机构”。
4.2 解析抓包结果:识别登录态、商品列表与购物车接口
抓包后,重点关注三类请求的
Request Headers与Response Body:
接口类型 URL 示例 关键 Header Response 结构要点 登录态校验 GET https://api.angelkids.com/v1/user/sessionAuthorization: Bearer eyJhbGciOi...{"code": 200, "data": {"userId": "u123", "nickName": "小天使"}}商品列表 GET https://api.angelkids.com/v1/products?category=infantAccept: application/json{"code": 200, "data": [{"id": 101, "name": "连体衣", "price": 89.00}]}购物车提交 POST https://api.angelkids.com/v1/ordersContent-Type: application/json{"code": 201, "data": {"orderId": "O20240520123456"}}在
utils/api.js中,所有wx.request调用必须携带header.Authorization,且success回调中必须校验res.data.code:// utils/api.js function request(url, options = {}) { const token = wx.getStorageSync('token') || ''; return new Promise((resolve, reject) => { wx.request({ url: 'https://api.angelkids.com' + url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json', 'Authorization': token ? `Bearer ${token}` : '' }, success(res) { if (res.statusCode === 200) { if (res.data.code === 200 || res.data.code === 201) { resolve(res.data); } else { wx.showToast({ title: res.data.message || '请求失败', icon: 'none' }); reject(new Error(res.data.message)); } } else { wx.showToast({ title: '网络错误', icon: 'none' }); reject(new Error(`HTTP ${res.statusCode}`)); } }, fail(err) { wx.showToast({ title: '连接失败', icon: 'none' }); reject(err); } }); }); }注意:
wx.request的fail回调仅捕获网络层错误(如超时、DNS 失败),success中的res.statusCode !== 200才是业务层错误,二者必须分开处理,否则401 Unauthorized会被误判为“网络问题”。5. 期末答辩前必查的 7 个硬性指标与避坑指南
5.1 页面性能与体验:顶部导航栏高度与加载页优化
“微信小程序顶部导航栏高度”是真机适配的隐形雷区。微信小程序默认导航栏高度为
44px,但若app.json中window.navigationBarBackgroundColor设为透明,或使用wx.setNavigationBarColor动态修改,部分安卓机型会出现导航栏闪烁。更稳妥的做法是禁用原生导航栏,自定义navigation-bar组件:// app.json { "window": { "navigationStyle": "custom" } }然后在每个页面 WXML 顶部插入自定义导航:
<!-- pages/index/index.wxml --> <view class="custom-nav"> <text class="nav-title">天使童装商城</text> </view>/* pages/index/index.wxss */ .custom-nav { height: 44px; background: #fff; display: flex; align-items: center; justify-content: center; position: fixed; top: 0; left: 0; right: 0; z-index: 999; } .nav-title { font-size: 16px; font-weight: 500; color: #333; }同时,“修改刚进入的加载页面”需在
app.js的onLaunch中控制:// app.js App({ onLaunch() { // 显示加载中 wx.showLoading({ title: '加载中...' }); // 模拟登录检查 setTimeout(() => { const token = wx.getStorageSync('token'); if (!token) { wx.redirectTo({ url: '/pages/login/login' }); // 跳转登录 } else { wx.hideLoading(); // 隐藏加载 } }, 800); } });提示:
wx.showLoading必须配对wx.hideLoading,否则加载动画永不消失;setTimeout模拟网络延迟,真实项目中应替换为wx.checkSession+wx.login的链式调用。5.2 发布前自查清单:从代码规范到合规红线
期末大作业虽不发布上线,但需模拟真实发布流程。以下 7 项必须逐条验证:
序号 检查项 合规要求 验证方法 1 页面路径注册 app.json中pages数组必须包含所有.js文件路径,且顺序与tabBar一致检查 app.json与实际文件目录是否一一对应2 图片资源路径 所有 image的src必须以/开头(绝对路径),禁止../相对路径在开发者工具中预览,查看控制台是否有 404 报错 3 API 域名白名单 若调用 https://api.angelkids.com,需在微信公众平台「开发管理」→「开发设置」→「服务器域名」中添加api.angelkids.com登录 mp.weixin.qq.com,核对「request 合法域名」列表 4 用户隐私授权 wx.getUserProfile调用前,WXML 中必须有<button open-type="getUserInfo">或<button open-type="getPhoneNumber">检查登录页是否含授权按钮,且 bindgetuserinfo事件已绑定5 支付功能声明 题目明确“支付功能暂时无法使用”,代码中严禁出现 wx.requestPayment调用全局搜索 requestPayment,确保无任何调用6 跳转链接安全 禁止使用 weixin://dl/business等未备案的私有协议跳转全局搜索 weixin://,只允许wx.navigateTo、wx.switchTab等标准 API7 代码注释覆盖率 每个 Page的data、onLoad、onShow及核心事件函数必须有中文注释使用 HBuilderX 的「代码统计」插件,注释行占比 ≥ 30% 最后一步:在微信开发者工具中点击「真机调试」,用自己手机扫码,依次点击首页 → 商品列表 → 详情页 → 加入购物车 → 购物车页 → 修改数量 → 删除商品 → 返回首页。全程无红字报错、无白屏、无空白区域、所有图片正常加载、所有文字清晰可读——此时,你的“天使童装商城”才真正具备了交付答辩的底气。
本文还有配套的精品资源,点击获取