【小程序】发布商品(上传图片)

效果

 

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: 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;
  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
      })
    }
  }
})

  

发布商品就到这里结束了,期间也发现了好多细节,通过自己踏踏实实的敲每一行代码,真的学到了很多! 

posted @   小李不背锅  阅读(219)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示