【小程序】发布商品(上传图片)
效果
1、主要布局
我们需要基本的input框和选择器picker,我设计的布局如下
重点在分类选择器和时间选择器
1-1.分类选择器
使用官方文档的picker标签,传入的数据为一个对象数组,数组中每一个对象包含两个字段
picker 标签的
value属性:绑定的是一个数组下标index值;
range属性:绑定data里面的分类数组
range-key: 绑定data分类数组的key值,一般为分类名
用法如下
selectClassify 方法
1-2、时间选择器
选择picker mode属性为multiSelector,因为微信官方没有提供日期和时间同时选择器,只有日期和时间分开的选择器,因此我们要自己做一个日期时间选择器,因为这个选择器太复杂,这里只给出代码,里面都有详细注释了
首先在util文件下新建一个js文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | function withData(param) { return param < 10 ? '0' + param : '' + param; } //传入月头和月尾的天数,返回一个天数数组 function getLoopArray(start, end) { var start = start || 0; var end = end || 1; var array = []; for ( var i = start; i <= end; i++) { array.push(withData(i)); } return array; } //获取每个月份对应的天数 function getMonthDay(year, month) { var flag = year % 400 == 0 || (year % 4 == 0 && year % 100 != 0), array = null ; switch (month) { case '01' : case '03' : case '05' : case '07' : case '08' : case '10' : case '12' : array = getLoopArray(1, 31) break ; case '04' : case '06' : case '09' : case '11' : array = getLoopArray(1, 30) break ; case '02' : array = flag ? getLoopArray(1, 29) : getLoopArray(1, 28) break ; default : array = '月份格式不正确,请重新输入!' } return array; } //获取当前日期时间用来默认显示在滑动块上面 function getNewDateArry() { // 当前时间的处理 var newDate = new Date(); var year = withData(newDate.getFullYear()), mont = withData(newDate.getMonth() + 1), date = withData(newDate.getDate()), hour = withData(newDate.getHours()), minu = withData(newDate.getMinutes()), seco = withData(newDate.getSeconds()); return [year, mont, date, hour, minu, seco]; } function dateTimePicker(startYear, endYear, date) { // 返回默认显示的数组和联动数组的声明 var dateTime = [], dateTimeArray = [ [], [], [], [], [], [] ]; var start = startYear || 1978; var end = endYear || 2100; // 默认开始显示数据 var defaultDate = date ? [...date.split( ' ' )[0].split( '-' ), ...date.split( ' ' )[1].split( ':' )] : getNewDateArry(); // 处理联动列表数据 /*年月日 时分秒*/ dateTimeArray[0] = getLoopArray(start, end); dateTimeArray[1] = getLoopArray(1, 12); dateTimeArray[2] = getMonthDay(defaultDate[0], defaultDate[1]); dateTimeArray[3] = getLoopArray(0, 23); dateTimeArray[4] = getLoopArray(0, 59); dateTimeArray[5] = getLoopArray(0, 59); dateTimeArray.forEach((current, index) => { dateTime.push(current.indexOf(defaultDate[index])); }); return { dateTimeArray: dateTimeArray, dateTime: dateTime } } //导出函数 module.exports = { dateTimePicker: dateTimePicker, getMonthDay: getMonthDay } |
在页面js文件的onLoad函数里面初始化data
记得先引入dateTimePicker
选择时间后的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | //处理选择的时间 changeDateTime(e) { let dateTimeArray = this .data.dateTimeArray, { type, param } = e.currentTarget.dataset; this .setData({ [type]: e.detail.value, [param]: dateTimeArray[0][e.detail.value[0]] + '-' + dateTimeArray[1][e.detail.value[1]] + '-' + dateTimeArray[2][e.detail.value[2]] + ' ' + dateTimeArray[3][e.detail.value[3]] + ':' + dateTimeArray[4][e.detail.value[4]] + ':' + dateTimeArray[5][e.detail.value[5]] }) }, //滑动时间触发 changeDateTimeColumn(e) { var dateArr = this .data.dateTimeArray, { type } = e.currentTarget.dataset, arr = this .data[type]; arr[e.detail.column] = e.detail.value; dateArr[2] = dateTimePicker.getMonthDay(dateArr[0][arr[0]], dateArr[1][arr[1]]); this .setData({ dateTimeArray: dateArr, [type]: arr }) } |
在wxml页面引入使用时间选择器
1-3、选择器效果
2、上传图片到云存储
2-1、wx.cloud.upLoadFile 接口
遇到点小bug ,用异步操作的时候,报错提示说异步函数参数有问题,网上页找不到类似的错误,只能自己排查了。
我的异步函数是一个递归函数,看着感觉像是return Promise 的问题,因为每一次递归都有一个return ,不符合逻辑。
改进错误:将递归函数整体放进Promise里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | //上传图片到云存储,异步函数,防止图片还没上传,就执行插入云数据库 uploadImages() { let _this = this return new Promise( function (resolve,reject){ function upload(index){ wx.showLoading({ title: '上传第' + index + '张图片' }) console.log(_this.data.selectImgs) wx.cloud.uploadFile({ cloudPath: 'goodsImgs/' + new Date().getTime() + '_' + Math.floor(Math.random() * 1000) + '.jpg' , //给图片命名 filePath: _this.data.selectImgs[index], //本地图片路径 success: (res) => { console.log( '上传成功' , res.fileID) _this.data.uploadImgs[index] = res.fileID wx.hideLoading({ success: (res) => {}, }) //判断是否全部上传 if (_this.data.selectImgs.length - 1 <= index) { console.log( '已全部上传' ) resolve( 'success' ) return } else { upload(index + 1) } }, fail: (err) => { reject( 'error' ) wx.showToast({ title: '上传失败,请重新上传' , type: 'none' }) } }) } upload(0) }) }, |
3、保存商品数据到云数据库
3-1、保存数据前的数据校验
我们要校验起拍价是否输入正确,起拍价规定只能是正数,并且第一位不能为0,小数点后面只能输入2位
还要校验商品描述,因为我是已拍卖形式交易,价高者才可以获得发布者的联系方式,所以不允许用户在发布商品时暴露联系方式
3-2、提交数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | //提交表单,保存商品到云数据库 submit(e) { let dateEndTime = Date.parse( this .data.end_time_p) / 1000; //转时间戳,精确到毫秒 let goodsName = e.detail.value.name //商品名 let startPrice = e.detail.value.start_price //起拍价 let describe = e.detail.value.describe //商品描述 let publisherId = wx.getStorageSync( 'userInfo' )._id //获取发布者id let startTime = Math.floor( new Date().getTime() / 1000) //起拍时间默认为当前时间 let classId = this .data.classifys[ this .data.objectIndex] // 当前选择的分类 //先上传图片再添加到云数据库 //点击提交的时候再次校验输入是否有误 if (! this .data.isNum || ! this .data.checkDescribe) { wx.showToast({ title: '起拍价或描述输入不符,请重新输入' , icon: 'none' }) } else { this .uploadImages().then((resolve, reject) => { let imagesUrl = this .data.uploadImgs //云存储的图片列表 wx.showLoading({ title: '发布中' }) setTimeout(() => {}, 500) wx.cloud.database().collection( 'goods' ).add({ data: { name: goodsName, start_price: startPrice, describe: describe, current_price: startPrice, images: imagesUrl, publisher_id: publisherId, end_time: dateEndTime, clicks: 0, class_id: classId, start_time: startTime, auctioning: true }, success: (res) => { console.log( '添加商品' ) wx.hideLoading({ success: (res) => { wx.navigateBack({ delta: 1, }) }, }) } }) }) } }, |
4、完整页面和js代码
4-1、wxml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | < view class="container"> < scroll-view class="main" scroll-y="true"> < form bindsubmit="submit" bindreset="reset"> < view class="addGoods"> < view > < label >标题:</ label > < input type="text" name="name" /> </ view > < view > < label >起拍价:</ label > < input type="number" name="start_price" bindblur="checkPrice"/> </ view > < view > < view > < picker bindchange="selectClassify" value="{{objectIndex}}" range="{{classifys}}" range-key="className"> < view class="picker" style="display: flex;"> 选择分类:{{classifys[objectIndex].className}} < image src="/image/select.png" style="width: 50rpx;height: 50rpx;"></ image > </ view > </ picker > </ view > </ view > < view > < label >描述:</ label > < textarea name="describe" bindblur="checkDescribe"></ textarea > </ view > < view > < picker mode="multiSelector" value="{{end_time}}" data-type="end_time" data-param='end_time_p' bindchange="changeDateTime" bindcolumnchange="changeDateTimeColumn" range="{{dateTimeArray}}"> < view class="selectTime" style="display: flex;"> 结束时间: < image src="/image/select.png" wx:if="{{!end_time_p}}" style="width: 50rpx;height: 50rpx;"></ image > < text wx:else >{{end_time_p}}</ text > </ view > </ picker > </ view > < view style="display: flex;"> 选择图片:< image src="/image/camera.png" style="width: 60rpx;height: 60rpx;" bindtap="selectImg"></ image > </ view > < view class="selectImg"> < block wx:for="{{selectImgs}}" wx:key="index1"> < image src="{{item}}" style="height: 200rpx; margin: 10rpx; border-radius: 7rpx;" mode="heightFix" data-url="{{item}}"></ image > </ block > </ view > < button form-type="submit" type="primary">提交</ button > < button form-type="reset" type="default">重置</ button > </ view > </ form > </ scroll-view > </ view >> |
4-2、wxss
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | .container{ position : fixed ; top : 0 ; left : 0 ; right : 0 ; bottom : 0 ; background-color : rgb ( 227 , 247 , 233 ); } .main{ position : fixed ; width : 80% ; top : 5% ; left : 10% ; } .addGoods view{ margin-bottom : 10 rpx; } .addGoods input{ border : black solid 1px ; min-height : 60 rpx; margin-bottom : 15 rpx; } .addGoods textarea{ height : 150 rpx; border : black solid 1px ; } .addGoods button{ margin-top : 20 rpx; } .selectTime{ margin-top : 15 rpx; } .selectImg{ display : flex; height : auto } |
4-3、js代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | var dateTimePicker = require( '../../util/dateTimer.js' ) Page({ data: { end_time: '' , dateTimeArray: '' , //时间数组 startYear: 2022, //最小年份 endYear: 2050, // 最大年份 end_time_p: '' , //显示的结束时间 classifys: null , objectIndex: 0, //默认显示位置 selectImgs: null , uploadImgs: [] }, onLoad(options) { // 获取完整的年月日 时分秒,以及默认显示的数组 var obj = dateTimePicker.dateTimePicker( this .data.startYear, this .data.endYear) this .setData({ end_time: obj.dateTime, dateTimeArray: obj.dateTimeArray, }) //获取数据库分类信息 wx.cloud.database().collection( 'classifys' ).get({ success: (res) => { this .setData({ classifys: res.data }) } }) }, //选择分类 selectClassify(e) { this .setData({ objectIndex: e.detail.value }) }, //选择图片 selectImg() { wx.chooseImage({ count: 9, success: (res) => { this .setData({ selectImgs: res.tempFilePaths }) } }) }, //上传图片到云存储,异步函数,防止图片还没上传,就执行插入云数据库 uploadImages() { let _this = this return new Promise( function (resolve, reject) { function upload(index) { var picnum = index+1 wx.showLoading({ title: '上传第' + picnum + '张图片' }) console.log(_this.data.selectImgs) wx.cloud.uploadFile({ cloudPath: 'goodsImgs/' + new Date().getTime() + '_' + Math.floor(Math.random() * 1000) + '.jpg' , //给图片命名 filePath: _this.data.selectImgs[index], //本地图片路径 success: (res) => { _this.data.uploadImgs[index] = res.fileID wx.hideLoading({ success: (res) => {}, }) //判断是否全部上传 if (_this.data.selectImgs.length - 1 <= index) { console.log( '已全部上传' ) resolve( 'success' ) return } else { upload(index + 1) } }, fail: (err) => { reject( 'error' ) wx.showToast({ title: '上传失败,请重新上传' , type: 'none' }) } }) } upload(0) }) }, //提交表单,保存商品到云数据库 submit(e) { let dateEndTime = Date.parse( this .data.end_time_p) / 1000; //转时间戳,精确到毫秒 console.log( 'time' , this .data.end_time_p) let goodsName = e.detail.value.name //商品名 let startPrice = e.detail.value.start_price //起拍价 let describe = e.detail.value.describe //商品描述 let publisherId = wx.getStorageSync( 'userInfo' )._id //获取发布者id let startTime = Math.floor( new Date().getTime() / 1000) //起拍时间默认为当前时间 let classId = this .data.classifys[ this .data.objectIndex] // 当前选择的分类 //先上传图片再添加到云数据库 //点击提交的时候再次校验输入是否有误 if (! this .data.isNum || ! this .data.checkDescribe) { wx.showToast({ title: '起拍价或描述输入不符,请重新输入' , icon: 'none' }) } else if (goodsName== '' || startPrice== null || classId== '' || this .data.end_time_p== '' || this .data.selectImgs== null ){ wx.showToast({ title: '每一项输入信息都不能为空' , icon: 'none' }) } else { this .uploadImages().then((resolve, reject) => { let imagesUrl = this .data.uploadImgs //云存储的图片列表 wx.showLoading({ title: '发布中' }) setTimeout(() => {}, 500) wx.cloud.database().collection( 'goods' ).add({ data: { name: goodsName, start_price: startPrice, describe: describe, current_price: startPrice, images: imagesUrl, publisher_id: publisherId, end_time: dateEndTime, clicks: 0, class_id: classId, start_time: startTime, auctioning: true }, success: (res) => { console.log( '添加商品' ) wx.hideLoading({ success: (res) => { wx.navigateBack({ delta: 1, }) }, }) } }) }) } }, reset() { //重置图片和时间 this .setData({ selectImgs: null , end_time: null , end_time_p: null }) }, //处理选择的时间 changeDateTime(e) { let dateTimeArray = this .data.dateTimeArray, { type, param } = e.currentTarget.dataset; this .setData({ [type]: e.detail.value, [param]: dateTimeArray[0][e.detail.value[0]] + '-' + dateTimeArray[1][e.detail.value[1]] + '-' + dateTimeArray[2][e.detail.value[2]] + ' ' + dateTimeArray[3][e.detail.value[3]] + ':' + dateTimeArray[4][e.detail.value[4]] + ':' + dateTimeArray[5][e.detail.value[5]] }) }, //滑动时间触发 changeDateTimeColumn(e) { var dateArr = this .data.dateTimeArray, { type } = e.currentTarget.dataset, arr = this .data[type]; arr[e.detail.column] = e.detail.value; dateArr[2] = dateTimePicker.getMonthDay(dateArr[0][arr[0]], dateArr[1][arr[1]]); this .setData({ dateTimeArray: dateArr, [type]: arr }) }, //校验价格输入格式 checkPrice(e) { let price = e.detail.value let isNum = /^(([1-9][0-9]*)|(([0]\.\d{1,2}|[1-9][0-9]*\.\d{1,2})))$/ if (isNum.test(price)) { this .setData({ isNum: true }) } else { this .setData({ isNum: false }) wx.showToast({ title: '起拍价输入有误' , icon: 'none' }) } }, //校验描述,不能输入数字,防止透露联系方式 checkDescribe(e) { let describe = e.detail.value let notNum = /[0-9]$/ if (notNum.test(describe)) { wx.showToast({ title: '描述不能含有数字' , icon: 'none' }) this .setData({ checkDescribe: false }) } else { this .setData({ checkDescribe: true }) } } }) |
发布商品就到这里结束了,期间也发现了好多细节,通过自己踏踏实实的敲每一行代码,真的学到了很多!
本文来自博客园,作者:小李不背锅,转载请注明原文链接:https://www.cnblogs.com/lishilin-glut/p/16530470.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了