小程序TodoList實踐

TodoList實踐

看完官方的文檔介紹後,就想找個簡單的例子來驗證明現一下,TodoList MVC就很好了,簡單容易。javascript

以前都用JQ、Backbone、vue簡單擼過,大概功能以下:html

  • 添加todo
  • 儲存在應用緩存
  • 列表展現
  • 區分狀態顯示:所有、未完成、已完成
  • 改變todo狀態
  • 刪除todo

根據以上功能,小程序完成以下:vue

GitHub: github.com/CH563/TodoL…java

下面記錄一下個人完成過程:

下載小程序開發工具:開發者工具下載git

安裝完成後,使用微信掃一掃登陸,選好文件夾後建立便可,開發工具會自動生成如下目錄:es6

pages/github

app.jsjson

app.jsongulp

app.wxss小程序

文錄紹構和具體配置查看官方文檔:mp.weixin.qq.com/debug/wxado…

基本配置

因爲平時開發習慣用Less,若是在直接使用小程序的wxss來編寫的話,就恢復原生編寫方式,大大的不便,因此直接使用了gulp來實時編譯Less,和修改文件名爲wxss。小程序開發工具是不支持Less,直接用vscode來開發,小程序開發工具是用實時預覽和調試便可,vscode也有豐富的插件支待小程序語法提示。

// gulpfile.js
var gulp = require('gulp')
var less = require('gulp-less')
var plumber = require('gulp-plumber')
var rename = require('gulp-rename')

gulp.task('less', function () {
  return gulp.src('./app.less')
    .pipe(plumber())  // 錯誤處理
    .pipe(less()) // 編譯less
    .pipe(rename((path) => path.extname = '.wxss')) // 編譯後生成文件修改後綴爲.wxss
    .pipe(gulp.dest('./'));
});
gulp.watch('./app.less', ['less']); // 實時監控app.less文件變化,運行任務
複製代碼

UI組件也直接引用了小程序支持的weui-wxss

@import "./weui.wxss";
複製代碼

在app.json定義好小程序頁面路由和配色:

{
  "pages":[
    "pages/index/index"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#ca2100",
    "navigationBarTitleText": "TodoList",
    "navigationBarTextStyle":"white"
  }
}
複製代碼

頁面開發

頁面文件都存放在pages/目錄下,每一個功能頁面都會建立一個文件夾,TodoList現只需一個頁面完成便可

數據綁定使用 Mustache 語法(雙大括號)將變量包起來

<text class="userinfo-nickname">{{userInfo.nickName}}</text>
<!-- 三元運算 -->
<text class="{{status === '1'?'active':''}}" data-status="1" bindtap="showStatus">所有</text>
複製代碼

添加todo

使用字段addShow來判斷添加輸入層顯示隱藏便可

input輸出框這裏不是雙向綁定,因此這裏添加一個事件bindinput="setInput"來賦值實時變化

<view class="addForm {{addShow?'':'hide'}}">
    <view class="addForm-div">
      <input class="weui-input" placeholder="請輸入todo" value="{{addText}}" bindinput="setInput" focus="{{focus}}" />
      <view class="addForm-btn">
        <button class="weui-btn mini-btn" type="warn" bindtap="addTodo" size="mini">肯定添加</button>
        <button class="weui-btn mini-btn" type="default" bindtap="addTodoHide" size="mini">取消</button>
      </view>
    </view>
  </view>
複製代碼

實時賦值事件處理

setInput: function (e) {
    this.setData({
      addText: e.detail.value
    })
}
複製代碼

取消時,須要清空input的值,input裏須要綁定value="{{addText}}"

Page({
 data:{
     //...
 }, 
 //...
 addTodoHide: function () {
    this.setData({
      addShow: false, // 控制添加輸入面板隱藏
      focus: false, // 失去焦點
      addText: '' // 清空值
    })
 }
 //...
})
複製代碼

添加todo

