- 阅读 9小时搞定微信小程序开发 源码总结(小书架)。
目录与页面模块 读代码首先应该认真阅读文档的[代码]README.md[代码]。 看下小书架页面模块、目录结构清晰,虽然模块不多,实际业务开发中首先应该构思拆解业务模块,确定目录结构。小程序包大小超过 2M 需要分包,所以从一开始确定目录结构的时候就要考虑进去,不然线上跑起来再去分包会有点小麻烦。 目录结构 [代码]├── config │ └── config.js ├── images ├── pages │ ├── books │ │ ├── books.js │ │ ├── books.json │ │ ├── books.wxml │ │ └── books.wxss │ ├── comment │ │ ├── comment.js │ │ ├── comment.js │ │ ├── comment.js │ │ └── comment.wxss │ ├── detail │ │ ├── detail.js │ │ ├── detail.js │ │ ├── detail.js │ │ └── detail.wxss │ ├── my │ │ ├── my.js │ │ ├── my.js │ │ ├── my.js │ │ └── my.wxss │ └── myBooks │ ├── myBooks.js │ ├── myBooks.js │ ├── myBooks.js │ └── myBooks.wxss ├── utils │ └── util.js ├── app.js ├── app.json ├── app.wxss └── project.config.json [代码] 各页面模块 页面 描述 books 首页/书籍列表页 comment 评论页面 detail 书籍详情页 my 个人中心页 myBooks 已购书籍页 接口封装 小书架没有对 wx.request 封装。考虑到是入门教程,没做处理也是正常。接口请求路径封装在 config.js [代码]// 服务器域名 const baseUrl = 'http://127.0.0.1:[your port]/'; // 获取书籍信息接口地址(可选择全部或单个书籍) const getBooksUrl = baseUrl + 'api/book/getBooks'; //... module.exports = { getBooksUrl: getBooksUrl //... }; [代码] 也可以根据个人习惯进行封装,这里一定要写注释以及考虑后期维护 [代码]let returnCancel = (memberId, refundId) => http.post(`api/return/goods/cancel`, { memberId: memberId, refundId: refundId}) export default { returnCancel }; [代码] 页面 book book.js 代码干净、整洁。注释很详细,虽然这是入门教程,但我们开发的时候也要养成这样的好习惯。 data [代码]data: { bookList: [], // 书籍列表数组 indicatorDots: false, // 是否显示轮播指示点 autoplay: false, // 是否自动播放轮播 sideMargin: '100rpx', // 幻灯片前后边距 showLoading: true // 是否显示loading态 //... }, [代码] onLoad getBookList 方法获取所有书籍列表,不要把所有的 wx.request 都写在load里面,简单封装下可维护性大大提高 [代码]/** * 获取所有书籍列表 */ getBookList: function() { let that = this; wx.request({ url: api.getBooksUrl, data: { is_all: 1 }, success: function(res) { let data = res.data; // console.log(data); if (data.result === 0) { setTimeout(function() { that.setData({ bookList: data.data, showLoading: false }); }, 800); } }, error: function(err) { console.log(err); } }); }, onLoad: function(options) { let that = this; that.getBookList(); }, [代码] loading处理 [代码]<block wx:if="{{showLoading}}"> <view class="donut-container"> <view class="donut"></view> </view> </block> // 默认 true 。getBookList 方法成功回调里设为 false。没有错误处,接口请求失败的话应该也做下处理的 [代码] comment comment.js 封装了检查用户输入的方法,实际业务中如果输入较多的话,可以提炼到 unit.js。封装了 wx.showToast [代码]// 检查输入是否为空,起名称注意语义话 checkEmpty: function(input) { return input === ''; }, /** * 检查用户是否输入了非法字符 */ checkIllegal: function(input) { let patern = /[`#^<>:"?{}\/;'[\]]/im; let _result = patern.test(input); return _result; }, /** * 检查用户输入 */ checkUserInput: function() { /* * 检测用户输入 * 1. 是否包含非法字符 * 2. 是否为空 * 3. 是否超出长度限制 */ let that = this; let comment = that.data.comment; let showToastFlag = false; let toastWording = ''; if (that.checkEmpty(comment)) { showToastFlag = true; toastWording = '输入不能为空'; } else if (that.checkIllegal(comment)) { showToastFlag = true; toastWording = '含有非法字符'; } else if (comment.length > 140) { showToastFlag = true; toastWording = '长度超出限制'; } if (showToastFlag) { that.showInfo(toastWording); return false; } else { return true; } }, [代码] 封装toast [代码]showInfo: function(info, icon = 'none', callback = () => {}) { wx.showToast({ title: info, icon: icon, duration: 1500, mask: true, success: callback }); }, [代码] detail 简单的返回刷新处理以及下载进度条 [代码]// 从上级页面返回时 重新拉去评论列表 backRefreshPage: function() { let that = this; that.setData({ commentLoading: true }); that.getPageData(); }, /** * 生命周期函数--监听页面显示 */ onShow: function() { if (wx.getStorageSync('isFromBack')) { wx.removeStorageSync('isFromBack') this.backRefreshPage(); } } [代码] 进度条(这个还是很少见的需求,很可爱) [代码]<!-- 下载进度条 --> <view class="loading-container" wx:if="{{downloading}}"> <progress percent="{{downloadPercent}}" stroke-width="6" activeColor="#1aad19" backgroundColor="#cdcdcd" show-info /> </view> [代码] my 主要是检查登陆。myBooks没有什么亮眼的操作,就不上场了。 [代码]data: { userInfo: {}, // 用户信息 hasLogin: wx.getStorageSync('loginFlag') ? true : false // 是否登录,根据后台返回的skey判断 }, [代码] app.js 主要负责检查处理登陆信息 [代码]App({ // 小程序启动生命周期 onLaunch: function () { let that = this; // 检查登录状态 that.checkLoginStatus(); }, // 检查本地 storage 中是否有登录态标识 checkLoginStatus: function () { let that = this; let loginFlag = wx.getStorageSync('loginFlag'); if (loginFlag) { // 检查 session_key 是否过期 wx.checkSession({ // session_key 有效(为过期) success: function () { // 直接从Storage中获取用户信息 let userStorageInfo = wx.getStorageSync('userInfo'); if (userStorageInfo) { that.globalData.userInfo = JSON.parse(userStorageInfo); } else { that.showInfo('缓存信息缺失'); console.error('登录成功后将用户信息存在Storage的userStorageInfo字段中,该字段丢失'); } }, // session_key 过期 fail: function () { // session_key过期 that.doLogin(); } }); } else { // 无登录态 that.doLogin(); } }, // 登录动作 doLogin: function (callback = () => {}) { let that = this; wx.login({ success: function (loginRes) { if (loginRes.code) { /* * @desc: 获取用户信息 期望数据如下 * * @param: userInfo [Object] * @param: rawData [String] * @param: signature [String] * @param: encryptedData [String] * @param: iv [String] **/ wx.getUserInfo({ withCredentials: true, // 非必填, 默认为true success: function (infoRes) { console.log(infoRes,'>>>') // 请求服务端的登录接口 wx.request({ url: api.loginUrl, data: { code: loginRes.code, // 临时登录凭证 rawData: infoRes.rawData, // 用户非敏感信息 signature: infoRes.signature, // 签名 encryptedData: infoRes.encryptedData, // 用户敏感信息 iv: infoRes.iv // 解密算法的向量 }, success: function (res) { console.log('login success'); res = res.data; if (res.result == 0) { that.globalData.userInfo = res.userInfo; wx.setStorageSync('userInfo', JSON.stringify(res.userInfo)); wx.setStorageSync('loginFlag', res.skey); callback(); } else { that.showInfo(res.errmsg); } }, fail: function (error) { // 调用服务端登录接口失败 that.showInfo('调用接口失败'); console.log(error); } }); }, fail: function (error) { // 获取 userInfo 失败,去检查是否未开启权限 wx.hideLoading(); that.checkUserInfoPermission(); } }); } else { // 获取 code 失败 that.showInfo('登录失败'); console.log('调用wx.login获取code失败'); } }, fail: function (error) { // 调用 wx.login 接口失败 that.showInfo('接口调用失败'); console.log(error); } }); }, // 检查用户信息授权设置 checkUserInfoPermission: function (callback = () => { }) { wx.getSetting({ success: function (res) { if (!res.authSetting['scope.userInfo']) { wx.openSetting({ success: function (authSetting) { console.log(authSetting) } }); } }, fail: function (error) { console.log(error); } }); }, // 获取用户登录标示 供全局调用 getLoginFlag: function () { return wx.getStorageSync('loginFlag'); }, // app全局数据 globalData: { userInfo: null } }); [代码] 中规中矩的小程序,入门还是可以的,代码简洁干净。新手的话撸一遍还是可以的。这样不知道算不算侵权,侵删。
2019-11-15 - 如何实现一个自定义数据版省市区二级、三级联动
社区可能有其他的方案了,但是再分享下吧,给有需要的童鞋。 效果图: [图片] 额,这个视频转GIF因为社区上传不了大图,所以剪了一部分,具体的效果还是直接工具打开代码片段预览吧~ 第一步:你的页面JSON引入该组件: [代码]{ "usingComponents": { "city-picker": "/components/cityPicker/index" } } [代码] 第二步:你的页面WXML引入该组件 [代码]<city-picker visible="{{visible}}" column="2" bind:close="handleClick" bind:confirm="handleConfirm" /> [代码] 第三步:你的页面JS调用 [代码]// 显示/隐藏picker选择器 handleClick() { this.setData( visible: !this.data.visible }) }, // 用户选择城市后 点击确定的返回值 handleConfirm(e) { const { detail: { provinceName = '', provinceId = '', cityName, cityId='', areaName = '', areaId = '' } = {} } = e this.setData({ cityId, cityName, areaId, areaName, provinceId, provinceName }) } [代码] 组件属性 属性 默认值 描述 visible false 是否显示picker选择器 column 3 显示几列,可选值:1,2,3 values [0, 0, 0] 必填,默认回填的省市区下标,可选择具体省市区后查看AppData的regionValue字段 close function 点击关闭picker弹窗 confirm function 点击选择器的确定返回值 confirm: 属性 默认值 描述 provinceName 北京市 省份名称 provinceId 110000 省份ID cityName 市辖区 城市名称 cityId 110100 城市ID areaName 东城区 区域名称 areaId 110000 区域Id 至于怎么获取你想默认城市的下标,可以滑动操作下选中省市区后,点击确定后查看appData里的regionValue的值。 以上就是一个自定义数据版本的省市区二级、三级联动啦,老规矩,结尾放代码片段。 https://developers.weixin.qq.com/s/F9k9cTmT7LAz
2022-07-20