资讯动态

微信小程序商城项目实战:从工程结构到购物车下单全解析

发布时间:2026/9/14 14:28:32 来源:尧图企业网站定制
简介一套适合毕业设计或期末大作业使用的超精简商城微信小程序源码包面向计算机相关专业学生与小程序入门开发者。源码实现商品展示、购物车、订单等基础商城模块结构清晰、逻辑简洁便于二次改造与答辩讲解。压缩包共60个文件包含逻辑脚本、页面结构、样式表、界面图片、配置文件以及导入教学视频和文档说明整体大小约33.57MB目录按模块划分上手简单。目前已有441人学习下载对于需要快速完成课程项目或准备毕设演示的读者提供了一套可运行的参考实现搭配视频教程可减少环境配置和代码阅读的阻力。下载后可根据源码中的商品数据与页面设置快速替换为自己的项目内容十分适合期末答辩或毕业设计演示场景。1. 从大作业到答辩这份超精简商城小程序到底能拆出什么做微信小程序毕业设计最尴尬的状态不是没思路而是思路在脑子里转了几个星期代码一行没写。商城类小程序又是点名率最高的题目之一市面上源码包很多打开一看几百个文件光理顺 pages 之间的跳转关系就要一晚上。这份「超精简商城」恰恰是对着这个痛点来的目录干净、页面克制、没有云开发、没有后端服务纯原生微信小程序语法拿到手改改数据就能跑跑起来就能截图写论文。它的实际组成是一个 ZIP 包加一份 Word 教程和导入视频里面 smallAPP 目录就是完整工程。static 放静态图utils 做公共方法pages 管页面app.js/app.json/app.wxss 负责全局启动逻辑与样式。适合三类人一是拿它当毕业设计底座的在校生二是培训机构用来练手的入门者三是想快速验证一个商城交互原型的在职开发者。下面是把它彻底拆开的过程从工程结构、登录态、购物车到最终答辩演示一条线走完。2. 先把工程拆开目录结构、页面栈与全局配置的搭法2.1 pages、app.json、app.js 的分工边界微信小程序不像 Web 项目没有 index.html 作为唯一入口它的启动顺序是先读 app.json 注册页面再执行 app.js 的 App() 生命周期然后加载第一个页面。这份源码的 app.json 里注册的页面数量不到十个包括首页、商品详情、购物车、订单确认、个人中心等这个数量对期末大作业来说刚好——既能展示完整业务链路又不至于让答辩老师觉得你在拿复杂工程凑数。{ pages: [ pages/index/index, pages/detail/detail, pages/cart/cart, pages/order/order, pages/user/user ], window: { navigationBarTitleText: 精简商城, navigationBarBackgroundColor: #ffffff, navigationBarTextStyle: black }, tabBar: { list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/cart/cart, text: 购物车 }, { pagePath: pages/user/user, text: 我的 } ] } }pages 数组里的第一项就是小程序的启动首页tabBar 里配置的三个页面会常驻底部切换detail 和 order 这类二级页面则通过 wx.navigateTo 压栈跳转。注意这里有个工程细节tabBar 页面不能用 wx.navigateTo 跳转必须用 wx.switchTab源码里如果出现点了底部 tab 没反应的情况先检查是不是用了 navigateTo。app.js 里只做了两件事调用 wx.login 拿 code以及把全局的用户信息存到 globalData。不依赖后端接口数据都从本地静态文件读取这保证了整个项目在微信开发者工具里打开的一瞬间就能看到商品列表不用联调、不用 mock server。2.2 全局样式与 utils 的职责划分app.wxss 里定义的是公共类比如按钮的通用态、价格文字的统一样式、卡片间的间距。这里值得沿用一个经验小程序页面样式隔离是默认行为但 app.wxss 里的样式对所有页面生效所以命名规范非常重要不然改一个公共类所有页面一起变样。utils 目录在多数小程序工程里承担可复用逻辑这份源码的 utils 里主要封装了请求层和格式化函数。由于没有真实后端请求层直接用 Promise 包了一层 wx.request 的模拟返回同时把价格单位分转元、时间格式化这类纯函数单独抽了出来。这样做的好处是后期接入真实接口时只需要替换 request 函数体页面里的调用方式完全不用动。function formatPrice(cents) { return (cents / 100).toFixed(2); } function request(options) { return new Promise((resolve, reject) { // 这是模拟数据模式接入真实后端时替换为 wx.request setTimeout(() { resolve(options.mockData); }, 200); }); } module.exports { formatPrice, request };这段代码代表了一类非常实用的小程序分层思路页面只关心数据渲染不关心数据从哪来。formatPrice 接收的是分输出的是带两位小数的元字符串避免浮点误差。request 函数用 setTimeout 模拟网络延迟同时预留了换 wx.request 的位置属于典型的「可演进架构」。2.3 顶部导航栏高度适配与胶囊按钮位置微信小程序的顶部不是完全由开发者控制的右上角胶囊按钮是系统级的不同型号手机里它的位置和高度不同。商城小程序的 custom 导航栏场景里通常需要把标题、搜索框对齐到胶囊按钮的水平中线这时就要用 wx.getMenuButtonBoundingClientRect() 拿胶囊的坐标。const menuRect wx.getMenuButtonBoundingClientRect(); const systemInfo wx.getSystemInfoSync(); const navBarHeight (menuRect.top - systemInfo.statusBarHeight) * 2 menuRect.height; const statusBarHeight systemInfo.statusBarHeight;这段代码的逻辑是胶囊按钮顶部到状态栏底部的距离乘 2 再加上胶囊自身高度就得到自定义导航栏的整体高度。这是一个在真实项目中高频复用的适配方案拿这份源码做毕设时如果想把默认导航栏改成自定义样式这段代码可以直接搬过去。statusBarHeight 是状态栏高度不同机型的刘海屏数值不一样不能写死。3. 登录态与用户体系code 换 token、本地缓存与静默登录3.1 wx.login 拿到 code 之后到底做了什么很多初学者把 wx.login 和登录划等号实际上 wx.login 只负责拿到一个临时凭证 code这个 code 的有效期只有五分钟而且只能用一次。真正的登录链路是小程序把 code 发给后端后端拿 code appid secret 去微信接口服务换 openid 和 session_key然后后端自己签发一个 token 返回给小程序。这份源码里没有后端所以做了一个简化处理直接用 code 的哈希值模拟 token。App({ onLaunch() { wx.login({ success: (res) { // 真实项目中这里要把 res.code 发给后端换 token const mockToken mock_token_ res.code; wx.setStorageSync(token, mockToken); } }); } });注意代码里的注释res.code 不能直接当作登录凭据因为它谁都能拿微信官方文档明确要求 code 必须在服务端使用。毕业设计如果只有小程序端答辩老师很可能会问「你的 token 是怎么来的」答案应该是出于演示目的用本地模拟 token真实生产环境需要配套后端接口。3.2 token 的存储位置与过期策略小程序本地存储有 wx.setStorageSync 和 wx.setStorage 两组 API同步版和异步版的区别在于是否阻塞后续代码执行。源码选用的是 Sync 系列因为小程序启动阶段需要立即读取 token 判断登录态异步会让页面渲染和登录判断产生竞态。const token wx.getStorageSync(token); if (!token) { // 未登录轻量提示不强制跳转登录页 wx.showToast({ title: 请先登录, icon: none }); } else { // 已登录直接渲染用户信息 }这里的判断逻辑做了一个值得借鉴的取舍商城小程序的首页和商品浏览不需要强制登录只有加购物车、下单这类写操作才需要校验 token。如果把判断放在首页会白白增加用户流失率。真正的生产环境里 token 过期应该返回 401 状态码由 request 封装统一拦截并触发重新登录而不是在每个页面里都写一遍过期判断。存储方式生命周期适用场景wx.setStorageSync持久化除非用户清除缓存token、用户信息、购物车数据globalData内存中小程序退出即销毁临时状态、页面间共享的非持久数据页面 data页面生命周期内当前页渲染数据这张表建议直接放进论文的「技术选型」章节。很多毕业设计论文里对 storage 的描述只有一句话「使用本地存储」如果能写出三种存储方式的区别和选择理由答辩的技术深度会明显不同。3.3 静默登录与主动登录的触发时机静默登录是指用户无感知地完成登录wx.login 本身不会弹窗、不会有任何 UI所以适合放在 onLaunch 里执行。但 wx.login 换来的只是 session 层面的身份拿不到用户的手机号、头像、昵称这些必须通过用户主动点击授权按钮才能获取。这份源码的个人中心页面里头像是默认图昵称是「未登录」点击后才去调 wx.getUserProfile。wx.getUserProfile({ desc: 用于完善会员资料, success: (res) { const userInfo res.userInfo; this.setData({ userInfo }); wx.setStorageSync(userInfo, userInfo); } });注意 getUserProfile 在基础库 2.27.1 版本之后行为和之前有变化新版本里 getUserProfile 返回的是匿名化的信息不再返回真实头像昵称。做毕业设计时如果发现授权拿到了头像但昵称变成「微信用户」这不是代码 bug是微信平台策略调整。想要完整昵称需要用到头像昵称填写能力也就是 open-typechooseAvatar 配合 input typenickname这是现在的标准做法。4. 商城核心链路落地商品列表、详情跳转、购物车与模拟下单4.1 首页商品列表的数据绑定与加载状态首页的数据源来自一个本地 JS 文件数组里每一项包含商品 id、标题、图片路径、价格、库存这几个字段。这份源码把商品数据直接写在页面的 data 里对一个演示项目来说最简单直接但想做得更规范一点可以把数据抽到 utils/data.js 里统一导出页面通过 require 引入。Page({ data: { goodsList: [], loading: true }, onLoad() { this.loadGoods(); }, loadGoods() { // 模拟接口请求 setTimeout(() { this.setData({ goodsList: goodsData, loading: false }); }, 300); } });loading 状态是初学者最容易忽略的部分。真实项目里商品列表一定是从网络加载的网络有延迟如果页面渲染时数据还是空的用户看到的就是一片白屏。加上 loading 字段后用 wx.showLoading 或骨架屏过渡体验会好一个档次。这份源码里 wxml 用了 wx:if 判断 loadingloading 为 true 时显示加载中为 false 时渲染商品列表逻辑虽简单但完整覆盖了「加载前 - 加载中 - 加载后」三个状态。4.2 商品详情页的参数传递与 SKU 简介列表页跳详情页标准的传参姿势是用 URL 参数拼接商品 id 是最常用的参数。接受参数的页面在 onLoad(options) 里拿 id再根据 id 从数据源中过滤出对应商品。// 列表页 wx.navigateTo({ url: /pages/detail/detail?id goodsId }); // 详情页 onLoad(options) { const goodsId options.id; const detail goodsData.find(item item.id goodsId); this.setData({ detail }); }这里要注意一个常见问题URL 传参如果带中文或特殊字符会被编码成 %E4%B8%AD%E6%96%87 这种形式接收端需要 decodeURIComponent 解码。商品 id 通常是数字或短字符串不需要额外处理但如果传的是商品标题就必须编码。详情页的 SKU 选择器在精简版里做得比较克制只有规格文本的切换没有联动库存扣减这在毕设里够用但如果想加分可以在选择规格后加一个「规格是否匹配库存」的判断。4.3 购物车的本地存储设计与数量加减逻辑购物车数据放在 storage 里而不是 globalData原因很简单用户把小程序杀掉重进购物车数据不能丢。全局变量在内存里小程序退出即清空storage 是持久化的key 值固定为 cartListvalue 是商品数组。function addToCart(goods) { const cartList wx.getStorageSync(cartList) || []; const index cartList.findIndex(item item.id goods.id); if (index -1) { cartList[index].count 1; } else { cartList.push({ ...goods, count: 1 }); } wx.setStorageSync(cartList, cartList); }这段代码有两个值得讲的细节。第一findIndex 用来判断当前商品是否已在购物车如果已存在就只增加数量避免重复添加同一条记录第二展开运算符 { ...goods, count: 1 } 是浅拷贝不会修改原商品数据对象这是一个好的编程习惯。购物车页面上每次数量加减操作都要重新读一次 storage再写回一次 storage数据量不大性能损耗可以忽略。购物车页面还需要一个「全选」功能全选的本质是给购物车每个商品项维护一个 checked 字段全选按钮切换时把所有项的 checked 统一置为 true 或 false。结算按钮计算的是所有 checked 为 true 的商品总价这个计算用 reduce 一行就能完成。const totalPrice cartList .filter(item item.checked) .reduce((sum, item) sum item.price * item.count, 0);filter 先筛出选中项reduce 累加价格乘数量这就是商城购物车总价计算的标准写法。毕业设计答辩时这个公式基本必问。4.4 下单流程订单确认、地址选择与支付占位从购物车点到「去结算」会进入订单确认页这个页面展示三块内容收货地址、商品清单、金额明细。地址选择在小程序里有两种做法一种是 wx.chooseAddress 直接调起微信自带的收货地址选择器另一种是自己在页面里维护地址表单。这份源码用的是 wx.chooseAddress优点是省事且交互规范缺点是拿不到真实地址数据只能拿到模拟结构。wx.chooseAddress({ success: (res) { this.setData({ address: { name: res.userName, phone: res.telNumber, detail: ${res.provinceName}${res.cityName}${res.countyName}${res.detailInfo} } }); } });支付环节是毕业设计里隐藏的雷区。微信支付需要有商户号个人开发者无法直接申请所以源码里在订单确认页放了「模拟支付」按钮点击后弹窗提示「支付成功」然后清空购物车已选商品生成一条订单记录写入 storage。答辩时如果老师问起真实支付怎么做回答思路是调用 wx.requestPayment 发起支付后端接收微信支付回调后更新订单状态。// 模拟支付 mockPay() { wx.showLoading({ title: 正在支付... }); setTimeout(() { wx.hideLoading(); wx.showToast({ title: 支付成功, icon: success }); // 清空已结算的商品 const remainCart this.data.cartList.filter(item !item.checked); wx.setStorageSync(cartList, remainCart); }, 1000); }这段逻辑是典型的毕业设计演示实现loading 提示模拟支付处理中延时后提示成功然后更新本地购物车数据。真实项目里支付结果必须以后端回调为准绝不能在前端写死 success但作为期末大作业的演示这个简化是合理的答辩时能说清楚差别即可。4.5 支付方式的单选框与订单状态流转订单确认页里通常会有一个「支付方式」区块源码用 radio-group 实现微信支付和货到付款两种方式的切换。这个交互虽然简单但注意 radio 组件的 value 类型是字符串不是数字判断时用严格等于或者把值转成字符串再比较否则会得到「明明选中了却匹配不上」的结果。radio-group bindchangeonPayMethodChange radio valuewechat checked{{payMethod wechat}} / 微信支付 radio valuecod checked{{payMethod cod}} / 货到付款 /radio-group订单状态在小程序端没有任何后端支撑的情况下只能靠 storage 维护一个 orderList字段包括订单号、商品快照、总价、状态、创建时间。生成订单号可以用 Date.now() 拼接随机数保证同一毫秒内多次下单不会重复。订单状态一般只做三个值pending 待支付、paid 已支付、cancelled 已取消页面用 wx:if 分别渲染不同状态的操作按钮。5. 交付前必做的三件事数据源替换、自定义导航栏与答辩演示路径5.1 把本地数据替换成自己的商品信息拿到源码第一件事不是读代码而是建一个自己的商品数据文件。把 utils 里商品数组的每一项替换成你自己的商品注意图片路径不能用网络 URL 直接扔进去小程序对图片域名有白名单限制开发工具里可以勾选「不校验合法域名」但真机预览时必须使用已备案且在小程序后台配置过的 HTTPS 域名。本地图片是毕设最稳妥的方案图片放到 static/imgs 目录路径用相对路径引用。// 商品数据结构参考 { id: g001, title: 纯棉基础款T恤, images: [/static/imgs/goods/tshirt.jpg], price: 9900, // 单位分 stock: 120, sales: 35, category: 服饰 }价格字段用整数分而不是浮点数元这是电商系统的通用约定避免 0.1 0.2 ! 0.3 的浮点精度问题。展示的时候再用 formatPrice 转成字符串。5.2 自定义导航栏适配与 iPhone 安全区原版导航栏用的是微信默认样式也就是硬编码的 navigationBarTitleText。如果想在论文里多写一个技术亮点可以把首页和详情页改成自定义导航栏把 app.json 里对应页面的 navigationStyle 设为 custom然后在页面里手动放置标题和返回按钮。{ pages: [pages/index/index], window: { navigationStyle: custom } }自定义导航栏之后原来状态栏和胶囊占用的空间全部变成页面内容区底部 tabBar 的页面还好非 tabBar 页面底部还有 iPhone 的 Home Indicator 横条区域需要给页面最外层容器加 padding-bottom: env(safe-area-inset-bottom)否则底部按钮会被横条挡住。这是真机适配里最常被忽略的一环使用自定义导航栏时一定要验证 iPhone X 之后的机型。5.3 答辩演示的一条完整路径演示时不要从首页慢慢逛时间不够节奏应该是打开开发者工具展示工程目录结构30 秒——运行项目展示首页商品流20 秒——点击商品进详情15 秒——加购两件商品到购物车并修改数量20 秒——结算下单到模拟支付成功30 秒——打开个人中心展示登录态切换15 秒。这条链路覆盖了小程序商城的所有核心模块全程两分钟以内。演示前把开发者工具的「模拟操作」里的机型切到 iPhone 14 Pro 这类刘海屏设备顺手展示自定义导航栏的适配效果再切到 Android 机型对比一下这个小动作在答辩评委眼里是「考虑过真机适配」的直接证据。最后打开调试器 Network 面板搜索框输入 request故意断网然后刷新列表页展示前端对加载失败的兜底文案——「加载失败点击重试」这比任何口头描述都有说服力。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价