
简介这套微信小程序生鲜商城项目附带完整截图与可运行源码适合小程序入门开发者、前端学习者及电商项目实训人员解决从页面设计到功能逻辑实现缺少完整参考的问题。资源压缩包共39个文件大小仅576KB其中15张png截图用于展示界面效果7个js文件负责交互逻辑7个wxss文件控制视觉样式6个wxml文件搭建页面结构2个json文件完成页面与组件配置另有md说明文档和dwg设计参考文件各文件类型分工明确便于按需查看。项目内部包含pages、component、template等目录并配有app.js、app.wxss、app.json等全局配置文件以及README.md能较完整地呈现生鲜商城类小程序的项目组织方式。读者可以对照截图与代码快速理解商品展示、分类导航、购物车等典型模块的写法也可以基于现有结构进行二次开发搭建自定义的生鲜商城小程序。目前已有1860人学习下载适合用来积累小程序项目实战经验。1. 一套带截图的生鲜商城源码包先别急着跑当你把《微信小程序 生鲜商城截图源码》解压后会看到一堆 png 图标、一份 README、几个页面文件夹甚至还有一个.dwg文件。别被这些文件吓到这其实是一条完整的微信小程序学习链路app.json负责入口注册app.wxss和「生鲜商城 style layout.wxss」负责全局视觉pages下的 index、list、logs、component 四个页面演示了商城最常见的列表、日志和组件交互template/item-template.wxml把商品卡片抽成了可以复用的模板。这份资源最大的价值不是拿来直接上线而是让你对照截图一步步看懂一个真实小程序工程是怎么组织样式、数据和模板的。下面我按从全局到局部的顺序拆中间会给出可以直接抄的代码和参数说明。2. 从 app.json 到 style layout.wxss先看清全局骨架2.1 pages 是入口清单第一个页面就是首页微信小程序运行时第一件事就是读根目录下的app.json。这个文件里最重要的是pages数组它声明了工程里所有页面路径而数组的第一项会作为编译后默认打开的页面。以这份资源为例pages目录下能看到 index、list、logs、component 几个页面文件夹所以app.json里的pages至少应该包含下面这些路径。{ pages: [ pages/index/index, pages/list/list, pages/logs/logs, pages/component/component ], window: { navigationBarBackgroundColor: #3CB371, navigationBarTitleText: 生鲜商城, navigationBarTextStyle: white, backgroundColor: #f6f6f6 }, sitemapLocation: sitemap.json }pages数组的作用是告诉开发者工具哪些.js、.wxml、.wxss、.json文件属于一个页面。如果你想把商品列表页作为首页把pages/list/list移到数组第一位就可以。window里的navigationBarBackgroundColor控制顶部导航栏颜色生鲜类目通常选绿色系因为绿色能直接传递新鲜、天然的感觉navigationBarTextStyle只支持white或black配深绿背景时选白色。提示如果你改动app.json后模拟器没有刷新点击开发者工具顶部的「编译」按钮即可不需要重启工具。2.2 全局样式拆成 style 与 layout 两层这份压缩包里有一个名字很长的样式文件叫「生鲜商城 style layout.wxss」。从命名看它没有把全部样式塞进单一文件而是单独拆出了 style 与 layout 两层。这种拆分方式在小程序工程里很实用style 层负责颜色、字体、圆角这类主题变量layout 层负责 flex 布局、间距、栅格这类结构样式。如果你以后维护一个超过 5 个页面的商城强烈建议也这样拆。app.wxss中放全局变量和公共类例如品牌色和主要按钮page { --primary-color: #3CB371; --price-color: #ff3b30; --bg-color: #f5f5f5; font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, sans-serif; } .btn-primary { background-color: var(--primary-color); color: #ffffff; border-radius: 8rpx; }CSS 变量在微信小程序基础库 2.6.0 之后才支持。如果你在开发者工具里看到变量没有生效把var(--primary-color)直接替换成#3CB371即可。layout 层可以抽象更通用的布局类配合图片资源里的箭头图标使用.flex-row { display: flex; flex-direction: row; align-items: center; } .flex-between { display: flex; flex-direction: row; align-items: center; justify-content: space-between; } .goods-card { display: flex; padding: 24rpx; margin: 20rpx; background: #ffffff; border-radius: 12rpx; }.flex-row解决 80% 的水平排列需求.flex-between负责左右分布.goods-card是商品卡片的通用容器。看到image目录下的arrowright.png和green_tri.png时通常就是把小图标放进 flex 容器中做箭头、角标、价格装饰。这种原子化类的缺点是在 wxml 里类名会变长但好处是样式复用率高页面之间视觉一致。2.3 从 image 目录的图标名还原界面功能看一个源码包有没有货最快的方法是把image目录里的文件名列出来逐个推断界面长什么样。下面是我对这份资源的初步判断。文件名推测用途对应界面位置icon64_appwx_logo.png小程序默认图标项目配置、分享卡片wechatHL.png微信标识授权登录页green_tri.png三角装饰或价格角标商品卡片plus.png加购按钮商品行右侧trash.png删除按钮购物车列表play.png / pause.png / stop.png音频播放/暂停/停止商品详情或公告区域arrowright.png右侧箭头设置项、列表行icon_API.png / icon_component.png组件/API 演示入口logs 或 component 页这个表能帮你快速定位功能plus和trash说明购物车有加购和删除play/pause/stop三个成组的图标说明工程里还做了音频播放。小程序里每个图片都会占用包体积如果你只想保留纯商城购物流程可以把这三个音频图标连同对应代码一起移除移除时注意搜索play、pause、stop在.js和.wxml中的所有引用避免留下无效逻辑。3. 模板与工具item-template.wxml 和 util.js 的复用姿势3.1 把商品卡片抽成 template避免三处重复代码如果首页、列表页、搜索结果页都要显示商品卡片最蠢的做法是每页复制一份 wxml。微信小程序提供了template模板机制把卡片结构写在template/item-template.wxml里页面通过import引入后就能反复使用。template nameitem-template view classgoods-item image classgoods-thumb src{{goods.thumb}} modeaspectFill/image view classgoods-info text classgoods-name{{goods.name}}/text view classprice-row text classprice-symbol¥/text text classprice-int{{goods.priceInt}}/text text classprice-dec.{{goods.priceDec}}/text view classcart-btn bindtaponAddCart>import src../../template/item-template.wxml / view classgoods-list block wx:for{{goodsList}} wx:keyid template isitem-template data{{goods: item}} / /block /viewimport的src是相对路径页面在pages/index/下想要回到根目录再进template/就要写../../template/item-template.wxml这里少一个../就会编译报错。data{{goods: item}}表示把循环变量item重命名为模板内部的goods对象模板里所有字段都从goods下读取。需要注意import有作用域限制模板里的onAddCart事件绑定的其实是页面 js 中同名方法所以页面必须定义onAddCart否则点击加购按钮会直接报not a function。3.3 util.js价格拆分和时间格式化提前做好数据处理模板取值越简单越好所以复杂的数据加工应该在utils/util.js里完成。价格格式化是最常见的需求后台返回的价格可能是12.9也可能直接是整数12统一转成两位小数后拆开前端就只需要关心展示。function formatPrice(price) { const val Number(price); if (isNaN(val)) { return { int: 0, dec: 00 }; } const fixed val.toFixed(2); const parts fixed.split(.); return { int: parts[0], dec: parts[1] }; } function formatTime(date) { const d date instanceof Date ? date : new Date(date); const y d.getFullYear(); const m String(d.getMonth() 1).padStart(2, 0); const day String(d.getDate()).padStart(2, 0); return ${y}-${m}-${day}; } module.exports { formatPrice, formatTime };formatPrice先做数字校验再用toFixed(2)保底确保不会出现12这种没法拆小数的情况。formatTime负责把时间戳或 Date 对象转成2025-06-01这种标准格式logs页面记录操作日志时正好用得上。在页面里使用时先const util require(../../utils/util.js)在onLoad中遍历商品列表把每个 item 转成包含priceInt和priceDec的结构再setData交给模板渲染。不要在 wxml 里直接调util函数因为import只能引模板不能引 JS 函数。4. 页面交互index、list 与 component 页面串联逻辑4.1 首页分类切换dataset 下标与高亮状态首页index在商城里的角色是流量入口常见结构是顶部轮播图、分类 tab、商品列表。分类 tab 的交互核心是「点击分类后高亮当前项并过滤商品列表」。实现时可以先把全量商品存在allGoods里用一个activeIndex记录当前分类下标。Page({ data: { categories: [全部, 蔬菜, 水果, 肉禽蛋], activeIndex: 0, goodsList: [] }, onLoad() { const all [ { id: 1, category: 蔬菜, name: 上海青, price: 3.5 }, { id: 2, category: 水果, name: 烟台苹果, price: 6.8 } ]; this.setData({ allGoods: all, goodsList: all }); }, onSwitchCategory(e) { const index Number(e.currentTarget.dataset.index); this.setData({ activeIndex: index }); const all this.data.allGoods || []; const filtered index 0 ? all : all.filter(item item.category this.data.categories[index]); this.setData({ goodsList: filtered }); } });wxml 里给每个 tab 项绑定>addToCart(e) { const id e.currentTarget.dataset.id; const goods this.data.goodsList.find(item item.id Number(id)); if (!goods) return; const cart this.data.cart || {}; if (cart[id]) { cart[id].count 1; } else { cart[id] { ...goods, count: 1 }; } this.setData({ cart }); wx.setStorageSync(cart, cart); }, removeFromCart(e) { const id e.currentTarget.dataset.id; const cart { ...this.data.cart }; delete cart[id]; this.setData({ cart }); wx.setStorageSync(cart, cart); }用对象cart存储购物车cart[id]直接访问删除时delete即可。wx.setStorageSync做本地持久化下次进入小程序还能读到购物车数据。注意wx.setStorageSync是同步接口数据量大时会有轻微的阻塞商城购物车数据量通常只有几十条用同步写法没有性能问题。removeFromCart里的...this.data.cart是浅拷贝避免直接修改 data 中的对象引用这是 setData 常见的踩坑点。4.3 play/pause/stop 成组出现多半是音频播放image 目录里的play.png、pause.png、stop.png是一个信号原工程还实现了音频播放能力。微信小程序早期支持audio组件但组件在基础库 1.6.0 后已经不再维护新项目推荐用wx.createInnerAudioContext()这个 API。const audioCtx wx.createInnerAudioContext(); function initAudio(src) { audioCtx.src src; audioCtx.onPlay(() { // 播放开始wxml 中把 play 图标切换成 pause }); audioCtx.onEnded(() { audioCtx.stop(); }); } function toggleAudio() { if (audioCtx.paused) { audioCtx.play(); } else { audioCtx.pause(); } }生鲜商品详情页可能加一段语音介绍讲产地、甜度、配送方式。用wx.createInnerAudioContext()时要注意离开页面要在onUnload里调用audioCtx.destroy()否则音频会继续播放并造成内存泄漏。图标切换的常见做法是在 data 里加一个isPlaying标记wxml 中根据标记用wx:if判断显示 play 还是 pause如果同时引入 stop 图标可以在stop时调用audioCtx.stop()并把isPlaying重置为false。5. 截图反推布局WXSS 里值得直接抄的几段5.1 商品卡片左图右文的最稳写法如果你手里只有截图没有设计稿我一般先从商品卡片开始拆。把卡片结构定位成「左侧固定宽高的图片 右侧自适应文字区域」再叠加价格和按钮这样一个布局就能覆盖首页、搜索页、收藏页。.goods-item { position: relative; display: flex; padding: 20rpx; background: #ffffff; border-radius: 16rpx; margin-bottom: 20rpx; } .goods-thumb { width: 180rpx; height: 180rpx; border-radius: 12rpx; background: #fafafa; flex-shrink: 0; } .goods-info { flex: 1; margin-left: 20rpx; display: flex; flex-direction: column; justify-content: space-between; }.goods-item用display: flex建立水平排列.goods-thumb设置flex-shrink: 0防止图片被右侧文字挤压。.goods-info作为右侧容器flex: 1撑满剩余空间justify-content: space-between让名称在顶部、价格在底部。生鲜商品名经常有「新鲜采摘 有机蔬菜 顺丰直达」这类长文案所以.goods-name建议加两行省略.goods-name { display: -webkit-box; overflow: hidden; text-overflow: ellipsis; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }两行省略在 iOS 和 Android 的 WebView 里都兼容前提是容器必须是 block 或 flex item并且固定宽度。把-webkit-line-clamp改成 1 就是单行省略适合商品分类名称。5.2 价格、角标与绿色三角的三种组合生鲜商城的价格要突出「便宜」两个字所以整数部分通常放大加粗小数部分缩小并靠底部对齐。green_tri.png 在源码里的出现大概率是放在价格旁边的小三角标识或者热门角标的底图。我如果不用图片会直接用 CSS 画一个右上角角标。.price-row { display: flex; align-items: baseline; } .price-int { font-size: 44rpx; font-weight: 600; color: #ff3b30; } .price-dec { font-size: 24rpx; color: #ff3b30; } .price-tag { position: absolute; top: 0; right: 0; padding: 4rpx 12rpx; background: linear-gradient(135deg, #ff9a56, #ff6b1a); color: #ffffff; font-size: 20rpx; border-radius: 0 16rpx 0 12rpx; }align-items: baseline让两个不同字体大小的 text 成为基线对齐视觉上小数会自然下沉。角标使用绝对定位所以.goods-item必须加position: relative。如果你想把图片资源里的green_tri.png用起来可以把绝色角标替换成image src/image/green_tri.png classtag-tri /两种方式效果差不多但图片更省 CSS 代码CSS 画三角则适合做动态颜色。5.3 rpx 适配与底部安全区参数rpx 是微信小程序的核心适配单位它的设计原则是屏幕宽度固定为 750rpx因此不同机型的 rpx 与 px 换算比例不同。机型屏幕宽度1rpx 约等于iPhone SE320px0.4267pxiPhone 6/7/8375px0.5pxiPhone Pro Max414px0.552px主流 Android360px~412px0.48px~0.55px只要设计稿按 750px 输出你直接把设计稿里的 px 数字写进 rpx 就可以不用手动换算。但要注意字体大小如果也用 rpx在超大屏上会被放大得很夸张所以正文可以混用 px 或者用28rpx这种折中值。底部购物车结算栏容易挡住 iPhone 的 Home 指示条必须加安全区适配.footer-bar { padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); background-color: #ffffff; box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.05); }constant()是老版本 iOS 的写法env()是较新的标准两条都要写顺序不能反。开发者工具模拟器里可以在设备列表选择 iPhone X 以上机型验证底部是否出现黑色横条遮挡如果遮挡说明env()没有生效。6. 导入开发者工具从源码到上线的最小验收流程6.1 导入工程的三步操作解压后不要直接双击某个文件要用微信开发者工具导入整个工程目录。首先打开开发者工具进入「项目」页面选择「导入」然后把目录定位到你解压出的包含app.json的那个文件夹如果工具提示没有找到 app.json说明目录层级不对应该往上一层。接着 AppID 可以选「测试号」这个选项不需要注册小程序账号适合本地学习如果你有自己的小程序账号也可以选自己的 AppID这样能真机预览和上传体验版。最后点「确定」后等待编译完成模拟器里应该能看到首页。6.2 三个最常见的编译报错报错信息原因处理方式app.json未找到导入时选中了内层子目录改选包含 app.json 的根目录Cannot find module ../../utils/util.jsrequire 路径层级不对检查从当前 js 文件到 utils 的相对路径template is not foundimport 路径写错或模板名拼错对照template nameitem-template确认 is 属性报错信息里最容易被忽略的是路径。微信小程序里的相对路径都是基于当前文件所在目录计算的和浏览器里的 base 路径逻辑一样。你只要记住页面在pages/index/下访问根目录的template/要写../../同样访问utils/也是../../utils/util.js。6.3 改造顺序与运行后的验证技巧把这份源码改造成能上线的生鲜商城不建议一上来就重写页面而是按三条线推进。第一条是视觉线把app.json和「生鲜商城 style layout.wxss」里的主色、按钮圆角、价格颜色替换成自己的品牌规范。第二条是数据线把onLoad里写死的allGoods数组换成wx.request或云开发数据库接口返回结构和本地数组保持相同的字段名这样template/item-template.wxml完全不用改。第三条是存储线购物车从wx.setStorageSync升级为云函数或服务端接口提交订单时把购物车数据 POST 到后端。联调阶段在开发者工具详情面板勾选「不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书」只用于本地代理上线前必须在后台配置真实的 request 合法域名。运行起来后打开调试器切到 WXML 面板检查商品卡片有没有出现多余的undefined节点再对着截图逐项核对价格对齐、图片裁剪和底部安全区这几项确认无误后这套源码才算真正吃透了。本文还有配套的精品资源点击获取