微信小程序todolist报告

TODO List

已上线,可扫描二维码测试:

设计思想

MVC

MVC是经典的软件架构模式,将应用程序划分为三种组件,模型,视图,控制器设计定义它们之间的相互作用。
微信小程序的MINA框架其核心思想正是MVC。通过对MVC的了解,我们可以从更抽象的层面设计小程序的功能逻辑,避免因为繁杂的技术细节,而失去了对程序功能的全面理解

前后端分离

前后端分离不光体现在软件架构的分离上,还体现在开发人员与工作流程的分离上。

在小组合作的过程中,通过事先的合作,设计小程序所需要的相关api与对应json数据格式。前端通过小程序提供的wx.request(),后端通过Go Gin进行相应的网络请求的应答,最后使用mysql数据库进行数据的管理。

在这个过程中,负责前端的成员与负责后端的成员同时独立进行,定期沟通进展,确保小程序开发的进展顺利。

前后端分离的设计方法,不但保证了项目在设计层面的简单,也极大的提高了开发效率,避免了因为沟通等问题耽误了开发进展。

界面设计

在原则上,小程序的整体设计风格以简约为主,力图使程序简单易懂,便于上手。

数据交互与实时反馈

为了用户的无感知使用,避免因网络原因造成的等待,小程序对网络的请求遵循了最小化原则,不进行不必要的网络请求,对重要的业务逻辑,先在本地进行数据更新,之后再与云端同步。

关键技术点

前端

image-20211224135258993
 <view class="header">
    <image class="plus" src="../../assets/plus.png"/>
    <input class="new-todo" value="{{ input }}" placeholder="请添加代办" auto-focus bindinput="inputChangeHandle" bindconfirm="addTodoHandle"/>
  </view>
 inputChangeHandle: function (e) {
    this.setData({ input: e.detail.value })
  },
  addTodoHandle: function (e) {
    if (!this.data.input || !this.data.input.trim()) return
    var todos = this.data.todos
    todos.push({ name: this.data.input, completed: false })
    var logs = this.data.logs
    logs.push({ timestamp: new Date(), action: '添加', name: this.data.input })
    this.setData({
      input: '',
      todos: todos,
      leftCount: this.data.leftCount + 1,
      logs: logs
    })
    this.save()
  },
image-20211224135359530
//打勾toggleTodoHandle
//清除打勾代办clearCompletedHandle
//直接清除removeTodoHandle

<block wx:if="{{ todos.length }}">
    <view class="todos">
      <!-- 完成的项目划线 -->
      <view class="item{{ item.completed ? ' completed' : '' }}" wx:for="{{ todos }}" wx:key="{{ index }}" bindtap="toggleTodoHandle" data-index="{{ index }}">
        <!-- 完成的打勾,没完成的圆圈 -->
        <icon class="checkbox" type="{{ item.completed ? 'success' : 'circle' }}"/>
        <text class="name">{{ item.name }}</text>
        <icon class="remove" type="clear" size="16" catchtap="removeTodoHandle" data-index="{{ index }}"/>
      </view>
    </view>
    <view class="footer">
      <text class="btn" bindtap="toggleAllHandle">完成全部代办</text>
      <text wx:if="{{ leftCount }}"> 还剩 {{ leftCount }} {{ leftCount === 1 ? '个代办' : '个代办' }} </text>
      <text class="btn" wx:if="{{ todos.length > leftCount }}" bindtap="clearCompletedHandle">确认清除</text>
    </view>
  </block>
//数据结构
data: {
    input: '',
    todos: [], //todo
    leftCount: 0,
    allCompleted: false,
    logs: [], //记录
    id:"0"
  },
