网址整理

视频教程地址:

https://www.ixigua.com/7017702820048536071

后端接口仓库地址:

https://gitee.com/maoshushare/fast-todolist

第一节 项目计划和准备工作

项目简介

本项目是一个fastadmin+微信小程序的实战案例,一个简单的todolist任务管理功能,包括后台数据增删改查、数据统计、api接口开发(小程序对接登录、小程序端管理数据)等。

功能比较简单,覆盖到的知识点不会太多,适合初学者,尤其适合没有做过小程序和fa结合使用的同学。

为便于大家利用碎片时间学习,每节课的时间会尽量控制在10分钟左右。

效果截图

本项目是模仿滴答清单的部分功能,大概界面是这样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6vrVqSwq-1641880230864)(https://cdn.jsdelivr.net/gh/978165754/picgo@master/blog/20211011104255.png)]

项目计划

本项目制作思路:

  • 1.先制作基础的后端api部分。
  • 2.再制作小程序界面。
  • 3.小程序对接api,打通基本功能。
  • 4.缺少的api一边补充一边对接。

准备工作

  • 1.准备工具:VsCode(或phpStorm)、phpStudy、chrome浏览器(或Edge)、微信小程序开发工具、apipost
  • 2.下载fastadmin框架代码(https://gitee.com/maoshushare/fastmodel)
  • 3.导入mysql数据库,配置环境,启动项目

第二节 登录和自动注册api

验证字段必填

先引入Validate,use think\Validate;

$rule = ['openid'  => 'require|length:10,30'
];$msg = ['openid.require' => '缺少openid','openid.length' => 'openid长度不符合',
];$v = new Validate($rule,$msg);

登录和注册

如果已经存在,则登录,不存在则先注册,然后再自动登录

api相关的拓展教程:https://www.bilibili.com/video/BV1Ji4y1V7ZV?p=11

$post=$this->request->param();
if (!$v->check($post)) {$this->error('登录失败:' . $v->getError());
}
$u = model('admin/User')->where('openid',$post["openid"])->find();
if($u){Token::clear($u["id"]);$this->auth->direct($u["id"]);$this->success('登录成功', $this->auth->getUserinfo());
}
else{$username = $post["openid"];// 初始密码给一个随机数$password = Random::alnum(15);$this->auth->register($username,$password,'','',["openid"=>$post["openid"]]);$this->success('注册成功', $this->auth->getUserinfo());
}

第三节 创建数据表 生成crud

创建数据表:todo

字段:
userid task status tags prio desc
todotime
donetime
giveuptime
createtime updatetime deletetime

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LNJbJLnN-1641880230868)(https://cdn.jsdelivr.net/gh/978165754/picgo@master/blog/20211012141903.png)]

一键生成CRUD

第四节 编写增删改查api

新建一个控制器Xcxtodo,专门写关于任务控制的api

增加 和 修改

增加和修改写在一起,方便维护

增加

$this->model
->allowField(true)
->save(["userid"=>$this->uid,"tags"=>"测试","task"=>"测试3331232233",
]);

修改

$this->model
->allowField(["task"
])
->save(["task"=>"89898",
],["id"=>20]);

删除

根据任务id删除(软删除)

关于软删除的文档说明:https://www.kancloud.cn/manual/thinkphp5/189658

$this->model->get($id)->delete();$this->model->destroy($id);

查询

  • 根据关键词查询任务内容(模糊查询)

    $task = input("task");
    $where=["userid"=>$this->uid,
    ];
    if($task){$where["task"]=["like","%$task%"];
    }
    $list = $this->model->where($where)->select();
    

    处理标签id为标签文字

    $tags = model('admin/Todotags')->where(["userid"=>$this->uid,])->column('tags','id');foreach ($list as $key => &$value) {$tagsArr = explode(',',$value["tags"]);$tagsNameArr = [];foreach ($tagsArr as $id) {$tagsNameArr[] = $tags[$id] ?? $id;}$value["tagsName"] = implode(',',$tagsNameArr);
    }
    
  • 根据关键词查询标签

    $tags = input("tags");
    $where=["userid"=>$this->uid,
    ];
    if($tags){$where["tags"]=["like","%$tags%"];
    }
    $list = $this->model->where($where)->group("tags")->column("tags");
    

    改为专表之后:

    $tags = input("tags");
    $where=["userid"=>$this->uid,
    ];
    if($tags){$where["tags"]=["like","%$tags%"];
    }
    $list = model('admin/Todotags')->where($where)->select();
    
  • 根据标签查询任务

    $tags = input("tags");
    $where=["userid"=>$this->uid,
    ];
    if($tags){$where["tags"]=$tags;
    }
    $list = $this->model->where($where)->select();
    

    改为专表之后:
    注意exp的写法(https://www.kancloud.cn/manual/thinkphp5/135182)

      $tags = input("tags");$where=["userid"=>$this->uid,];$list = $this->model->where($where);if($tags){$list=$list->where('','exp', "instr(CONCAT( ',', tags, ',' ),  ',".$tags.",' )");}$list = $list->select();
    

    拓展知识instr:

    INSTR(str,substr)
    str:从哪个字符串中搜索
    substr:要搜索的子字符串
    instr()函数不区分大小写

  • 按日期分组:
    核心:时间查询( https://www.kancloud.cn/manual/thinkphp5/165789 )

已过期的 (status=1 && todotime 在当前时间之前)
根据自己的项目实际需求来做,可以是当前时间以前,也可以是当前日期以前

$where=["userid"=>$this->uid,"status"=>1,];
$list =$this->model->where($where)->whereTime('todotime','<',time())->select();

今天的

$list =$this->model->where($where)->whereTime('todotime','d')->select();

明天的

$day = date('Y-m-d',strtotime("+1 day"));
$list =$this->model->where($where)->whereTime('todotime', 'between', [$day.' 00:00:00', $day.' 23:59:59'])->select();

后天到7日内

$day1 = date('Y-m-d',strtotime("+2 day"));
$day2 = date('Y-m-d',strtotime("+7 day"));
$list =$this->model->where($where)->whereTime('todotime', 'between', [$day1.' 00:00:00', $day2.' 23:59:59'])->select();

更久以后

$day2 = date('Y-m-d',strtotime("+7 day"));
$list =$this->model->where($where)->whereTime('todotime', '>', $day2.' 23:59:59')->select();

无计划日期的 (status=1 && todotime为空)

$where=["userid"=>$this->uid,"status"=>1,];
$list =$this->model->where($where)->whereNull('todotime')->select();

已完成的 (status=2)

$where=["userid"=>$this->uid,"status"=>2,];
$list =$this->model->where($where)->select();

已放弃的 (status=3)

$where=["userid"=>$this->uid,"status"=>3,];
$list =$this->model->where($where)->select();

安全优化

修改和删除限制只能操作当前用户自己的数据

修改数据

着重分析下update和save的区别(https://www.kancloud.cn/manual/thinkphp5/135189)

update方法:allowField无效,不会更新时间

$res = $this->model// ->allowField(true) //无效->where(["id"=>$id,"userid"=> $this->uid])->update($data);

save方法:isUpdate(true)

if($info = $this->model->get(['id'=>$id,'userid'=>$this->uid])){$res = $info->allowField(true)->isUpdate(true)->save($data);
}
else{$this->error($type."失败,未找到数据");
}

删除数据

$type = "删除".$id."数据";
if($info = $this->model->get(['id'=>$id,'userid'=>$this->uid])){        $res = $info->delete();
}
else{$this->error($type."失败");
}if($res){$this->success($type."成功");
}
else{$this->error($type."失败");
}

第五节 小程序

小程序准备工作

  • 1.新建项目
  • 2.引入UI库(vant)
    https://youzan.github.io/vant-weapp/#/quickstart
  • 3.调试ui库看是否正常引入

小程序界面

先简单搭建下界面,不需要太完善

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C9MwxdUK-1641880230872)(https://cdn.jsdelivr.net/gh/978165754/picgo@master/blog/20211017152953.png)]


<van-cell icon="todo-list-o"  border="{{ false }}"><view slot="title"><view class="van-cell-text">收集箱</view><!-- <van-tag type="danger">标签</van-tag> --></view><van-icon  slot="right-icon" class="mr-10" name="clock-o"/><van-icon slot="right-icon"  name="ellipsis" />
</van-cell><van-cell-group  border="{{ false }}"><van-fieldvalue="{{ sms }}"centerclearablelabel=""placeholder="+ 添加任务到收集箱,回车即可保存"border="{{ false }}"use-button-slotclass="ccc"><view slot="right-icon"><van-icon name="clock-o" class="mr-10"/><van-icon name="ellipsis" /></view></van-field>
</van-cell-group><van-collapse value="{{ activeNames }}" bind:change="onChange"  border="{{ false }}"><van-collapse-item name="1"  border="{{ false }}"><view slot="title">已过期<text class="smalltext">4</text></view><!-- 1 --><van-cell border="{{ true }}"><view slot="title"><van-checkbox value="{{ checked }}" bind:change="onChange">复选框</van-checkbox></view><view slot="right-icon"><van-tag round type="success">标签</van-tag><text class="mr-10 ml-10">10-18</text><van-icon name="ellipsis" /></view></van-cell><!-- 2 --><van-cell border="{{ true }}"><view slot="title"><van-checkbox value="{{ true }}" bind:change="onChange">复选框</van-checkbox></view><view slot="right-icon" style="font-size: 28rpx;">10-18       </view><van-icon slot="right-icon"  name="ellipsis" /></van-cell></van-collapse-item>
</van-collapse>

app.json引入组件

    "van-button": "@vant/weapp/button/index","van-field": "@vant/weapp/field/index","van-cell": "@vant/weapp/cell/index","van-cell-group": "@vant/weapp/cell-group/index","van-tag": "@vant/weapp/tag/index","van-icon": "@vant/weapp/icon/index","van-collapse": "@vant/weapp/collapse/index","van-collapse-item": "@vant/weapp/collapse-item/index","van-checkbox": "@vant/weapp/checkbox/index","van-checkbox-group": "@vant/weapp/checkbox-group/index"

第六节 小程序对接api

小程序开发文档:https://developers.weixin.qq.com/miniprogram/dev/framework/
申请测试号:https://developers.weixin.qq.com/miniprogram/dev/devtools/sandbox.html

登录

1.登录授权,获取openid
wx.login ( https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html )
获取用户信息

let u = wx.getStorageSync('userInfo')if(u){console.log(1,u);this.setData({userInfo: u,hasUserInfo:true})this.login(u)}if (wx.getUserProfile) {this.setData({canIUseGetUserProfile: true})}
  getUserProfile(e) {// 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认// 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗wx.getUserProfile({desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写success: (res) => {console.log("用户信息",res);wx.setStorageSync('userInfo', res.userInfo)this.login(res.userInfo)this.setData({userInfo: res.userInfo,hasUserInfo: true})}})},

2.根据code获取openid,并且对接登录接口

login(u){wx.login({success (res) {console.log(res);if (res.code) {//发起网络请求wx.request({url: 'http://todo.tt/api/xcxuser/getopenid',data: {code: res.code,nickName:u.nickName},success :(res)=>{console.log(res);},fail:(err)=>{console.log(err);}})} else {console.log('登录失败!' + res.errMsg)}}})}
})

接口:

public function getopenid($code){$url = "https://api.weixin.qq.com/sns/jscode2session?appid=$this->AppID&secret=$this->AppSecret&js_code=$code&grant_type=authorization_code";$res = Http::get($url);$this->success('成功',['r'=>$this->request->param(),"res"=>$res]);}

全局变量 token

globalData: {userInfo: null,token:null,apidomain:'http://todo.tt/api'
}

封装request方法

request(url,data,callback){wx.request({url: gb.apidomain + url,data: data,dataType: 'json',enableCache: true,enableHttp2: true,enableQuic: true,header: {token:gb.token},method: "POST",// responseType: 'json',success: (result) => {callback(result)},fail: (res) => {console.error(res)},complete: (res) => {},})
}

新增

bind:confirm=“confirm_add”

  confirm_add(event){// console.log(event.detail);let task = event.detailthis.add(task)},add(task){if(task != ''){this.request('/xcxtodo/addEdit',{task:task},(res)=>{console.log(res); wx.showToast({title: res.data.msg,})       })}},

查询和显示

引入util.js来格式化时间

var util = require('../../utils/util')
const formatTime = (time, option) => {const date = new Date(time)const year = date.getFullYear()const month = date.getMonth() + 1const day = date.getDate()const week = date.getDay()const hour = date.getHours()const minute = date.getMinutes()const second = date.getSeconds()//获取 年月日if (option == 'YY-MM-DD') return [year, month, day].map(formatNumber).join('-')//获取 月日if (option == 'MM-DD') return [month, day].map(formatNumber).join('-')//获取 年月if (option == 'YY-MM') return [year, month].map(formatNumber).join('-')//获取 年if (option == 'YY') return [year].map(formatNumber).toString()//获取 月if (option == 'MM') return  [mont].map(formatNumber).toString()//获取 日if (option == 'DD') return [day].map(formatNumber).toString()//获取 年月日 周一 至 周日if (option == 'YY-MM-DD Week')  return [year, month, day].map(formatNumber).join('-') + ' ' + getWeek(week)//获取 月日 周一 至 周日if (option == 'MM-DD Week')  return [month, day].map(formatNumber).join('-') + ' ' + getWeek(week)//获取 周一 至 周日if (option == 'Week')  return getWeek(week)//获取 时分秒if (option == 'hh-mm-ss') return [hour, minute, second].map(formatNumber).join(':')//获取 时分if (option == 'hh-mm') return [hour, minute].map(formatNumber).join(':')//获取 分秒if (option == 'mm-dd') return [minute, second].map(formatNumber).join(':')//获取 时if (option == 'hh')  return [hour].map(formatNumber).toString()//获取 分if (option == 'mm')  return [minute].map(formatNumber).toString()//获取 秒if (option == 'ss') return [second].map(formatNumber).toString()//默认 时分秒 年月日return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}const formatNumber = n => {n = n.toString()return n[1] ? n : '0' + n
}const getWeek = n => {switch(n) {case 1:return '星期一'case 2:return '星期二'case 3:return '星期三'case 4:return '星期四'case 5:return '星期五'case 6:return '星期六'case 7:return '星期日'}
}module.exports = {formatTime: formatTime
}

获取数据,并格式化处理

getbyday(opt = {}){this.request('/xcxtodo/getbyday',opt,(res)=>{let datalist = res.data.datafor (const key in datalist) {if (Object.hasOwnProperty.call(datalist, key)) {const element = datalist[key];element.forEach((ele,ind) => {              if(ele.tags){  datalist[key][ind].tagsnum = ele.tags.split(',').length}if(ele.todotime){  datalist[key][ind].todotime = util.formatTime(ele.todotime*1000,'MM-DD')}});}}this.setData({datalist})})},

渲染数据

  groups:{timeout:"已过期",today:"今天",tomorrow:"明天",seven:"7天",langtime:"更久以后",notime:"无日期",timeend:"已完成",},activeNames: ['timeout','today'],
  <block wx:for="{{groups}}" wx:if="{{datalist[ind].length>0}}" wx:key="ind" wx:for-item="itm" wx:for-index="ind"><van-collapse-item name="{{ind}}"><view slot="title">{{itm}}<text class="smalltext">{{datalist[ind].length}}</text></view><!-- 任务1 --><block wx:for="{{datalist[ind]}}" wx:key="id"><van-cell><view slot="title"><van-checkbox value="{{ checked }}" bind:change="onChange">{{item.task}}</van-checkbox></view><view slot="right-icon"><van-tag round type="success" wx:if="{{item.tags_number}}">{{item.tags_number}}</van-tag><text class="mr-10 ml-10" wx:if="{{item.todotime}}">{{item.todotime}}</text><van-icon name="ellipsis" /></view></van-cell></block></van-collapse-item></block>

优化add方法:新增完数据,重新渲染数据

  const { data } = resif(data.code === 1){this.getbyday()}

修改

状态修改为已处理或待处理

修改的方法:

edit(id,data){const param={...data,id}this.request('/xcxtodo/addEdit',param,(res)=>{// console.log(res); const { data } = resif(data.code === 1){this.getbyday()}wx.showToast({title: data.msg,})       })},

getbyday方法优化(格式化完成时间和选中状态)

if(ele.donetime){datalist[key][k]["donetime"] = util.formatTime(ele.donetime*1000,'MM-DD')datalist[key][k]["checked"] = true
}

增加选中状态,增加data数据绑定

<van-checkbox value="{{ item.checked }}" data-id="{{item.id}}" data-ind="{{ind}}" data-index="{{index}}" bind:change="onCheckChange">{{item.task}}</van-checkbox>

点击checkbox选项时切换状态

onCheckChange(event){console.log(event);const {index,ind,id} = event.currentTarget.datasetconst {datalist} = this.datadatalist[ind][index].checked =  event.detailthis.edit(id,{status:event.detail===true?2:1})this.setData({datalist});
}

删除

1.首先加一个点击右侧按钮弹出编辑项

<van-icon name="ellipsis" bind:click="showEditPopup" data-ind="{{ind}}"  data-index="{{index}}" />

2.弹出层放在右侧,里面放几个选项

<van-popup show="{{ showPopup }}" position="right" custom-style="height: 100%" bind:close="onClosePopup"><van-cell title="删除" icon="delete-o" bind:click="delete"/><van-cell title="标签" icon="location-o" /><van-cell title="改日期" icon="clock-o" />
</van-popup>

3.写对应的js方法

showEditPopup(event){this.setData({showPopup : true})const {index,ind} = event.currentTarget.datasetconst {datalist} = this.datathis.setData({editItem:datalist[ind][index]})},onClosePopup(){this.setData({showPopup : false,editItem:null})
},
delete(){console.log('编辑的是',this.data.editItem);const {id} = this.data.editItemthis.request('/xcxtodo/delete',{id},(res)=>{// console.log(res); const { data } = resif(data.code === 1){this.getbyday()this.onClosePopup()}wx.showToast({title: data.msg,})       })
}

后续内容将在个人博客上更新:https://maoshu.fun/posts/45372c3c.html

fastadmin-微信小程序实战课程:todolist项目文档(课件)整理汇总相关推荐

  1. 微信小程序开发指南,接口文档,工具下载使用

    相信前天,从事互联网行业人的朋友圈给刷爆了,前微信公众平台开始陆续对外发送小程序内测邀请,而小程序即被外界广为关注的微信"应用号". 小程序的推出也并非一蹴而就,早在2016年1月 ...

  2. 微信小程序实现PDF转Word文档

    效果查看 1.打开微信小程序项目,并创建toword模块 1.两个主要文件,toword.wxml与toword.js.toword.wxml文件内容 <view class="vie ...

  3. 微信小程序学习实录1(wxml文档、引入weui、双向数据绑定、提交表单到后端)

    微信小程序学习实录 一.wxml文档 二.新建页面快捷方式 三.微信小程序引入weui 四.双向数据绑定 1.wxml渲染层 2.js逻辑层 提交表单到后端 五.微信小程序跳转到H5 一.wxml文档 ...

  4. axure 小程序 网盘_万门大学微信小程序实战开发特训班【完结】网盘高清全套最新系列精品课程...

    万门大学微信小程序实战开发特训班[完结]网盘高清全套最新系列精品课程 课 程 简介 我买了这个课程,课程很有价值,我们通过链接或百度网盘群的形式在共享资料库中与您共享,需要万门大学微信小程序实战开发特 ...

  5. 微信小程序实战教程-闫涛-专题视频课程

    微信小程序实战教程-38472人已学习 课程介绍         介绍微信小程序API,包括页面布局.网络交互.媒体文件.本地缓存.地理位置.WebSocket和传感器技术,后以翼健康为模板,开发一套 ...

  6. 微信小程序之网易云项目实战(一)

    微信小程序之网易云项目实战(一) 1.全局app.json 配置 {"pages": ["pages/index/index","pages/logs ...

  7. 前端《Vue.js从入门到项目实战》PDF课件+《微信小程序实战入门第2版》PDF代码调试

    JS进行开发,正如一切的编程语言都立足于电元信号的正负极,即01码,可为什么软件都不采用二进制编码来 进行开发呢?这里面牵扯到一个成本的问题,这正是影响项目领导者进行决策的关键因素.Vue项目与原生J ...

  8. 知乎爆赞!4504页《微信小程序零基础入门开发文档》+《小程序实战笔记》,你学废了吗?

    前言 微信小程序作为近几年"微服务"的杰出代表,应用十分广泛.小程序是一种新的开放能力,开发者可以快速地开发一个小程序.小程序可以在微信内被便捷地获取和传播,同时具有出色的使用体验 ...

  9. 小程序swiper怎么让内容撑开高度_[视频]微信小程序实战优购商城,涵盖你所学的技能点...

    很多友友都在找视频教程学习,IT技术教程分享网[http://www.mano100.cn]已经为你收集了各种各样的视频教程,不用再到处找视频教程学习了.无论是免费的,还是收费的,都在这里了.只要你注 ...

  10. 微信小程序实战篇-下拉刷新与加载更多

    下拉刷新 实现下拉刷新目前能想到的有两种方式 调用系统的API,系统有提供下拉刷新的API接口 下拉刷新API.png 监听scroll-view,自定义下拉刷新,还记得scroll-view里面有一 ...

最新文章

  1. 2022-2028年中国钢材市场投资分析及前景预测报告(全卷)
  2. RTD 比率式温度测量传感器设计思路
  3. CVE-2021-29454——Smarty模板注入
  4. 学院派CAD工具箱及CAD调用外部应用程序的方法
  5. 在神经网络中使用辍学:不是一个神奇的子弹
  6. 两列布局、三列适应布局、两列等高适应布局。
  7. 在windows下安装webpy
  8. SpringCloud Alibaba - Nacos 作为配置中心 读取Properties配置信息
  9. 一.hadoop入门须知
  10. html实现点击切换页面,JavaScript实现的简单Tab点击切换功能示例
  11. 使用tomcat7-maven-plugin部署Web项目
  12. C#调用DLL函数方法
  13. 数据恢复关键技术与实战指南
  14. Web2.0是什么:下一代软件的业务模式与设计模式
  15. Acunetix 网站漏洞扫描软件 检测您网站的安全性。
  16. linux yum安装scp,CentOS安装scp命令的软件包openssh-clients
  17. 天嵌i.mx6q--1
  18. jupyter添加标题、文字注释;
  19. 火爆的人工智能项目都在这里了|Gitee项目推荐
  20. arduino声音传感器与二极管_Arduino--声音传感器(九)

热门文章

  1. 手把手带你撸一个校园APP(四):APP功能设计及主页面框架
  2. 985 材料专业研究生年薪 20 多万也不少了吧,为什么还有那么多人劝退,转行能挣多少钱?
  3. matlab二重定积分_matlab 对于变限积分的计算,二重积分 三重积分
  4. Oracle EXPLAIN PLAN用法
  5. ios uri正则表达式_众果搜的博客
  6. adb命令模拟按键事件 KeyCode
  7. 额,我要说一件重要的事+用C++编写一个走迷宫小游戏(1.1版)
  8. 苹果开发者账号注册申请流程(2017.6.27)
  9. photoshop笔记
  10. JavaScript模板引擎-artTemplate