Page({
 data:{
     //...
 }, 
 //...
 addTodo: function () {
    // 檢查有沒有輸入
    if (!this.data.addText.trim()) {
      return
    }
    var temp = this.data.lists // 取出lists
    var addT = {
      id: new Date().getTime(), // 取當前時間
      title: this.data.addText,
      status: '0'
    }
    temp.push(addT) // 添加新的todo
    this.showCur(temp) // 處理當前狀態的方法
    this.addTodoHide() // 添加成功後,隱藏添加面板方法
    wx.setStorage({ // 小程序異步緩存
      key:"lists",
      data: temp
    })
    wx.showToast({ // weui toast組件
      title: '添加成功!',
      icon: 'success',
      duration: 1000
    });
 }
 //...
})
複製代碼

列表部分

scroll-view內滾動

列表渲染,事件觸發,利用data傳參,bind綁定事件

<scroll-view class="lists" scroll-y>
    <!-- 判斷列表是否爲空 -->
    <block wx:if="{{curLists.length < 1}}">
      <view class="nodata">暫無數據</view>
    </block>
    <!-- 列表渲染 -->
    <view class="item" wx:for="{{curLists}}" wx:key="index">
      <!-- 內容view,綁定touch三個件事,來實現滑動冊除 -->
      <view class="content" style="{{item.txtStyle}}" data-index="{{index}}" bindtouchstart="touchS" bindtouchmove="touchM" bindtouchend="touchE">
        <!-- checkbox圖標,changeTodo事件來控制狀態切換 -->
        <icon class="icon-small" type="{{item.status === '0'?'circle':'success'}}" size="23" data-item="{{item.id}}" bindtap="changeTodo"></icon>
        <text class="title {{item.status === '1'?'over':''}}">{{item.title}}</text>
        <!-- api.formatTime是使用了wxs模塊化編寫的模塊 -->
        <text class="time">{{api.formatTime(item.id)}}</text>
      </view>
      <!-- 刪除按鈕,綁定刪除事件 -->
      <view class="del" data-item="{{item.id}}" bindtap="delTodo"><text>刪除</text></view>
    </view>
  </scroll-view>
複製代碼

滑動刪除

效果:當向左滑動時,content跟隨手指像左移動,同時右側出現del按鈕;當滑動距離大於按鈕寬度一半鬆開手指時自動滑動到左側顯示出按鈕,小於一半時自動回到原來的位置,隱藏按鈕。

實現思路: content和del按鈕分別是絕對定位,利用z-index層來控制讓content來蓋住del,當content向左滑動時,del按鈕就會露出來。

微信小程序api提供的touch對象和3個有關手指觸摸的函數(touchstart,touchmove,touchend)來實現content隨手指移動

詳細api說明,請查看:mp.weixin.qq.com/debug/wxado…

列表的content已綁定這個三個事件:bindtouchstart="touchS" bindtouchmove="touchM" bindtouchend="touchE"

實現方法:

注意txtStyle,在content在綁定這個屬性的,實現跟隨手指移動的style="{{item.txtStyle}}"

delBtnWidth爲了del按鈕的寬度,這裏以rpx爲單位