//添加记录信息  
toggleTodoHandle: function (e) {
    var index = e.currentTarget.dataset.index
    var todos = this.data.todos
    todos[index].completed = !todos[index].completed
    var logs = this.data.logs
    logs.push({
      timestamp: new Date(),
      action: todos[index].completed ? '完成' : '重启',
      name: todos[index].name
    })
    this.setData({
      todos: todos,
      leftCount: this.data.leftCount + (todos[index].completed ? -1 : 1),
      logs: logs
    })
    this.save()
  },
    removeTodoHandle: function (e) {
    var index = e.currentTarget.dataset.index
    var todos = this.data.todos
    var remove = todos.splice(index, 1)[0]
    var logs = this.data.logs
    logs.push({ timestamp: new Date(), action: '直接清除', name: remove.name })
    this.setData({
      todos: todos,
      leftCount: this.data.leftCount - (remove.completed ? 0 : 1),
      logs: logs
    })
    this.save()
  },
    toggleAllHandle: function (e) {
    this.data.allCompleted = !this.data.allCompleted
    var todos = this.data.todos
    for (var i = todos.length - 1; i >= 0; i--) {
      todos[i].completed = this.data.allCompleted
    }
    var logs = this.data.logs
    logs.push({
      timestamp: new Date(),
      action: this.data.allCompleted ? '完成' : '重启',
      name: '完成全部代办'
    })
    this.setData({
      todos: todos,
      leftCount: this.data.allCompleted ? 0 : todos.length,
      logs: logs
    })
    this.save()
  },
     clearCompletedHandle: function (e) {
    var todos = this.data.todos
    var remains = []
    for (var i = 0; i < todos.length; i++) {
      todos[i].completed || remains.push(todos[i])
    }
    var logs = this.data.logs
    logs.push({
      timestamp: new Date(),
      action: '清除',
      name: '清除代办'
    })
    this.setData({ todos: remains, logs: logs })
    this.save()
  }
image-20211224135453569
<block wx:else>
    <view class="empty">
      <text class="title">太棒了,现在没有代办!</text>
      <text class="content">要么你真的做完了,要么你还没有开始</text>
      <view style=" color:darkgrey; padding: 120rpx; ">
        <text style="font-size: 30rpx;">
      </text>
      </view>
      
    </view>
  </block>
//wxml
<view class="container">
  <view class="logs" wx:if="{{ logs.length }}">
    <view class="item" wx:for="{{ logs }}" wx:key="{{ index }}">
      <text class="name">代办名称: {{ item.name }}</text>
      <text class="action">状态:{{ item.action }}</text>
      <text class="timestamp">时间:[{{ item.timestamp }}]</text>
    </view>
  </view>
</view>

//logs.js
Page({
  data:{
    logs: []
  },
  onShow: function () {
    var logs = wx.getStorageSync('todo_logs')
    if (logs) {
      this.setData({ logs: logs.reverse() })
    }
  },
})
//index.js
Page({
  data: {
    input: '',
    todos: [],
    leftCount: 0,
    allCompleted: false,
    logs: [],
    id:"0"
  },
  save: function () {
    wx.setStorageSync('todo_list', this.data.todos)
    wx.setStorageSync('todo_logs', this.data.logs)
  },
  getUserID: function(){
    wx.getUserInfo({
      success: function(res){
        id = res.userInfo.nickName
      }
    })
  },
  loadFromDB: function() {
    wx.request({
      url : "175.24.118.206:8080/api/v1/GetUserInfo",
      method: "POST",
      data: {
        answer : JSON.stringify({ID: "1"}),
      },
      header: {
        "Content-Type": "application/json"
      },
      success: function (res) {
        console.log(res.data);
        this.setData({
          todos:res.data.things
        })
      },
    })
  },
  load: function () {
    this.loadFromDB()
    if (this.data.todos) {
      var leftCount = this.data.todos.filter(function (item) {
        return !item.completed
      }).length
      this.setData({leftCount: leftCount })
    }
    var logs = wx.getStorageSync('todo_logs')
    if (logs) {
      this.setData({ logs: logs })
    }
  },

  onLoad: function () {
    this.load()
  },

  inputChangeHandle: function (e) {
    this.setData({ input: e.detail.value })
  },

  addTodoHandle: function (e) {
    if (!this.data.input || !this.data.input.trim()) return
    var todos = this.data.todos
    todos.push({ name: this.data.input, completed: false })
    var logs = this.data.logs
    logs.push({ timestamp: new Date(), action: '添加', name: this.data.input })
    this.setData({
      input: '',
      todos: todos,
      leftCount: this.data.leftCount + 1,
      logs: logs
    })
    this.save()
  },

  toggleTodoHandle: function (e) {
    var index = e.currentTarget.dataset.index
    var todos = this.data.todos
    todos[index].completed = !todos[index].completed
    var logs = this.data.logs
    logs.push({
      timestamp: new Date(),
      action: todos[index].completed ? '完成' : '重启',
      name: todos[index].name
    })
    this.setData({
      todos: todos,
      leftCount: this.data.leftCount + (todos[index].completed ? -1 : 1),
      logs: logs
    })
    this.save()
  },

  removeTodoHandle: function (e) {
    var index = e.currentTarget.dataset.index
    var todos = this.data.todos
    var remove = todos.splice(index, 1)[0]
    var logs = this.data.logs
    logs.push({ timestamp: new Date(), action: '直接清除', name: remove.name })
    this.setData({
      todos: todos,
      leftCount: this.data.leftCount - (remove.completed ? 0 : 1),
      logs: logs
    })
    this.save()
  },

  toggleAllHandle: function (e) {
    this.data.allCompleted = !this.data.allCompleted
    var todos = this.data.todos
    for (var i = todos.length - 1; i >= 0; i--) {
      todos[i].completed = this.data.allCompleted
    }
    var logs = this.data.logs
    logs.push({
      timestamp: new Date(),
      action: this.data.allCompleted ? '完成' : '重启',
      name: '完成全部代办'
    })
    this.setData({
      todos: todos,
      leftCount: this.data.allCompleted ? 0 : todos.length,
      logs: logs
    })
    this.save()
  },
  
  clearCompletedHandle: function (e) {
    var todos = this.data.todos
    var remains = []
    for (var i = 0; i < todos.length; i++) {
      todos[i].completed || remains.push(todos[i])
    }
    var logs = this.data.logs
    logs.push({
      timestamp: new Date(),
      action: '清除',
      name: '清除代办'
    })
    this.setData({ todos: remains, logs: logs })
    this.save()
  }
})

