fastadmin|fastadmin-微信小程序实战课程(todolist项目文档(课件)整理汇总)

网址整理 视频教程地址:
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
fastadmin|fastadmin-微信小程序实战课程(todolist项目文档(课件)整理汇总)
文章图片

第四节 编写增删改查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)]
收集箱 已过期4 复选框 标签 10-18 复选框 10-18

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.detail this.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() + 1 const 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.data for (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'],

{{itm}}{{datalist[ind].length}} {{item.task}} {{item.tags_number}} {{item.todotime}}

优化add方法:新增完数据,重新渲染数据
const { data } = res if(data.code === 1){ this.getbyday() }

修改
状态修改为已处理或待处理 修改的方法:
edit(id,data){ const param={...data,id} this.request('/xcxtodo/addEdit',param,(res)=>{ // console.log(res); const { data } = res if(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数据绑定
{{item.task}}

点击checkbox选项时切换状态
onCheckChange(event){ console.log(event); const {index,ind,id} = event.currentTarget.dataset const {datalist} = this.data datalist[ind][index].checked =event.detail this.edit(id,{ status:event.detail===true?2:1 }) this.setData({ datalist }); }

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

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

3.写对应的js方法
showEditPopup(event){ this.setData({ showPopup : true })const {index,ind} = event.currentTarget.dataset const {datalist} = this.data this.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 } = res if(data.code === 1){ this.getbyday() this.onClosePopup() } wx.showToast({ title: data.msg, }) }) }

【fastadmin|fastadmin-微信小程序实战课程(todolist项目文档(课件)整理汇总)】后续内容将在个人博客上更新:https://maoshu.fun/posts/45372c3c.html

    推荐阅读