# hmyg **Repository Path**: lvdedian/hmyg ## Basic Information - **Project Name**: hmyg - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-08-26 - **Last Updated**: 2021-09-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 黑马优购项目 ## 1 搭建基本框架结构 #### 1.1 全局搭建脚手架 ``` npm install -g @vue/cli ``` ``` vue -V 查看vue版本 ``` #### 1.2 创建脚手架项目 ``` vue create -p dcloudio/uni-preset-vue my-project ``` #### 1.3 填写appid 在 src\manifest.json 内的第57行,填入 appid #### 1.4 在vscode对小程序代码进行编译。命令: ``` npm run dev:mp-weixin ``` #### 1.5 在微信开发者工具导入项目文件 文件路径:dist\dev\mp-weixin ![效果图](效果图.png) 出现这个效果即为脚手架项目创建成功 #### 1.6 配置**uview** ###### 1.6.1 下载依赖 ``` npm i uview-ui sass sass-loader@10 ``` ###### 1.6.2 在main.js入口中全局配置 ```js import uView from "uview-ui"; Vue.use(uView); ``` ###### 1.6.3 在uni.scss 中引入 uview的sass主题库 ```scss @import 'uview-ui/theme.scss'; ``` ###### 1.6.4 在 App.vue 全局引入uview的sass主题库 ```scss ``` ###### 1.6.5 在pages.json配置 easycom,方便组件的创建和使用 ```json "easycom": { "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue" }, ``` ## 2 项目开始,进行首页的设计 #### 2.1 tabbar导航栏功能的实现 pages.json 借助uview封装好的tabbar导航栏功能 ```json "tabBar": { //配置选中的颜色 "color": "#939494", "selectedColor": "#e03440", "borderStyle": "black", "backgroundColor": "#ffffff", "list": [ { "pagePath": "pages/index/index", "iconPath": "static/icons/home.png", "selectedIconPath": "static/icons/home-o.png", "text": "首页" }, { "pagePath": "pages/category/category", "iconPath": "static/icons/category.png", "selectedIconPath": "static/icons/category-o.png", "text": "分类" }, { "pagePath": "pages/cart/cart", "iconPath": "static/icons/cart.png", "selectedIconPath": "static/icons/cart-o.png", "text": "购物车" }, { "pagePath": "pages/my/my", "iconPath": "static/icons/my.png", "selectedIconPath": "static/icons/my-o.png", "text": "我的" } ] } ``` #### 2.2 搭建好tabbar导航栏,进行微信小程序的初始化样式设置 【因为微信小程序不支持 * 通配符,所以需要手动设置所有的标签进行初始化设置】 在 App.vue 入口进行全局初始化配置 ```css page, view, text, swiper, swiper-item, image, navigator { padding: 0; margin: 0; box-sizing: border-box; } ``` #### 2.3 设置版头的搜索框 **需求分析:** 1。这不是一个真正的搜索框,只是看起来像, 2 。点击会跳转到真正的搜索页面 3。这个搜索框在很多页面都需要用到,所以需要把它封装起来,以便复用 **代码实现 :** ###### 2.3.1 应用uview封装好的组件搜索框, ```vue show-action是关闭右侧的小组件 ``` ###### 2.3.2 修改主题颜色 ```scss ``` ###### 2.3.3 创建组件来对搜索框进行封装 因为已经配置了easycom,遵循这个规则,在components下文件夹名和要创建的组件名必须相同。、创建:components\search\search.vue 然后把代码放进组件中,在index.vue中使用 ```vue ........ ``` #### 2.4 轮播图的实现 需求分析: 1、在页面生命周期内进行数据的请求 2、先发请求把轮播图的数据请求回来,再进行数据渲染 3、 代码实现: ###### 2.4.1 发送请求获取数据 ```js async onLoad() { // 使用组件封装的请求方式 // const res= await this.$u.get('http://www.example.com', {}, { // token: 'xyz' // }).then(res => { // console.log(res); // }); // } const resSwiper = await this.$u.get("/home/swiperdata"); this.swiperList = resSwiper.message; //第二种方法 // this.swiperList = res.data.message.map((v) => ({ // ...v, // image: v.image_src, // })); ``` ###### 2.4.2 把请求回来的数据渲染到页面上 ```vue ``` #### 2.5 http请求的封装 实现目标:1、方便后面的维护修改 2、实现加载中效果 代码实现: ###### 2.5.1 、创建common文件夹和http.interceptor.js ###### 2.5.2 查找文档 ,使用文档的HTTP封装 ![Snipaste_2021-08-28_19-28-38](img/Snipaste_2021-08-28_19-28-38.png) ![Snipaste_2021-08-28_22-06-52](img/Snipaste_2021-08-28_22-06-52.png) 代码实现: ```js // common/http.interceptor.js // es5构造函数 es6 class原型 继承 // 这里的Vue为Vue对象(非创建出来的实例),vm为main.js中“Vue.use(httpInterceptor, app)”这一句的第二个参数, // 为一个Vue的实例,也即每个页面的"this" // 如果需要了解这个install方法是什么,请移步:https://uviewui.com/components/vueUse.html const install = (Vue, vm) => { // 此为自定义配置参数,具体参数见上方说明 Vue.prototype.$u.http.setConfig({ baseUrl: 'https://api-hmugo-web.itheima.net/api/public/v1', loadingText: '努力加载中~', loadingTime: 800, // 设置自定义头部content-type // header: { // 'content-type': 'xxx' // } // ...... }); } export default { install } /** * const Vue1 = new Vue() * Vue。prototype.show = function(){} * Vue1.show() * Vue 支持写插件 拓展 * Vue.use(obj) 本质 use 方法内部会调用obj.install * 如果想要编写一个vue的插件js ,就必须提供install方法 * ,main.js 中才可以 Vue。use(l来使用你的插件) * */ ``` #### 2.6 面包屑导航栏的实现 需求分析: 1、也是拿到请求接口,进行发送请求 2、把请求到的数据放到定义的变量里存着 3、进行数组遍历,把获取到的数据渲染到对应的页面结构标签上 4、样式微调 代码实现: ```js // 进行导航的请求 并把请求的的数据放到定义的变量里 const resNav = await this.$u.get("/home/catitems"); this.navList = resNav.message; console.log(resNav); ``` 进行数据渲染 ```vue ``` ![image-20210904154505980](img/image-20210904154505980.png) #### 2.7 楼层的开始 需求分析:1、布局分析:因为楼层的结构比较复杂,所以选择用浮动来进行布局处理而不用flex布局,flex布局适合处理那些比较小的,比较工整的页面布局,而在这里的布局用浮动更为适合。。因为设计稿的大小是750px,和小程序的规则吻合,比例为 1:1 2、布局分析完,接下来发送请求,把数据获取回来, 3、把请求回来的数据渲染到页面上 4、完成楼层的布局以及样式优化 ![Snipaste_2021-08-29_08-07-59](img/Snipaste_2021-08-29_08-07-59.png) 代码实现: 1、进行布局和使用的技术分析 ****2 设计思路** **1 第一张定宽度 33% 高自适应** **2 剩下4张图片 高度等于第一张的高度的一半** **3 css选择器** **前几个** **:nth-child(-n+几)** **后几个** **:nth-last-child(-n+几)**** 1 前端电商项目商品分类页面布局 ```html Document
1
2
3
4
5
``` 2、分析完布局开始写接口请求数据 ###### 2.1.1 定义一个变量把请求到的数据保存起来 ```js // 楼层数组 floorsList: [], ``` ###### 2.1.2 真实发送请求 ```js // 楼层设置 const resFloors = await this.$u.get("home/floordata"); this.floorsList = resFloors.message; console.log(resFloors); ``` ###### 2.1.3 把获取到的数据渲染到页面上 ```vue ``` ###### 2.1.4 渲染数据后,进行一些样式的细节处理 ```scss ``` ## 3 分类页面的实现 需求分析: 1、需要实现左边内容超出,拉动可以滚动,右边的商品内容也可以滚动 2、呈现多个层次结构的商品版块内容 3、点击左边的滚动标题,需要呈现对应左边标题的商品内容 4、点击右边的商品缩略图能跳转到对应的那一块商品详情列表 5、 实现步骤: ###### 3.1、创建相应页面src\goods_list\goods_list.vue ```json { "path": "pages/goods_list/goods_list", "style": { "navigationBarTitleText": "商品列表", "enablePullDownRefresh": true, "navigationBarBackgroundColor": "#f8cecc" } }, ``` 3.2、对页面布局进行分析, ###### 3.2.1、先解决页面的两侧分别滚动问题 ```css * { margin: 0; padding: 0; box-sizing: border-box; ; } .main { display: flex; height: 100vh; } .main>div { flex: 1; } .left { background-color: brown; overflow: auto; } .right { background-color: aqua; overflow: auto; } ``` ###### 3.2.2 解决左侧宽度不变,右边变化的问题 ```css ``` ```html
1
。。。。
``` ###### 3.2。3、解决中间高度变化,上下不变 ```css ``` ```html
``` ###### 3.2.4 、经过多个过程,把功能整合起来,使用到分类页面中 ```css ``` ```html
1
。。。。。
``` ###### 3.3 经过页面布局原理分析,开始布局分类页面 ###### 3.3.1 、调用封装好的版头搜索框 ```vue ``` ###### 3.3.2、先测试请求接口 ```js async onLoad() { const res = await this.$u.get("/categories"); console.log(res) } ``` **因为数据过多而且繁杂,为了方便对数据类型的层级进行区分和判断,所以把数据请求回来后,运用小技巧对请求回来的数据进行拆分。** ```json { "message": [{ "cat_id": 1, "cat_name": "大家电", "cat_pid": 0, "cat_level": 0, "cat_deleted": false, "cat_icon": "/full/none.jpg", "children": [ { "cat_id": 3, "cat_name": "电视", "cat_pid": 1, "cat_level": 1, "cat_deleted": false, "cat_icon": "/full/none.jpg", "children": [ { "cat_id": 5, "cat_name": "曲面电视", "cat_pid": 3, "cat_level": 2, "cat_deleted": false, "cat_icon": "https://api-hmugo-web.itheima.net/full/2fb113b32f7a2b161f5ee4096c319afedc3fd5a1.jpg" } ] }] }], "meta": { "msg": "获取成功", "status": 200 } } ``` **经过拆分数据结构清晰很多了,方便下面的进一步操作的进行** ###### 3.3.3 、数据请求回来后,把它定义到一个全局的数组里 *// 定义一个全局数组,不像在data定义的那样,在data里定义了就一定在视图中使用,* */*** ** 把数据放到data中,两个目的才这么做,* ** 1 这个变量肯定也是全局变量* ** 2 如果我们要把这个数据放到data中,要保证这个数据一定是在视图中使用* ** 3 小程序中 建议 data中的数据 越少越好, 越少性能越强* ** \*/* ```js let categories = []; export default { data() { return { // 左侧的标题数组 titles: [], // 右侧的商品数组 goods: [], // 选中的左边标题 contentIndex: 0, // 右边商品的滚动高度 scrollTop: 0, }; }, ``` ```js categories = res.message; // console.log(17, categories); // console.log(17, categories); // 把titles数组进行解析,并且把v.cat_name返回到数组里,上面就可以直接遍历 this.titles = categories.map((v) => v.cat_name); // 这个就是直接解析数组里的第一个元素的子元素数组, this.goods = categories[0].children; ``` **对保存了获取到的数据的全局数组进行拆分** **把全局数组里的数据拆分为三部分:左边标题,右侧大导航栏标题,大导航栏标题下的商品详情** ###### 3.3.4、把左边标题渲染出来 ```vue {{ item }} ``` **进行一些样式的处理:** ```scss .content { // 正常溢出就开启滚动条,分配flex布局 flex: 1; overflow: hidden; display: flex; .left { padding-top: 30rpx; margin-left: 20rpx; width: 180rpx; overflow: auto; .title-item { height: 80rpx; font-size: 28rpx; flex-direction: column; justify-content: center; // 单行子盒子在侧轴上的对齐方式 前面 u-flex 就是已经封装好这个属性还有主轴方向,还有子盒子主轴方向上的对齐方式 align-items: center; } } ``` ###### 3.3.5、把右边大导航的标题渲染出来 ```vue / {{ item1.cat_name }} / ``` ![Snipaste_2021-08-31_20-41-26](img/Snipaste_2021-08-31_20-41-26.png) ###### 3.3.6 把大导航标题渲染出来后,再把已经拆分好的请求回来的数据进行循环,把每一个对应的标题下的商品内容渲染出来 这里要注意分清楚每一个对应的数据:前面已经定义了不同的变量来保存着不同的数据,所以这里直接拿数据来进行渲染 ```vue > {{ item2.cat_name }} ``` ###### 3.3.7 数据渲染出来后,进行样式处理 ```scss .right { flex: 1; overflow: auto; .goods-group { .goods-title { height: 80rpx; display: flex; justify-content: center; align-items: center; font-size: 28rpx; } .goods-list { display: flex; flex-wrap: wrap; // navigator是超链接便签,,,不用点。。。。。 navigator { width: 33.33%; text-align: center; image { width: 60%; } .goods-subtitle { font-size: 26rpx; } } } } } ``` ![image-20210904160837957](img/image-20210904160837957.png) **结构呈这样分布** ###### 3.3.8 当所有的布局完成后,进行小优化,给左侧的标题添加一个激活选中样式,用三元表达式配合伪元素实现 ```vue ``` css样式: ```scss .active { color: #ea4350; position: relative; &::before { // 添加的是最左边的那条竖杆的样式 content: ""; width: 5rpx; height: 80%; background-color: #ea4350; left: -3rpx; top: 20%; // Y轴上的旋转 transform: translateY(-50%); position: absolute; } } ``` ###### 3.3.9 当选中左边标题,右侧的商品内容也应跟随着动态变化并且呈现对应标题的详细商品内容 【1】给左边标题结构绑定点击事件 ```vue {{ item }} ``` 【2】通过绑定的点击事件进行处理 ```js handlerMenuSelect(index) { // 绑定选中的标题的下标 this.contentIndex = index; // 解析原数组的子元素数组的下标 this.goods = categories[index].children; // 重新定义滚动条的高度,但是这里要注意,当重复设置某些属性为相同的值时,不会同步到view层。例如:每次将scroll-view组件的scroll-top属性值设置为0,只有第一次能顺利返回顶部。 这和props的单向数据流特性有关,组件内部scroll-top的实际值改动后,其绑定的属性并不会一同变化。 this.scrollTop = Math.random() * Math.random() * Math.random() * Math.random(); }, ``` 【3】这里会出现一个bug,就是右边的商品内容如果拉到尽头,再切换左边的标题,商品内容会停留在最后一个内容,不会回到最开始的数据,不合理,所以需优化。解决这个bug用scroll-view也可以解决 ```js this.scrollTop = Math.random() * Math.random() * Math.random() * Math.random(); ``` ###### 3.4.0 实现右侧商品动态切换后,添加数据缓存功能 **简单的回顾 h5 的数据缓存 存储:localstorage.setItem()** **获取:localStorage.getItem()** **只能存储字符串,如果传进去复杂数据类型,容易造成数据丢失,所以存储之前要先转化成JSON字符串,而微信小程序的本地存储微信小程序已经封装好,所以不用转化。直接 uni.setStorageSync 即可,不用在转化为JSON格式** **业务分析:【1】发送请求前先判断本地存储中有没有商品分类数据 (1 如果没有数据)——就发送请求获取数据 页面渲染 ——再把数据存储到缓存中 (2 有数据)——判断数据有没有过期--如果过期了就再发送请求获取数据 --把数据存储到缓存中 ——没有过期 ,直接获取数据进行页面渲染操作** 【1】把**发送请求的功能进行封装 ,然后进行数据的本地存储和给这些数据添加一个时间戳为后面的判断数据过期铺垫:** ```js async getStorageCategory() { // 正常发送请求方式 const res = await this.$u.get("/categories"); categories = res.message; // console.log(17, categories); // 把titles数组进行解析,并且把v.cat_name返回到数组里,上面就可以直接遍历 this.titles = categories.map((v) => v.cat_name); // 这个就是直接解析数组里的第一个元素的子元素数组, this.goods = categories[0].children; // 把数据存到本地缓存中 uni.setStorageSync("category", { time: Date.now(), data: res.message }); }, ``` **【2 】存好数据后,把前面的业务分析用代码具体实现,先对数据进行判断,如果存在,但是过期了那么重新发送请求获取数据,如果没过期,就继续使用。如果数据不存在那么就重新发送请求获取数据进行渲染操作** ```js async onLoad() { // 存储数据到本地存储中有两种情况:【1】 没有数据,发请求获取数据渲染数据,再存到本地存储中 【2】有数据,判断数据有没有过期,如果没有过期,则发请求获取数据,存到本地存储中,如果过期了,重新发请求获取数据,存到本地存储中 // 微信小程序本地存储方式:: // wx.setStorageSync("category", { msg: "大胆,曹贼" }); let storageCategory = uni.getStorageSync("category"); if (storageCategory) { console.log("存在"); // 如果过期了,那么就是重新发请求获取数据 if (Date.now() - storageCategory.time > 1000 * 30) { console.log("过期了"); this.getStorageCategory(); } else { // 这个就是数据直接从本地存储中直接获取,而不用再发一次请求 console.log("没过期,可以直接使用"); categories = storageCategory.data; // console.log(17, categories); // 把titles数组进行解析,并且把v.cat_name返回到数组里,上面就可以直接遍历 this.titles = categories.map((v) => v.cat_name); // 这个就是直接解析数组里的第一个元素的子元素数组, this.goods = categories[0].children; } } else { console.log("不存在"); this.getStorageCategory(); } // // 正常发送请求方式 // const res = await this.$u.get("/categories"); // categories = res.message; // // console.log(17, categories); // // 把titles数组进行解析,并且把v.cat_name返回到数组里,上面就可以直接遍历 // this.titles = categories.map((v) => v.cat_name); // // 这个就是直接解析数组里的第一个元素的子元素数组, // this.goods = categories[0].children; // console.log(38, this.goods); }, ``` 效果如下: ![image-20210904182658313](img/image-20210904182658313.png) ###### 3.4.1 点击携带参数进行跳转 **把当前的详细商品数据携带过去到商品详情页面** 把当前选中的商品 id传过去并拼接起来,这里跳转路径需要根路径 / 。可以在onLoad里打印onLoad的参数option看到传过去的id参数 ```vue :url="'/pages/goods_list/goods_list?id=' + item2.cat_id" ``` ## 4 商品列表页面 业务分析:【1】上拉加载页面 【1.1】先找到分页事件,【1.2】PC端一般是横向排列的按钮,【1.3】移动端一般是页面滑动,【1.4】滚动条触底事件(onReachBottom)【1.5】 搞清楚分页逻辑【1.5.1】触发分页事件后,判断还有没有下一页数据【1.5.2】有下一页数据,页码递增,pageNum++。【1.5.3】重新发送请求要用到修改pageNum的参数。【1.6】数据回来了【1.6.1】新的数据和旧的数据叠加 【2】下拉刷新页面 【2.1】【2.2】【3】分页加载页面 ![image-20210904201819107](img/image-20210904201819107.png) #### 4.1 设置搜索栏,直接把封装好的组件放进去 ![image-20210904192306290](img/image-20210904192306290.png) ```vue ``` #### 4.2 测试接口 ​ **根据商品分类页面传过来的商品分类id,进行请求测试** ```js async onLoad({ id }) { // const { id } = option; // console.log(option); const res = await this.$u.get("/goods/search", {cid:id}); }, ``` ![image-20210904193123907](img/image-20210904193123907.png) #### 4.3 进行静态结构的铺设 ###### 4.3.1 搜索框下的tab栏,在uview中找相似功能的组件 ![image-20210904193642206](img/image-20210904193642206.png) ```vue ``` ![image-20210904193848703](img/image-20210904193848703.png) 点击不同的标签显示不同的页面内容 ![image-20210904194134884](img/image-20210904194134884.png) ![image-20210904194215463](img/image-20210904194215463.png) #### 4.4 列表的动态渲染 ```js this.goodsList = res.message.goods; ``` ```vue 文字省略 u-line {{ item.goods_name }} ¥{{ item.goods_price }} ``` u-flex语法,可以节减代码 ![image-20210904194724150](img/image-20210904194724150.png) **由于右边的子项受父项设置了居中垂直,所以要额外给子项重新设置高度** ```scss .goods-item { // 设置子项拉伸到父项的高度 align-items: stretch; } ``` 继续优化样式: ```scss .goods_info { display: flex; flex-direction: column; justify-content: space-around; .goods_price { color: #eb4450; } } ``` 效果: ![image-20210904194910103](img/image-20210904194910103.png) #### 4.5 封装发送请求的代码 ```js // 数组需要传的参数 定义全局变量 const params = { query: "", // 关键字 cid: "", // 分类id pagenum: 1, // 页码 pagesize: 10, // 页容量 }; // 定义数据总条数||页码数 let totalPage = 1; ``` 自定义分类id为cid ```js async onLoad({ id }) { // const { id } = option; // console.log(option); // let id = 6; params.cid = id; this.getGoods(); }, ``` 封装发送请求的代码 ```js methods:{ async getGoods(callback) { const res = await this.$u.get("/goods/search", params); console.log(86,res); // 前面的下拉页面加载事件,重新发起请求获取到的数据是最新的,前面旧的数据没有再显示了,不符合逻辑。所以要把数组列表里面的新旧数据进行合并再显示 this.goodsList = res.message.goods; //message.goods是后台返回来的 }, } ``` #### 4.6 判断有没有下一页数据 如何判断:: 【1】数据的总条数【2】页容量 【3】当前的页码 【】 ![image-20210904204410101](img/image-20210904204410101.png) ###### 4.6.1 假设数据总条数为一 ```js // 定义数据总条数||页码数,用全局方便后面维护,而且因为是跨函数使用这个变量 let totalPage = 1; ``` ###### 4.6.2 给总条数赋值并计算结果 ```js // 给数据总数重新赋值,获取总条数,计算总页数 totalPage = Math.ceil(res.message.total / params.pagesize); console.log(totalPage); ``` ###### 4.6.3 判断还有没有下一页,没有就提示,有就重新加载下一页数据 ```js onReachBottom() { // console.log("onReachBottom"); if (params.pagenum >= totalPage) { // console.log("没有啦"); uni.showToast({ title: "没有更多数据啦", icon: "none", //不写none 显示的是打钩的样式 }); } else { //页码递增 params.pagenum++; // console.log("下面更多精彩"); //重新发送请求,调用上面封装额度函数 this.getGoods(); } }, ``` ###### 4.6.4 新旧代码叠加: ```js // this.goodsList = res.message.goods; message.goods是后台返回来的 // 新旧代码叠加 把旧的数组解析再把新的数组放进去 this.goodsList = [...this.goodsList, ...res.message.goods]; ``` #### 4.7 下拉刷新事件 。代码功能的实现(onPullDownRefresh) ###### 4.7.1 需要在页面配置一个属性 ![image-20210904211121312](img/image-20210904211121312.png) ```js data里声明// 商品列表数组 goodsList: [], methods:{ onPullDownRefresh() { console.log("onPullDownRefresh"); // 清空数组 this.goodsList = []; // 重置请求列表 重置页码 params.pagenum = 1; // 发送请求 this.getGoods(function () { //等数据回来 才关闭下拉窗口 console.log("关闭窗口"); uni.stopPullDownRefresh(); }); }, } ``` ###### 4.7.2 解决异步请求下拉窗口先关闭而数据还没加载的问题 ```js // 因为异步请求关闭窗口快于发送请求,但是发送请求不等于请求回来了,所以要放在发送请求里,请求完数据后才关闭下拉窗口.但是新bug来了,在首次加载中也进行了关闭窗口,正常情况下不需要执行这个关闭窗口程序 // 所以这里使用函数+回调函数+短路运算进行处理 // uni.stopPullDownRefresh(callback); console.log("发送请求"); // console.log("关闭下拉窗口"); callback && callback(); ``` ![image-20210904212631485](img/image-20210904212631485.png) ![image-20210904212814619](img/image-20210904212814619.png) ## 5.0 商品详情页面 #### 5.1 进行跳转页面进行传参,在goods_list进行跳转 ```vue ``` ###### 在详情页面进行参数接收 ```js async onLoad({ id }) { // const { id } = option; // const id = 47869; // console.log(option); ``` #### 5.2 轮播图的动态渲染 发送请求 ```js data声明 goods:{} // 发送请求 id是必传属性 // const res = await this.$u.get("/goods/detail", { goods_id: id }) this.goods = res.message; ``` 动态渲染 ```vue ``` #### 5.3轮播图放大预览功能 ![image-20210904215056964](img/image-20210904215056964.png) ![image-20210904215108962](img/image-20210904215108962.png) ```vue ``` 给轮播图绑定一个点击事件,然后进行处理 ```js // uview提供了 click 点击轮播图时触发 index:点击了第几张图片,从0开始 // current string urls 的第一张 当前显示图片的链接 handlerPreviewimage(index) { const urls = this.goods.pics.map((v) => v.pics_big); const current = urls[index]; uni.previewImage({ urls, current }); }, ``` #### 5.4 渲染名称和价格 ```vue ¥{{ goods.goods_price }} {{ goods.goods_name }} ``` 样式优化: ```scss .goods_detail { padding-bottom: 102rpx; } .goods-price { font-size: 36rpx; color: #eb4450; margin: 10rpx 0 10rpx 30rpx; } .goods-title { font-size: 30rpx; margin-left: 30rpx; } ``` #### 5.5 解决当网络慢的时候数据请求回来的时候会一闪而过undefind的问题 **goods: null,** **** **** ** #### 5.6 用富文本渲染图文详情 **有两种:一种是微信小程序提供的,一种是uview提供的** **小程序提供的rich-text适合一些轻量级的的图文详情页面渲染,而u-parse比较适合一些大型的,比较复杂的图文详情渲染** ```vue 图文详情 ```