Page({
 data:{
     //...
 }, 
 //...
  touchS: function (e) {
    // console.log('開始:' + JSON.stringify(e))
    // 是否只有一個觸摸點
    if(e.touches.length === 1){
      this.setData({
        // 觸摸起始的X座標
        startX: e.touches[0].clientX
      })
    }
  },
  touchM: function (e) {
    // console.log('移動:' + JSON.stringify(e))
    var _this = this
    if(e.touches.length === 1){
     // 觸摸點的X座標
      var moveX = e.touches[0].clientX
      // 計算手指起始點的X座標與當前觸摸點的X座標的差值
      var disX = _this.data.startX - moveX
     // delBtnWidth 爲右側按鈕區域的寬度
      var delBtnWidth = _this.data.delBtnWidth
      var txtStyle = ''
      if (disX == 0 || disX < 0){ // 若是移動距離小於等於0,文本層位置不變
        txtStyle = 'left:0'
      } else if (disX > 0 ){ // 移動距離大於0,文本層left值等於手指移動距離
        txtStyle = 'left:-' + disX + 'rpx'
        if(disX >= delBtnWidth){
          // 控制手指移動距離最大值爲刪除按鈕的寬度
          txtStyle = 'left:-' + delBtnWidth + 'rpx'
        }
      }
      // 獲取手指觸摸的是哪個item
      var index = e.currentTarget.dataset.index;
      var list = _this.data.curLists
      // 將拼接好的樣式設置到當前item中
      list[index].txtStyle = txtStyle
      // 更新列表的狀態
      this.setData({
        curLists: list
      });
    }
  },
  touchE: function (e) {
    // console.log('中止:' + JSON.stringify(e))
    var _this = this
    if(e.changedTouches.length === 1){
      // 手指移動結束後觸摸點位置的X座標
      var endX = e.changedTouches[0].clientX
      // 觸摸開始與結束,手指移動的距離
      var disX = _this.data.startX - endX
      var delBtnWidth = _this.data.delBtnWidth
      // 若是距離小於刪除按鈕的1/2,不顯示刪除按鈕
      var txtStyle = disX > delBtnWidth/2 ? 'left:-' + delBtnWidth + 'rpx' : 'left:0'
      // 獲取手指觸摸的是哪一項
      var index = e.currentTarget.dataset.index
      var list = _this.data.curLists
      list[index].txtStyle = txtStyle
      // 更新列表的狀態
      _this.setData({
        curLists: list
      });
    }
  }
  //...
})
複製代碼

WXS實現時間格式

實如今效果以下:

這裏我使用小程序的WXS

WXS(WeiXin Script)是小程序的一套腳本語言,結合 WXML,能夠構建出頁面的結構。瞭解詳細

新建一個api.wxs文件,而後在index.wxml引用,定義模塊名便可引用:

<!-- index.wxml -->
<wxs src="./api.wxs" module="api" />
...
<text class="time">{{api.formatTime(item.id)}}</text>
複製代碼

每一個 wxs 模塊均有一個內置的 module 對象

api.wxs文件和時間格式實現方法:

var formatTime = function(time){
  // 獲取當前時間
  var getUnix = function () {
    var date = getDate()
    return date.getTime()
  }
  // 獲取今天零點時間
  var getTodayUnix = function () {
    var date = getDate()
    date.setHours(0)
    date.setMinutes(0)
    date.setSeconds(0)
    date.setMilliseconds(0)
    return date.getTime()
  }
  // 獲取今年的1月1日零點時間
  var getYearUnix = function () {
    var date = getDate()
    date.setMonth(0)
    date.setDate(1)
    date.setHours(0)
    date.setMinutes(0)
    date.setSeconds(0)
    date.setMilliseconds(0)
    return date.getTime()
  }
  // 獲取標準時間
  var getLastDate = function (time) {
    var date = getDate(time)
    var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
    var day = date.getDay() < 10 ? '0' + (date.getDay()) : date.getDay()
    return date.getFullYear() + '-' + month + '-' + day
  }
  // 轉換時間
  var getFormatTime = function (timestamp) {
    var now = getUnix()
    var today = getTodayUnix()
    var year = getYearUnix()
    var timer = (now - timestamp) / 1000
    var tip = ''
    if (timer <= 0) {
      tip = '剛剛'
    } else if (Math.floor(timer / 60) <= 0) {
      tip = '剛剛'
    } else if (timer < 3600) {
      tip = Math.floor(timer / 60) + '分鐘前'
    } else if (timer >= 3600 && (timestamp - today >= 0)) {
      tip = Math.floor(timer / 3600) + '小時前'
    } else if (timer / 86400 <= 31) {
      tip = Math.ceil(timer / 86400) + '天前'
    } else {
      tip = getLastDate(timestamp)
    }
    return tip
  }
  return getFormatTime(+time)
}

// es6方法同樣,導出formatTime方法
module.exports.formatTime = formatTime;
複製代碼

這裏須要注意的是獲取當前時間,WXS是不支持new Date(),它有本身自己的方法getDate()。使用方法跟new Date()同樣。

至此已完成,TodoList實踐例子,功能簡單,入門好例子。

但願對你們有幫助,不足的地方請你們指點,剛啃完官方文檔擼的例子。

原碼下載:github.com/CH563/TodoL…

願你們都升職加薪!

相關文章
相關標籤/搜索