后端

package main

import (
	//"go/printer"
	"log"
	"net/http"

	"database/sql"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	r := gin.New()
	db, err := sql.Open("mysql", "root:123456@tcp(175.24.118.206:3306)/homework")
	if err != nil   //GetUserInfo api,用来获取服务器上的用户信息和数据
	r.POST("/api/v1/GetUserInfo", func(c *gin.Context) {
		param := struct {
			UserID string `json:"ID" binding:"required"`
		}{}
		if err := c.ShouldBind(&param); err != nil {
			c.JSON(http.StatusOK, gin.H{
				"status": 400,
				"msg":    "param error",
			})
			return
		}
		log.Println(param)
		sql := "select NumByID,things from todo_list_dbs where ID = " + "'" + param.UserID + "'"
		log.Println(sql)
		rs, err := db.Query(sql)
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"status": 400,
				"msg":    "query error 1",
			})
			return
		}
		var result []interface{}
		temp := struct {
			Num    int
			Things string
		}{}
		for rs.Next() {
			if err = rs.Scan(&temp.Num, &temp.Things); err != nil {
				c.JSON(http.StatusOK, gin.H{
					"status": 400,
					"msg":    "query error 2",
				})
				return
			}
			log.Println(temp)
			result = append(result, temp)
			log.Println(result...)
		}
		log.Println(result...)
		c.JSON(http.StatusOK, gin.H{"result": result})
	})
    //SetUserInfo api,用来新增代办事项
	r.POST("/api/v1/SetUserInfo", func(c *gin.Context) {
		param := struct {
			ID     string `json:"ID" binding:"required"`
			Things string `json:"Things" binding:"required"`
		}{}
		log.Println(param)
		if err := c.ShouldBind(&param); err != nil {
			c.JSON(http.StatusOK, gin.H{
				"status": 400,
				"msg":    "param error",
			})
			return
		}
		stmt, err := db.Prepare("insert into todo_list_dbs set ID =?,things=?")
		if err != nil {
			log.Println(err)
		}
		_, err = stmt.Exec(param.ID, param.Things)
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"status": 400,
				"msg":    "query error 1",
			})
			return
		}
	})
	
	log.Fatal(r.Run(":8080"))
}

总结与展望

小程序虽然已经开发完毕,但在功能上还稍显单薄,像待办提醒,附件上传,日程安排等功能,虽然一开始是放在了设计中的,但在最后因为种种原因只能遗憾的取消,此外,像页面的交互逻辑上还有待优化。在开发过程中,我们也进一步认识到了前期的项目文档,中期的及时沟通,后期的反思整理的重要性。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×