网址整理
视频教程地址:
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数据库,配置环境,启动项目
先引入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 )
根据自己的项目实际需求来做,可以是当前时间以前,也可以是当前日期以前
$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
推荐阅读
- 面试|前端面试题解答(九)
- 操作题|从零开始部署前端项目到阿里云windows服务器
- vue|vue项目引入vue-i18n,实现中英文切换
- 前端|JS不跨域操控网易云音乐外链播放器
- 点餐小程序APP开发
- 小程序安卓渲染问题
- 微信公众号关联小程序AppID是什么
- 微信小程序 app注册小程序+page注册页面代码一
- 前端|我的前端之路