小程序项目案例1

发布时间:2026/5/21 14:57:52

小程序项目案例1 项目概述慕尚花坊是一款 同城鲜花订购 的小程序专业提供各地鲜花速递、鲜花预定、网上订花、包月鲜花等服务涵盖电商项目常见功能模块包含项目首页商品分类商品列表商品详情用户管理收货地址购物车结算支付订单管理等……项目技术栈小程序内置组件采用小程序内置组件 结合Vant组件库实现页面结构的搭建项目中使用了 css 拓展语言 Scss 绘制页面的结构小程序内置API交互、支付、文件上传、地图定位、网络请求、预览图片、本地存储等小程序分包加载降低小程序的启动时间、包的体积提升用户体验度小程序组件开发将页面内的功能模块抽象成自定义组件实现代码的复用网络请求封装request 方法封装、快捷方式封装、响应拦截器、请求拦截器骨架屏组件利用开发者工具提供了自动生成骨架屏代码的能力提高了整体使用体验和用户满意度。UI组件库使用Vant组件库实现小程序 结构的绘制LBS使用腾讯地图服务进行LBS逆地址解析实现选择收货地址功能miniprogram-licia使用 licia 进行函数的防抖节流async-validator使用 async-validator 实现表单验证miniprogram-computed: 使用 miniprogram-computed 进行计算属性功能mobx-miniprogram使用mobx-miniprogram进行项目状态的管理​项目初始化创建项目与项目初始化重置app.js中的代码删除app.json中pages下的pages/logs/logs路径同时删除pages/logs文件夹删除app.json中pages下的rendererOptions以及componentFramework字段重置app.wxss中的代码删除components中的自定义组件重置pages/index文件夹下的index.js、index.wxss、index.html以及index.json文件更新utils下util.js的文件名为formatTime.js自定义构建 npm 集成Sass在慕尚花坊项目中我们就需要将小程序源码放到 miniprogram 目录下首先在project.config.json配置miniprogramRoot选项指定小程序源码的目录然后配置project.config.json的setting.packNpmManually为true开启自定义 node_modules 和 miniprogram_npm 位置的构建 npm 方式最后配置 project.config.json 的setting.packNpmRelationList项指定packageJsonPath和miniprogramNpmDistDir的位置packageJsonPath 表示 node_modules 源对应的 package.jsonminiprogramNpmDistDir 表示 node_modules 的构建结果目标位置安装vant然后进行npm 构建测试是否能够正常vant构建成功npm i vant/weapp集成 Sass在project.config.json文件中修改setting下的useCompilerPlugins字段为[sass]即可开启工具内置的 sass 编译插件。VsCode 开发小程序项目WXML - Language Serviceprettier微信小程序开发工具微信小程序助手-Y小程序开发助手(可选)配置详细插件在【项目的根目录】下创建.vscode文件夹注意文件夹名字前面带.点❗在.vscode文件夹下创建settings.json用来对安装的插件属性进行设置具体属性设置从下面复制即可注意.vscode文件夹下的settings.json文件只对当前一个项目生效在【项目的根目录】下创建.prettierrc文件进行Prettier代码规则的配置规则从下面复制即可为了让Prettier配置项在微信开发者工具生效需要在微信开发者工具中也安装Prettier扩展插件。➡️ .vscode/settings.json{ // 保存文件时是否自动格式化 editor.formatOnSave: true, // ---------------- 以下是 [ prettier ] 插件配置 ---------------- // 指定 javascript、wxss、scss、less、json、jsonc 等类型文件使用 prettier 进行格式化 [javascript]: { editor.defaultFormatter: esbenp.prettier-vscode }, [wxss]: { editor.defaultFormatter: esbenp.prettier-vscode }, [scss]: { editor.defaultFormatter: esbenp.prettier-vscode }, [less]: { editor.defaultFormatter: esbenp.prettier-vscode }, [json]: { editor.defaultFormatter: esbenp.prettier-vscode }, [jsonc]: { editor.defaultFormatter: esbenp.prettier-vscode }, // Prettier 的一个配置项用于指定哪些文件类型需要使用 Prettier 进行格式化 prettier.documentSelectors: [**/*.wxml, **/*.wxss, **/*.wxs], // ---------------- 以下是 [ WXML - Language Service ] 插件配置 ---------------- // wxml 文件使用 prettier 进行格式化 [wxml]: { // qiu8310.minapp-vscode 是 WXML - Language Service 插件提供的配置项 // 此插件主要是针对小程序的 wxml 模板语言可以自动补全所有的组件、组件属性、组件属性值等等 // 如果是 VsCode 需要开启这个配置 editor.defaultFormatter: qiu8310.minapp-vscode // 如果是微信小程序需要开启这个配置通过 esbenp.prettier-vscode 对代码进行格式化 // editor.defaultFormatter: esbenp.prettier-vscode }, // 创建组件时使用的 css 后缀 minapp-vscode.cssExtname: scss, // 默认 wxss支持 styl sass scss less css // 指定 WXML 格式化工具 minapp-vscode.wxmlFormatter: prettier, // 配置 prettier 代码规范 minapp-vscode.prettier: { useTabs: false, tabWidth: 2, printWidth: 80 }, // ---------------- 以下是 [ 微信小程序助手-Y ] 插件配置 ---------------- // 新增、删除小程序页面时是否自动同步 app.json pages 路径配置默认为 false wechat-miniapp.sync.delete: true, // 设置小程序页面 wxss 样式文件的扩展名 wechat-miniapp.ext.style: scss, // ---------------- 其他配置项 ---------------- // 配置语言的文件关联运行 .json 文件时写注释 // 但在 app.json 和 page.json 中无法使用 files.associations: { *.json: jsonc } }➡️ .prettierrc{ semi: false, singleQuote: true, useTabs: false, tabWidth: 2, printWidth: 180, trailingComma: none, overrides: [ { files: *.wxml, options: { parser: html } }, { files: *.wxss, options: { parser: css } }, { files: *.wxs, options: { parser: babel } } ] }配置项配置项含义semi: false不要有分号singleQuote: true使用单引号useTabs: false缩进不使用 tab,使用空格tabWidth: 2tab缩进为4个空格字符printWidth: 80一行的字符数如果超过会进行换行默认为80trailingComma: none尾随逗号问题设置为none 不显示 逗号overrides: []overrides 解析器默认情况下Prettier 会根据文件文件拓展名推断要使用的解析器项目根目录.vscode文件夹中settings.json文件只对当前项目生效❗如果想配置项生效还需要注意在 VsCode 中只能打开当前一个小程序项目不能同时打开多个小程序项目❗ 且项目目录请勿嵌套打开 ❗在进行项目开发的时候我们经常的会频繁的使用到一些 API例如wx.showToast()、wx.showModal()等消息提示API这些 API 的使用方法如下:wx.showToast({ title: 消息提示框, // 提示的内容 icon: success, // 提示图标 duration: 2000, // 提示的延迟时间 mask: true // 是否显示透明蒙层防止触摸穿透 }) wx.showModal({ title: 提示, // 提示的标题 content: 您确定执行该操作吗, // 提示的内容 confirmColor: #f3514f, // 确定按钮的样式 // 接口调用结束的回调函数调用成功、失败都会执行 complete({ confirm, cancel }) { if (confirm) { console.log(用户点击了确定) return } if (cancel) { console.log(用户点击了取消) } } })消息提示模块封装wx.showToast()消息提示框是在项目中频繁使用的一个小程序API常用来给用户进行消息提示反馈。使用方式如下wx.showToast({ title: 消息提示框, // 提示的内容 icon: success, // 提示的图标success(成功)、error(失败)、loading(加载)、none(不显示图标) duration: 2000, // 提示的延迟时间 mask: true // 是否显示透明蒙层防止触摸穿透 })封装思路创建一个toast方法对wx.showToast()方法进行封装调用该方法时传递对象作为参数如果没有传递任何参数设置一个空对象{}作为默认参数从对象中包含title、icon、duration、mask参数并给参数设置默认值在需要显示弹出框的时候调用toast方法并传入相关的参数有两种参数方式不传递参数使用默认参值传入部分参数覆盖默认的参数调用方式新封装的模块我们希望有两种调用的方式模块化的方式导入使用import { toast } from ./extendApi toast() toast({ title: 数据加载失败...., mask: true })将封装的模块挂载到wx全局对象身上wx.toast() wx.toast({ title: 数据加载失败...., mask: true })实现步骤在utils目录下新建extendApi.js文件对wx.showToast()方法进行封装➡️ utils/extendApi.js/** * description 封装消息提示组件 * param {*} title 提示的内容 * param {*} icon 图标 * param {*} duration 提示的延迟时间 * param {*} mask 是否显示透明蒙层防止触摸穿透 */ const toast ({ title 数据加载中, icon none, mask true, duration 3000 } {}) { wx.showToast({ title, icon, mask, duration }) } // 在 wx 全局对象上封装 toast 方法 // 调用 API 方式 // 1. 在入口文件 app.js 导入封装的模块 import ./utils/extendApi // 2. 调用封装的方法wx.toast() wx.toast toast // 模块化的方式使用 // 调用 API 方式 // 1. 导入该文件import { toast } from ../utils/extendApi // 2. 调用封装的方法toast() export { toast }➡️ app.jsimport { toast } from ./utils/extendApi App({ onLaunch() { // 第一种调用方式不传入任何参数 toast() // 第二种调用方式传入部分参数 toast({ title: 数据加载失败...., mask: true }) // 第三种调用方式传入全部的参数 toast({ title: 数据加载失败...., mask: true }) } })模态对话框封装wx.showModal()模态对话框也是在项目中频繁使用的一个小程序API通常用于向用户询问是否执行一些操作例如询问用户是否真的需要退出、是否确认删除等等wx.showModal({ title: 提示, // 提示的标题 content: 您确定执行该操作吗, // 提示的内容 confirmColor: #f3514f, // 接口调用结束的回调函数调用成功、失败都会执行 complete({ confirm, cancel }) { confirm console.log(点击了确定) cancel console.log(点击了取消) } })封装思路对wx.showModal()方法进行封装 封装后的新方法叫modal调用该方法时传递对象作为参数对象的参数同wx.showModal()参数一致封装的modal方法的内部通过Promise返回用户执行的操作(确定和取消都通过 resolve 返回)在需要显示模态对话框的时候调用modal方法并传入相关的参数有三种调用方式不传递参数使用默认参数传递参数覆盖默认的参数调用方式新封装的本地存储模块我们依然希望有两种调用的方式模块化的方式导入使用将封装的模块挂载到wx全局对象身上实现步骤在extendApi.js文件中新建modal方法方法内部modal方法方法内部用来处理封装的逻辑➡️ utils/extendApi.js// coding... /** * description 封装 wx.showModal 方法 * param {*} options 同 wx.showModal 配置项 */ export const modal (options {}) { // 使用 Promise 处理 wx.showModal 的返回结果 return new Promise((resolve) { // 默认的参数 const defaultOpt { title: 提示, content: 您确定执行该操作吗?, confirmColor: #f3514f, } // 将传入的参数和默认的参数进行合并 const opts Object.assign({}, defaultOpt, options) wx.showModal({ // 将合并的参数赋值传递给 showModal 方法 ...opts, complete({ confirm, cancel }) { // 如果用户点击了确定通过 resolve 抛出 true // 如果用户点击了取消通过 resolve 抛出 false confirm resolve(true) cancel resolve(false) } }) }) } // 在 wx 全局对象上封装 myToast 方法 // 调用 API 方式 // 1. 在入口文件 app.js 导入封装的模块 import ./utils/extendApi // 2. 调用封装的方法wx.toast() wx.toast toast wx.modal modal // 模块化的方式使用 // 调用 API 方式 // 1. 导入该文件import { toast } from ../utils/extendApi // 2. 调用封装的方法toast() export { toast, modal }➡️ app.jsimport { modal } from ./utils/extendApi App({ async onLaunch() { // 第一种调用方式不传入任何参数 // 不使用任何参数使用默认值 const res await modal() console.log(res) // 第二种调用方式更改默认配置 const res await modal({ content: 鉴权失败请重新登录, showCancel: false }) console.log(res) } })封装本地存储 API在小程序中经常需要将一些数据存储到本地方便多个页面的读取使用例如将用户的登录状态、用户的个人信息存储到本地。小程序提供了同步、异步两类 API 来实现本地存储操作。例如:wx.setStorageSync、wx.setStorage等方法try { wx.setStorageSync(key, value) } catch (err) { console.error(存储指定 ${key} 数据发生错误:, err) } wx.setStorage({ key: key, data: data, success (res) {}, fail (err) {} })如果直接使用这些 API会比较麻烦通常情况下我们需要对本地存储的方法进行封装。实现步骤在utils目录下新建storage.js文件在该文件中封装对本地数据进行 存储、获取、删除、清除的方法落地代码➡️ utils/storage.js/** * description 存储数据 * param {*} key 本地缓存中指定的 key * param {*} value 需要缓存的数据 */ export const setStorage (key, value) { try { wx.setStorageSync(key, value) } catch (e) { console.error(存储指定 ${key} 数据发生错误:, e) } } /** * description 从本地读取对应 key 的数据 * param {*} key */ export const getStorage (key) { try { const value wx.getStorageSync(key) if (value) { return value } } catch (e) { console.error(获取指定 ${key} 数据发生错误:, e) } } /** * description 从本地移除指定 key 数据 * param {*} key */ export const removeStorage (key) { try { wx.removeStorageSync(key) } catch (err) { console.error(移除指定 ${key} 数据发生错误:, e) } } /** * description 从本地清空全部的数据 */ export const clearStorage () { try { wx.clearStorageSync() } catch (e) { console.error(清空本地存储时发生错误:, e); } }missing请求封装-使用 npm 包发送请求封装的网络请求模块发布到了npm如果你在学习网络请求模块封装时感觉比较吃力可以先使用 npm 包实现功能。npm install mina-request 构建 npm​ 安装包后需要在微信开发者工具中进行 npm 构建点击工具➡️构建 npmimport WxRequest from ./request import { getStorage, clearStorage } from ./storage import { modal, toast } from ./extendApi import { env } from ./env // 对 WxRequest 进行实例化 const instance new WxRequest({ // baseURL: https://gmall-prod.atguigu.cn/mall-api, baseURL: env.baseURL, timeout: 15000, isLoading: false }) // 配置请求拦截器 instance.interceptors.request (config) { // 在请求发送之前做点什么…… // 在发送请求之前需要先判断本地是否存在访问令牌 token const token getStorage(token) // 如果存在 token就需要在请求头中添加 token 字段 if (token) { config.header[token] token } return config } // 配置响应拦截器 instance.interceptors.response async (response) { // 从 response 中解构 isSuccess const { isSuccess, data } response // 如果 isSuccess 为 false说明执行了 fail 回调函数 // 这时候就说明网络异常需要给用户提示网络异常 if (!isSuccess) { wx.showToast({ title: 网络异常请重试, icon: error }) return response } // 判断服务器响应的业务状态码 switch (data.code) { // 如果后端返回的业务状态码等于 200说请求成功服务器成功响应了数据 case 200: // 对服务器响应数据做点什么…… return data // 如果返回的业务状态码等于 208说明 没有 token或者 token 失效 // 就需要让用户登录或者重新登录 case 208: const res await modal({ content: 鉴权失败请重新登录, showCancel: false // 不显示取消按钮 }) if (res) { // 清除之前失效的 token 同时要清除本地存储的全部信息 clearStorage() wx.navigateTo({ url: /pages/login/login }) } return Promise.reject(response) default: toast({ title: 程序出现异常请联系客服或稍后重试 }) return Promise.reject(response) } } // 将 WxRequest 实例进行暴露出去方便在其他文件中进行使用 export default instance小程序设置环境变量在实际开发中不同的开发环境调用的接口地址是不一样的。例如开发环境需要调用开发版的接口地址生产环境需要调用正式版的接口地址这时候我们就可以使用小程序提供了wx.getAccountInfoSync()接口用来获取当前账号信息在账号信息中包含着 小程序 当前环境版本环境版本合法值开发版develop体验版trial正式版release// 获取当前帐号信息 const accountInfo wx.getAccountInfoSync() // 获取小程序项目的 appId console.log(accountInfo.miniProgram.appId) // 获取小程序 当前环境版本 console.log(accountInfo.miniProgram.envVersion)根据环境的不同我们给 env 变量设置不同的请求基准路径baseURL然后将env环境变量导出// 获取 小程序帐号信息 const { miniProgram } wx.getAccountInfoSync() // 获取小程序当前开发环境 // develop 开发版, trial 体验版, release 正式版 const { envVersion } miniProgram let env { baseURL: https://gmall-prod.atguigu.cn } switch (envVersion) { case develop: env.baseURL https://gmall-prod.atguigu.cn break case trial: env.baseURL https://gmall-prod.atguigu.cn break case release: env.baseURL https://gmall-prod.atguigu.cn break defaulr } export { env }接口调用方式在开发中我们会将所有的网络请求方法放置在 api 目录下统一管理然后按照模块功能来划分成对应的文件在文件中将接口封装成一个个方法单独导出例如// 导入封装的网络请求工具 http.js import http from ../utils/http /** * description 获取轮播图数据 * returns Promise */ export const reqBannerData () http.get(/index/findBanner)这样做的有以下几点好处易于维护一个文件就是一个模块一个方法就是一个功能清晰明了查找方便便于复用哪里使用哪里导入可以在任何一个业务组件中导入需要的方法团队合作分工合作// 导入封装的网络请求工具 http.js import http from ../utils/http /** * description 获取轮播图数据 * returns Promise */ export const reqSwiperData () http.get(/mall-api/index/findBanner)// 导入接口 API import { reqSwiperData } from ../../api/index Page({ // 页面数据 data: { swiperList: [] }, // 小程序页面加载时执行 onLoad () { // 调用获取首页数据的方法 getHomeList() } // 获取首页数据 async getHomeList() { // 获取轮播图数据 const res await reqSwiperData() console.log(res) } })获取首页数据封装接口请求函数可以一个个封装也可以直接使用all方法进行封装在页面 .js 文件中导入封装的接口API函数在onLoad钩子函数中调用方法获取首页轮播图数据➡️ api/index.js: 准备接口 APIimport http from ../utils/http /** * 通过并发请求获取首页的数据 */ export const reqIndexData () { return instance.all( http.get(/index/findBanner), http.get(/index/findCategory1), http.get(/index/advertisement), http.get(/index/findListGoods), http.get(/index/findRecommendGoods) ) }➡️ page/index/index.js// 导入接口 API import { reqIndexData } from ../../api/index Page({ // 初始化数据 data: { bannerList: [], // 轮播图数据 categoryList: [], // 分类数据 activeList: [], // 活动广告 hotList: [], // 人气推荐 guessList: [] // 猜你喜欢 }, // 获取首页数据 async getIndexData() { // 调用接口获取首页数据 // 数组每一项是 Promise 产生的结果并且是按照顺序返回。 const res await reqIndexData() // 在获取数据以后对数据进行赋值 this.setData({ bannerList: res.data[0], categoryList: res.data[1], activeList: res.data[2], hotList: res.data[3], guessList: res.data[4] }) }, // 监听页面加载 onLoad() { // 调用获取首页数据的回调 this.getIndexData() } })分析轮播图区域并渲染轮播图区域采用组件化方式开发我们在index目录下新建banner文件夹里面存放轮播图组件。在index/index.json文件中导入组件然后将组件当成标签进行使用{ usingComponents: { banner: ./banner/banner } }!-- 轮播图区域 -- banner /swiper、swiper-item、navigator、image组件实现页面结构的搭建block渲染数组实现列表渲染使用flex布局实现了页面样式的绘制另外需要注意的是轮播图面板指示点不支持自定义所以只能页面结构的方式实现轮播图的面板指示点功能!--pages/index/banner/banner.wxml-- !-- 轮播图 -- view classswiper-box !-- swiper 滑块视图容器 -- swiper autoplay classswiper indicator-active-color#FF734C interval2000 duration1000 indicator-colorrgba(0, 0, 0, .3) !-- 使用 block 标签实现通过数组进行列表渲染 -- block wx:for{{ bannerList }} wx:keyindex !-- swiper-item 单个滑块视图容器 -- swiper-item classswiper-item !-- 通过 navigator 组件跳转的链接 -- navigator classnavigator url/pages/goods/detail/detail?goodsIdid image classimg src{{ item }}/image /navigator /swiper-item /block /swiper !-- 轮播图的面板指示点因为面板指示点不支持所以我们只能通过自定义结构的方式 -- view classindicator !-- active 类名当前被激活的面板指示点颜色 -- !-- circle 类名默认的面板指示点颜色 -- text wx:for{{bannerList.length}} wx:keyid class{{ active rectangle }} /text /view /view渲染页面结构➡️ page/index/index.js!-- 轮播图区域 -- banner bannerList{{ bannerList }} /➡️ page/index/banner/banner.wxml!-- 使用 block 标签实现通过数组进行列表渲染 -- block wx:for{{ bannerList }} wx:keyindex !-- swiper-item 单个滑块视图容器 -- swiper-item classswiper-item !-- 通过 navigator 组件跳转的链接 -- navigator classnavigator url/pages/goods/detail/detail?goodsId{{item.id}} image classimg src{{ item.imageUrl }}/image /navigator /swiper-item /block实现轮播图和指示点的联动轮播图和指示点进行联动当切换到第二张轮播图时第二个面板指示点高亮如果想实现这种一一对应的关系需要借助索引首先在data中初始化状态activeIndex默认为 0代表第一个高亮也是用来接收切换后的轮播图索引然后使用swiper组件的change事件监听轮播图是否发生改变如果改变则获取到轮播图的索引赋值到data中通过activeIndex对小圆点进行动态的渲染在data中初始化状态activeIndex默认为 0给swiper绑定bindchange事件监听轮播图是否切换将切换后轮播图的索引赋值给activeIndex利用activeIndex对小圆点进行动态的渲染➡️ pages/index/banner/banner.js// pages/index/banner/banner.js Component({ /** * 组件的属性列表 */ properties: { // 轮播图数据 bannerList: { type: Array, value: [] } }, /** * 组件的初始数据 */ data: { activeIndex: 0 // 被激活的轮播图索引默认是 0 }, /** * 组件的方法列表 */ methods: { // 获取被激活的轮播图索引 getSwiperIndex(event) { // console.log(event) const { current } event.detail this.setData({ activeIndex: current }) } } })➡️ pages/index/banner/banner.wxml!-- 轮播图 -- view classswiper-box !-- swiper 滑块视图容器用来绘制轮播图 -- swiper autoplay classswiper indicator-active-color#FF734C interval2000 duration1000 indicator-colorrgba(0, 0, 0, .3) bindchangegetSwiperIndex !-- 通过 block 标签对 轮播图数据 进行渲染 -- block wx:for{{ bannerList }} wx:keyindex !-- coding... -- /block /swiper !-- 轮播图的面板指示点因为面板指示点不支持自定义所以我们只能通过自定义结构的方式 -- view classindicator !-- active 类名当前被激活的面板指示点颜色 -- !-- rectangle 类名默认的面板指示点颜色 -- text wx:for{{bannerList.length}} wx:keyid class{{ index activeIndex ? active rectangle : rectangle }} /text /view /view分析商品导航区域并渲染商品导航区域采用组件化方式开发我们在index目录下新建entrance文件夹里面存放导航分类组件。采用view、navigator、image、text组件实现了进行页面结构的搭建使用flex布局实现了页面样式的绘制!-- 导航分类 -- view classnav-list !-- 一级分类导航容器 -- view wx:for{{ cateList }} wx:keyindex classnav-item {{ index 5 ? small : }} !-- 导航链接 -- navigator classnavigator-nav url/pages/goods/list/list?category1Id1 image classnav-img src导航路径 / text classnav-text导航名字/text /navigator /view /view在index/index.json文件中导入组件然后将组件当成标签进行使用{ usingComponents: { // ... entrance: ./entrance/entrance }, navigationBarTitleText: 慕尚花坊 }!-- 导航分类 -- entrance /渲染导航分类结构➡️ page/index/index.wxml!-- 导航分类 -- entrance cateList{{ categoryList }} /➡️ pages/index/entrance/entrance.jsComponent({ // 组件的属性列表 properties: { cateList: { type: Array, value: [] } }, // coding... }➡️ pages/index/entrance/entrance.htmlview classnav-list !-- 一级分类导航容器 -- view wx:for{{ cateList }} wx:keyindex classnav-item {{ index 5 ? small : }} !-- 导航链接 -- navigator classnavigator-nav url/pages/goods/list/list?category1Id{{item.id}} image classnav-img src{{ item.imageUrl }} / text classnav-text{{ item.name }}/text /navigator /view /view➡️ pages/index/index.html!-- 广告区域 -- view classadver view classadver-left navigator url/pages/goods/list/list?category2Id{{ activeList[0].category2Id }} image src{{ activeList[0].imageUrl }} modewidthFix / /navigator /view view classadver-right view navigator url/pages/goods/list/list?category2Id{{ activeList[1].category2Id }} image src{{ activeList[1].imageUrl }} modewidthFix / /navigator /view view navigator url/pages/goods/list/list?category2Id{{ activeList[2].category2Id }} image src{{ activeList[2].imageUrl }} modewidthFix / /navigator /view /view /view

相关新闻