草庐IT

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

小李不背锅 2023-03-28 原文

效果

 

1、主要布局

我们需要基本的input框和选择器picker,我设计的布局如下

 

 重点在分类选择器和时间选择器

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; 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

.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代码

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

  

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

有关【小程序】发布商品(上传图片)的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  3. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  4. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  5. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  6. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  7. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  8. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  9. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

  10. ruby-on-rails - 添加回形针新样式不影响旧上传的图像 - 2

    我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司

随机推荐