效果
1、主要布局
?重点在分类选择器和时间选择器
1-1.分类选择器
使用官方文档的picker标签,传入的数据为一个对象数组,数组中每一个对象包含两个字段
picker 标签的?
value属性:绑定的是一个数组下标index值;
range属性:绑定data里面的分类数组
range-key: 绑定data分类数组的key值,一般为分类名
用法如下
?selectClassify 方法
?1-2、时间选择器
选择picker mode属性为multiSelector,因为微信官方没有提供日期和时间同时选择器,只有日期和时间分开的选择器,因此我们要自己做一个日期时间选择器,因为这个选择器太复杂,这里只给出代码,里面都有详细注释了
首先在util文件下新建一个js文件
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
?选择时间后的处理
//处理选择的时间
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里面
//上传图片到云存储,异步函数,防止图片还没上传,就执行插入云数据库
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、提交数据
//提交表单,保存商品到云数据库
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
<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;width: 200rpx; margin: 10rpx; border-radius: 7rpx;" 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
.container{
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: white
}
.main{
position: fixed;
width: 80%;
top: 5%;
left: 10%;
bottom: 10rpx;
}
.addGoods view{
margin-bottom: 10rpx;
}
.addGoods input{
border: black solid 1px;
min-height: 60rpx;
margin-bottom: 15rpx;
}
.addGoods textarea{
height: 150rpx;
border: black solid 1px;
}
.addGoods button{
margin-top: 20rpx;
}
.selectTime{
margin-top: 15rpx;
}
.selectImg{
display: flex;
flex-wrap: wrap;
height: auto
}
?4-3、js代码
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]._id // 当前选择的分类
//先上传图片再添加到云数据库
//点击提交的时候再次校验输入是否有误
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 *1,
describe: describe,
current_price: startPrice *1,
images: imagesUrl,
publisher_id: publisherId,
end_time: dateEndTime,
clicks: 0,
class_id: classId,
start_time: startTime,
auctioning: true
},
success: (res) => {
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
})
}
}
})
发布商品就到这里结束了,期间也发现了好多细节,通过自己踏踏实实的敲每一行代码,真的学到了很多!?
|