zoukankan      html  css  js  c++  java
  • 微信小程序开发06-一个业务页面的完成

    前言

    接上文:微信小程序开发05-日历组件的实现

    github地址:https://github.com/yexiaochai/wxdemo

    这里来说一说我们的理念,我们也学习小程序开发有一周多了,从近期的使用上来说,小程序可以作为底层,但是缺少一个框架层,这个框架层需要提供:

    ① 组件库

    ② 更好的代码组织方式,也就是让我们可以做到轻松的组件化开发

    我们从最开始到现在,都在沿着这个方向去分解小程序学习,其实小程序本身的东西差不多了,但是我们代码过程中有时候却越高越复杂,多了很多封装,其实这所有的复杂都是为了设置一个基本的架构,一个标准的开发模式,让后面写业务代码的同学能更高效的写代码,经过一年多的发展,事实上这种较为成熟的框架已经有了,比如我们正在使用的:

    https://tencent.github.io/wepy/

    但是,可以看到小程序基本还是原生JS,这其实是个非常好的学习整理机会,所以我这边一步步和大家对小程序进行了拆分,期望能形成一套还能用的雏形,帮助大家理解,所以我们继续今天的学习吧,为了降低单页面难度,我们将首页进行下改造。

    首页

    首页做了一点改造,变成了这个样式了:

    这里需要三个点击时间点,因为日历组件,我们昨天就做好了,而他这个出发日期事实上就是我们日历组件的selecedDate,处理这块逻辑:

     1 <template name="searchbox">
     2   <view class="c-row search-line" data-flag="start">
     3     <view class="c-span3">
     4       出发</view>
     5     <view class="c-span9 js-start search-line-txt">
     6       请选择出发地</view>
     7   </view>
     8   <view class="c-row search-line" data-flag="arrive">
     9     <view class="c-span3">
    10       到达</view>
    11     <view class="c-span9 js-arrive search-line-txt">
    12       请选择到达地</view>
    13   </view>
    14   <view class="c-row search-line" data-flag="arrive">
    15     <view class="c-span3">
    16       出发日期</view>
    17     <view class="c-span9 js-arrive search-line-txt">
    18       {{calendarSelectedDate || '请选择出发日期'}} </view>
    19   </view>
    20   <view class="c-row " data-flag="arrive">
    21     <span class="btn-primary full-width js_search_list">查询</span>
    22   </view>
    23 </template>
    View Code
    1 <view class="c-row search-line" data-flag="arrive">
    2   <view class="c-span3">
    3     出发日期</view>
    4   <view class="c-span9 js-arrive search-line-txt">
    5     {{calendarSelectedDate || '请选择出发日期'}} </view>
    6 </view>

    点击时候我们弹出我们的日历,这个时候我们日历模块释放一个事件显示日历:

    PS:template不与页面级别WXML共享一个作用域,所以我暂时都采用的include引入

     1 <view class="c-row search-line" data-flag="start">
     2   <view class="c-span3">
     3     出发</view>
     4   <view class="c-span9 js-start search-line-txt">
     5     请选择出发地</view>
     6 </view>
     7 <view class="c-row search-line" data-flag="arrive">
     8   <view class="c-span3">
     9     到达</view>
    10   <view class="c-span9 js-arrive search-line-txt">
    11     请选择到达地</view>
    12 </view>
    13 <view class="c-row search-line" data-flag="arrive" ontap="showCalendar">
    14   <view class="c-span3">
    15     出发日期</view>
    16   <view class="c-span9 js-arrive search-line-txt">
    17     {{calendarSelectedDateStr}}</view>
    18 </view>
    19 <view class="c-row " data-flag="arrive">
    20   <span class="btn-primary full-width js_search_list">查询</span>
    21 </view>
    22 <include src="./mod/calendar.wxml" />
    23 <include src="../../utils/abstract-page.wxml" />
    View Code
    1 <view class="c-row search-line" data-flag="arrive" ontap="showCalendar">
    2   <view class="c-span3">
    3     出发日期</view>
    4   <view class="c-span9 js-arrive search-line-txt">
    5     {{calendarSelectedDateStr}}</view>
    6 </view>
     1 /*
     2 事实上一个mod就只是一个对象,只不过为了方便拆分,将对象分拆成一个个的mod
     3 一个mod对应一个wxml,但是共享外部的css,暂时如此设计
     4 所有日历模块的需求全部再此实现
     5 */
     6 const util = require('../../../utils/util.js')
     7 
     8 let selectedDate = new Date();
     9 
    10 module.exports = {
    11   showCalendar: function () {
    12     this.setData({
    13       isCalendarShow: ''
    14     });
    15   },
    16   onCalendarDayTap: function (e) {
    17     let data = e.detail;
    18     var date = new Date(data.year, data.month, data.day);
    19     console.log(date)
    20     this.setData({
    21       calendarSelectedDate: date,
    22       calendarSelectedDateStr: util.dateUtil.format(date, 'Y年M月D日')
    23     });
    24   },
    25   data: {
    26     isCalendarShow: 'none',
    27     calendarDisplayMonthNum: 2,
    28     calendarDisplayTime: new Date(),
    29     calendarSelectedDate: selectedDate,
    30     calendarSelectedDateStr: util.dateUtil.format(selectedDate, 'Y年M月D日')
    31   }
    32 }

    显然,这里的日历这样摆设有点丑,我们这里将其封装成一个弹出层,所以我们这里再做一个容器类组件,专门用于装载页面样式用:

    1 <view class="cm-modal " style="z-index: {{uiIndex}}; position: fixed; display: {{isShow}}; ">
    2   <slot ></slot>
    3 </view>
    4 <view class="cm-overlay" bindtap="onMaskEvent" style="z-index: {{maskzIndex}}; display: {{isShow}}" >
    5 </view>
    1 <ui-container bindonContainerHide="onContainerHide" is-show="{{isCalendarShow}}" >
    2     <view class="calendar-wrapper-box">
    3       <view class="box-hd">
    4         <text class="fl icon-back js_back "></text>
    5         <text class="fr icon-next js_next"></text>
    6       </view>
    7       <ui-calendar bindonDayTap="onCalendarDayTap" displayTime="{{calendarDisplayTime}}" 
    selectedDate
    ="{{calendarSelectedDate}}" displayMonthNum="{{calendarDisplayMonthNum}}"
    is-show
    ="{{isCalendarShow}}"></ui-calendar> 8 </view> 9 </ui-container>

    但是这里也引起了其他问题,因为引入了shadow-dom概念,我的样式不能重用,组件内部样式与外部是不能通信的,但是这里是页面级别容器,内容的样式肯定是来源页面的,这里没什么问题,所以我们这里显示的是正确的,但是我这里想做一个出格一点的操作,我想用样式将这里日历月标题换个位置:

    而日历组件和外部是不能通信的,我们这里该如何处理呢,我这里想了两个方案:

    ① 设置一个全局使用的组件库样式,让所有组件继承,但是不知道这里对性能是否有影响,因为这样的话体积不会太小

    ② 小程序设计了可以传入组件的方法,比如我们这里的日历组件我们可以这样改变其样式

    1 .calendar-cm-month {
    2     position: absolute;
    3     top: 0;
    4     height: 90rpx;
    5     line-height: 90rpx;
    6     width: 100%;
    7     color: #00b358;
    8     text-align: center;
    9 }
     1 Component({
     2   externalClasses: ['ex-class'],
     3   behaviors: [
     4     View
     5   ],
     6   properties: {
     7     displayMonthNum: {
     8       type: Number
     9     },
    10     displayTime: {
    11       type: Date
    12     },
    13     selectedDate: {
    14       type: Date
    15     }
    16   },
    17   data: {
    18     weekDayArr: ['日', '一', '二', '三', '四', '五', '六'],
    19   },
    20 
    21   attached: function () { 
    22     //console.log(this)
    23     // debugger
    24   },
    25   methods: {
    26     onDayTap: function (e) {
    27       this.triggerEvent('onDayTap', e.currentTarget.dataset)
    28     }
    29   }
    30 })
    1 <ui-container bindonContainerHide="onContainerHide" is-show="{{isCalendarShow}}" >
    2     <view class="calendar-wrapper-box">
    3       <view class="box-hd">
    4         <text class="fl icon-back js_back "></text>
    5         <text class="fr icon-next js_next"></text>
    6       </view>
    7       <ui-calendar ex-class="calendar-cm-month" bindonDayTap="onCalendarDayTap" 
    displayTime
    ="{{calendarDisplayTime}}" selectedDate="{{calendarSelectedDate}}"
    displayMonthNum
    ="{{calendarDisplayMonthNum}}" is-show="{{isCalendarShow}}"></ui-calendar> 8 </view> 9 </ui-container>

    具体各位去github上查看,总而言之,我们的页面变成了这个样子了:

    PS:这里发现一个不知道是不是坑点的点,我们这里属性传递的是一个date对象,但是到了组件层之间变成了对象,不知微信底层做了什么:

    calendarDisplayTime: new Date()

    好像变成了一个空对象,这里可能发生的情况是,经过传递的日期对象会被某种特殊处理,但是具体发生了什么事情就不知道了,这个却引起了我们不小的麻烦,这里大概去翻开了一下源码:

    极有可能,小程序本身就不支持date属性的传递,我们的日历组件能跑起来的原因是什么,我这里都有点疑惑了......

    而且就算以对象方式传递到组件的date类型都会变成莫名其妙的东西:

    1     ttt: {
    2       key: 'date',
    3       value: selectedDate
    4     },

    这个特性有点令人抓不住头脑了,这里根据探查,很有可能Component将date对象传入WXML解释时候,自动转为了日期字符串了,所以我们这里看上去是对象的东西其实是字符串,这里的建议是:跟组件的date传递,暂时全部使用字符串代替,以免自我麻烦,然后我们先将之前的日历操作全部变成字符串,再为我们的前后按钮加上事件:

     1 module.exports = {
     2   showCalendar: function () {
     3     this.setData({
     4       isCalendarShow: ''
     5     });
     6   },
     7   hideCalendar: function () {
     8     this.setData({
     9       isCalendarShow: 'none'
    10     });
    11   },
    12   preMonth: function () {
    13 
    14     this.setData({
    15       calendarDisplayTime: util.dateUtil.preMonth(this.data.calendarDisplayTime).toString()
    16     });
    17   },
    18   nextMonth: function () {
    19     this.setData({
    20       calendarDisplayTime: util.dateUtil.nextMonth(this.data.calendarDisplayTime).toString()
    21     });
    22   },
    23   onCalendarDayTap: function (e) {
    24     let data = e.detail;
    25     var date = new Date(data.year, data.month, data.day);
    26     console.log(date)
    27     this.setData({
    28       isCalendarShow: 'none',
    29       calendarSelectedDate: date.toString(),
    30       calendarSelectedDateStr: util.dateUtil.format(date, 'Y年M月D日')
    31     });
    32   },
    33   onContainerHide: function () {
    34     this.hideCalendar();
    35   },
    36 
    37   data: {
    38     ttt: {
    39       key: 'date',
    40       value: selectedDate
    41     },
    42     isCalendarShow: '',
    43     calendarDisplayMonthNum: 1,
    44     calendarDisplayTime: new Date(2018, 9).toString(),
    45     calendarSelectedDate: selectedDate,
    46     calendarSelectedDateStr: util.dateUtil.format(new Date(selectedDate), 'Y年M月D日')
    47   }
    48 }

    虽然看上去恶心了一点,但是总是不会出什么明显的问题,忍一忍吧......日期部分基本结束了,还有些小的限制没有做上,比如哪些时段能选,哪些不能,这块就有待各位发现吧,我们这里毕竟是学习,做细了很花功夫,我们接下来做出发目的地选择部分。

    数据请求

    城市列表

    城市列表这里看起来需要新开一个页面,但是我这里想做在一个页面中,考虑篇幅,我们使用弹出层容器组件看并且尽量削弱一些特性,几天下来别说写的还有些累......

    这个又作为首页的一个模块而存在:

     1 <view style="display: {{isCityShow}}; " class="city-wrapper"  >
     2     <view class="city-list">
     3         <view class="list-name">A</view>
     4         <view class="list-item">成都</view>
     5         <view class="list-item">成都</view>
     6         <view class="list-item">成都</view>
     7         <view class="list-item">成都</view>
     8         <view class="list-item">成都</view>
     9         <view class="list-item">成都</view>
    10     </view>
    11     <view class="city-list">
    12         <view class="list-name">A</view>
    13         <view class="list-item">成都</view>
    14         <view class="list-item">成都</view>
    15         <view class="list-item">成都</view>
    16         <view class="list-item">成都</view>
    17         <view class="list-item">成都</view>
    18         <view class="list-item">成都</view>
    19     </view>
    20 </view>
     1 /*
     2 事实上一个mod就只是一个对象,只不过为了方便拆分,将对象分拆成一个个的mod
     3 一个mod对应一个wxml,但是共享外部的css,暂时如此设计
     4 所有日历模块的需求全部再此实现
     5 */
     6 const util = require('../../../utils/util.js')
     7 
     8 let selectedDate = new Date().toString();
     9 
    10 module.exports = {
    11   showCitylist: function (e) {
    12     let flag = e.currentTarget.dataset.flag;
    13 
    14     if(flag === 'start') {
    15 
    16     } else {
    17 
    18     }
    19   },
    20   //用于设置城市数据
    21   setCityData: function (data) {
    22 
    23   },
    24   showCity: function () {
    25       this.setData({
    26         isCityShow: ''
    27       });
    28   },
    29   shideCity: function () {
    30     this.setData({
    31       isCityShow: 'none'
    32     });
    33   },
    34   data: {
    35     isCityShow: ''
    36   }
    37 }

    首页调用代码:

     1 //获取公共ui操作类实例
     2 const _page = require('../../utils/abstract-page.js');
     3 let modCalendar = require('./mod/calendar.js');
     4 let modCity = require('./mod/city.js');
     5 
     6 //获取应用实例
     7 const app = getApp()
     8 
     9 Page(_page.initPage({
    10   data: {
    11   },
    12   // methods: uiUtil.getPageMethods(),
    13   methods: {
    14   },
    15   onShow: function () {
    16     global.sss = this;
    17     let scope = this;
    18   },
    19   onLoad: function () {
    20     // this.setPageMethods();
    21   }
    22 }, {
    23   modCalendar: modCalendar,
    24   modCity: modCity
    25 }))

    这里我们开始有数据请求模块了,小程序使用这个接口请求数据,这里比较尴尬的是他要设置域名白名单:

    wx.request(OBJECT)

    而我们使用的是测试账号没有可以设置的地方,所以我们还是去申请个小程序账号吧...配置成功,我们继续代码:

    可以看到数据请求已经回来了,但是我们一般来说一个接口不止会用于一个地方,每次重新写好像有些费事,加之我这里想将重复的请求缓存起来,所以我们这里封装一套数据访问层出来

    数据缓存(持久层)

    之前在浏览器中,我们一般使用localstorage存储一些不太更改的数据,微信里面提供了接口处理这一切:

    wx.setStorage(OBJECT)

    我们这里需要对其进行简单封装,便与后面更好的使用,一般来说有缓存就一定要有过期,所以我们动态给每个缓存对象增加一个过期时间:

      1 class Store {
      2   constructor(opts) {
      3     if(typeof opts === 'string') this.key = opts;
      4     else Object.assign(this, opts);
      5 
      6     //如果没有传过期时间,则默认30分钟
      7     if(!this.lifeTime) this.lifeTime = 1;
      8 
      9     //本地缓存用以存放所有localstorage键值与过期日期的映射
     10     this._keyCache = 'SYSTEM_KEY_TIMEOUT_MAP';
     11 
     12   }
     13   //获取过期时间,单位为毫秒
     14   _getDeadline() {
     15     return this.lifeTime * 60 * 1000;
     16   }
     17 
     18   //获取一个数据缓存对象,存可以异步,获取我同步即可
     19   get(sign){
     20     let key = this.key;
     21     let now = new Date().getTime();
     22     var data = wx.getStorageSync(key);
     23     if(!data) return null;
     24     data = JSON.parse(data);
     25     //数据过期
     26     if (data.deadLine < now) {
     27       this.removeOverdueCache();
     28       return null;
     29     }
     30 
     31     if(data.sign) {
     32       if(sign === data.sign) return data.data;
     33       else return null;
     34     }
     35     return null;
     36   }
     37 
     38   /*产出页面组件需要的参数
     39   sign 为格式化后的请求参数,用于同一请求不同参数时候返回新数据,比如列表为北京的城市,后切换为上海,会判断tag不同而更新缓存数据,tag相当于签名
     40   每一键值只会缓存一条信息
     41   */
     42   set(data, sign) {
     43     let timeout = new Date();
     44     let time = timeout.setTime(timeout.getTime() + this._getDeadline());
     45     this._saveData(data, time, sign);
     46   }
     47   _saveData(data, time, sign) {
     48     let key = this.key;
     49     let entity = {
     50       deadLine: time,
     51       data: data,
     52       sign: sign
     53     };
     54     let scope = this;
     55 
     56     wx.setStorage({
     57       key: key,
     58       data: JSON.stringify(entity),
     59       success: function () {
     60         //每次真实存入前,需要往系统中存储一个清单
     61         scope._saveSysList(key, entity.deadLine);
     62       }
     63     });
     64   }
     65   _saveSysList(key, timeout) {
     66     if (!key || !timeout || timeout < new Date().getTime()) return;
     67     let keyCache = this._keyCache;
     68     wx.getStorage({
     69       key: keyCache,
     70       complete: function (data) {
     71         let oldData = {};
     72         if(data.data) oldData = JSON.parse(data.data);
     73         oldData[key] = timeout;
     74         wx.setStorage({
     75           key: keyCache,
     76           data: JSON.stringify(oldData)
     77         });
     78       }
     79     });
     80   }
     81   //删除过期缓存
     82   removeOverdueCache() {
     83     let now = new Date().getTime();
     84     let keyCache = this._keyCache;
     85     wx.getStorage({
     86       key: keyCache,
     87       success: function (data) {
     88         if(data && data.data) data = JSON.parse(data.data);
     89         for(let k in data) {
     90           if(data[k] < now) {
     91             delete data[k];
     92             wx.removeStorage({key: k, success: function(){}});
     93           }
     94         }
     95         wx.setStorage({
     96           key: keyCache,
     97           data: JSON.stringify(data)
     98         });
     99       }
    100     });
    101   }
    102 
    103 }
    104 
    105 module.exports = Store

    这个类的使用也非常简单,这里举个例子:

    1 sss = new global.Store({key: 'qqq', lifeTime: 1})
    2 sss.set({a: 1}, 2)
    3 sss.get()//因为没有秘钥会是null
    4 sss.get(2)//sss.get(2)

    这个时候我们开始写我们数据请求的类:

    首先还是实现了一个抽象类和一个业务基类,然后开始在业务层请求数据:

     1 class Model {
     2   constructor() {
     3     this.url = '';
     4     this.param = {};
     5     this.validates = [];
     6   }
     7   pushValidates(handler) {
     8     if (typeof handler === 'function') {
     9       this.validates.push(handler);
    10     }
    11   }
    12   setParam(key, val) {
    13     if (typeof key === 'object') {
    14       Object.assign(this.param, key);
    15     } else {
    16       this.param[key] = val;
    17     }
    18   }
    19   //@override
    20   buildurl() {
    21     return this.url;
    22   }
    23   onDataSuccess() {
    24   }
    25   //执行数据请求逻辑
    26   execute(onComplete) {
    27     let scope = this;
    28     let _success = function(data) {
    29       let _data = data;
    30       if (typeof data == 'string') _data = JSON.parse(data);
    31 
    32       // @description 开发者可以传入一组验证方法进行验证
    33       for (let i = 0, len = scope.validates.length; i < len; i++) {
    34         if (!scope.validates[i](data)) {
    35           // @description 如果一个验证不通过就返回
    36           if (typeof onError === 'function') {
    37             return onError.call(scope || this, _data, data);
    38           } else {
    39             return false;
    40           }
    41         }
    42       }
    43 
    44       // @description 对获取的数据做字段映射
    45       let datamodel = typeof scope.dataformat === 'function' ? scope.dataformat(_data) : _data;
    46 
    47       if (scope.onDataSuccess) scope.onDataSuccess.call(scope, datamodel, data);
    48       if (typeof onComplete === 'function') {
    49         onComplete.call(scope, datamodel, data);
    50       }
    51     };
    52     this._sendRequest(_success);
    53   }
    54 
    55   //删除过期缓存
    56   _sendRequest(callback) {
    57     let url = this.buildurl();
    58     wx.request({
    59       url: this.buildurl(),
    60       data: this.param,
    61       success: function success(data) {
    62         callback && callback(data);
    63       }
    64     });
    65   }
    66 }
    67 module.exports = Model
     1 let Model = require('./abstract-model.js');
     2 
     3 class DemoModel extends Model {
     4   constructor() {
     5     super();
     6     let scope = this;
     7     this.domain = 'https://apikuai.baidu.com';
     8     this.param = {
     9       head: {
    10         version: '1.0.1',
    11         ct: 'ios'
    12       }
    13     };
    14 
    15     //如果需要缓存,可以在此设置缓存对象
    16     this.cacheData = null;
    17 
    18     this.pushValidates(function(data) {
    19       return scope._baseDataValidate(data);
    20     });
    21   }
    22 
    23   //首轮处理返回数据,检查错误码做统一验证处理
    24   _baseDataValidate(data) {
    25     if (typeof data === 'string') data = JSON.parse(data);
    26     if (data.data) data = data.data;
    27     if (data.errno === 0) return true;
    28     return false;
    29   }
    30 
    31   dataformat(data) {
    32     if (typeof data === 'string') data = JSON.parse(data);
    33     if (data.data) data = data.data;
    34     if (data.data) data = data.data;
    35     return data;
    36   }
    37 
    38   buildurl() {
    39     return this.domain + this.url;
    40   }
    41 
    42   getSign() {
    43     let param = this.getParam() || {};
    44     return JSON.stringify(param);
    45   }
    46   onDataSuccess(fdata, data) {
    47     if (this.cacheData && this.cacheData.set)
    48       this.cacheData.set(fdata, this.getSign());
    49   }
    50 
    51   //如果有缓存直接读取缓存,没有才请求
    52   execute(onComplete, ajaxOnly) {
    53     let data = null;
    54     if (!ajaxOnly && this.cacheData && this.cacheData.get) {
    55       data = this.cacheData.get(this.getSign());
    56       if (data) {
    57         onComplete(data);
    58         return;
    59       }
    60     }
    61     super.execute(onComplete);
    62   }
    63 
    64 }
    65 
    66 class CityModel extends DemoModel {
    67   constructor() {
    68     super();
    69     this.url = '/city/getstartcitys';
    70   }
    71 }
    72 
    73 module.exports = {
    74   cityModel: new CityModel
    75 
    76 }
    业务基类

    接下来是实际调用代码:

    1 let model = models.cityModel;
    2 model.setParam({
    3   type: 1
    4 });
    5 model.execute(function(data) {
    6   console.log(data);
    7   debugger;
    8 });

    数据便请求结束了,有了这个类我们可以做非常多的工作,比如:

    ① 前端设置统一的错误码处理逻辑

    ② 前端打点,统计所有的接口响应状态

    ③ 每次请求相同参数做数据缓存

    ④ 这个对于错误处理很关键,一般来说前端出错很大可能都是后端数据接口字段有变化,而这种错误是比较难寻找的,如果我这里做一个统一的收口,每次数据返回记录所有的返回字段的标志上报呢,就以这个城市数据为例,我们可以这样做:

     1 class CityModel extends DemoModel {
     2   constructor() {
     3     super();
     4     this.url = '/city/getstartcitys';
     5   }
     6   //每次数据访问成功,错误码为0时皆会执行这个回调
     7   onDataSuccess(fdata, data) {
     8     super.onDataSuccess(fdata, data);
     9     //开始执行自我逻辑
    10     let o = {
    11       _indate: new Date().getTime()
    12     };
    13     for(let k in fdata) {
    14       o[k] = typeof fdata[k];
    15     }
    16     //执行数据上报逻辑
    17     console.log(JSON.stringify(o));
    18   }
    19 }

    这里就会输出以下信息:

    {"_indate":1533436847778,"cities":"object","hots":"object","total":"number","page":"string"}

    如果对数据要求非常严苛,对某些接口做到字段层面的验证,那么加一个Validates验证即可,这样对接口的控制会最大化,就算哪次出问题,也能很好从数据分析系统之中可以查看到问题所在,如果我现在想要一个更为具体的功能呢?我想要首次请求一个接口时便将其数据记录下来,第二次便不再请求呢,这个时候我们之前设计的数据持久层便派上了用处:

     1 let Store = require('./abstract-store.js');
     2 
     3 class CityStore extends Store {
     4   constructor() {
     5     super();
     6     this.key = 'DEMO_CITYLIST';
     7     //30分钟过期时间
     8     this.lifeTime = 30;
     9   }
    10 }
    11 
    12 module.exports = {
    13   cityStore: new CityStore
    14 }
     1 class CityModel extends DemoModel {
     2   constructor() {
     3     super();
     4     this.url = '/city/getstartcitys';
     5     this.cacheData = Stores.cityStore;
     6   }
     7   //每次数据访问成功,错误码为0时皆会执行这个回调
     8   onDataSuccess(fdata, data) {
     9     super.onDataSuccess(fdata, data);
    10     //开始执行自我逻辑
    11     let o = {
    12       _indate: new Date().getTime()
    13     };
    14     for(let k in fdata) {
    15       o[k] = typeof fdata[k];
    16     }
    17     //执行数据上报逻辑
    18     console.log(JSON.stringify(o));
    19   }
    20 }

    这个时候第二次请求时候便会直接读取缓存了

    接下来便可以回到我们的页面渲染逻辑了,这个时候就变得非常简单了:

     1 <view style="display: {{isCityShow}}; " class="city-wrapper">
     2   <block wx:for="{{cityData}}" wx:key="k">
     3     <view class="city-list">
     4       <block wx:for="{{item}}" wx:key="kk" wx:for-index="key" wx:for-item="value">
     5         <view class="list-name">{{key}}</view>
     6         <block wx:for="{{value}}" wx:key="kkk" wx:for-index="i" wx:for-item="v">
     7           <view class="list-item" data-cnname="{{v.name}}" data-id="{{v.regionid}}">{{v.cnname}}</view>
     8         </block>
     9       </block>
    10     </view>
    11   </block>
    12 </view>
     1 //用于设置城市数据
     2   setCityData: function(data) {
     3     data = data.cities;
     4     let citys = {}, sortCitys = [];
     5     let k, gname, name, i, tmp = {}, index;
     6 
     7     //首先处理每个name生成唯一K
     8     for (k in data) {
     9       name = data[k].name;
    10       if (!name) {
    11         continue;
    12       }
    13       gname = name[0].toUpperCase();
    14       if (!citys[gname]) citys[gname] = [];
    15       citys[gname].push(data[k]);
    16     }
    17 
    18     for (i = 65; i < 91; i++) {
    19       tmp = {};
    20       tmp[String.fromCharCode(i)] = [];
    21       sortCitys.push(tmp);
    22     }
    23 
    24     for (k in citys) {
    25       index = k.charCodeAt() - 65;
    26       tmp = {};
    27       tmp[k] = citys[k];
    28       sortCitys[index] = tmp;
    29     }
    30 
    31     this.setData({
    32       cityData: sortCitys,
    33       isCityShow: ''
    34     });
    35   },

    然后我们这里为组件绑定事件等就比较简单了,大家可以自己看github,于是我们首页的功能便完成了:

    经过一个多星期的学习,我们慢慢的完成了我们的首页,好像也就几个元素,但是后面的一切却不简单啊,我们明天继续完成list页面逻辑,便开始总结小程序开发

  • 相关阅读:
    LInux-crontab
    Linux权限-chmod1
    Tool_BurpSuite安装和简单使用
    python与redis交互(四)
    Flask_环境部署(十六)
    Nginx_配置文件nginx.conf配置详解
    Tool_linux环境安装python3和pip
    Nginx_全局命令设置
    Linux_无法解析域名
    VMware_克隆机器后主机Ping不同虚拟机,虚拟机能Ping通主机
  • 原文地址:https://www.cnblogs.com/yexiaochai/p/9419368.html
Copyright © 2011-2022 走看看