const {request} = require("../request/index") | |||||
/** | |||||
* 申请志愿者服务 | |||||
* @param {*} data | |||||
*/ | |||||
export const applyVolunteer = function (data) { | |||||
return request({ | |||||
url: '/westreamVolunteer/add', | |||||
method: 'POST', | |||||
data | |||||
}) | |||||
} |
// package_A/pages/volunteer-page/index.js | |||||
import { getOssAuth } from '../../../api/uploadOss.js' | |||||
import { Base64 } from 'js-base64' | |||||
const crypto = require('crypto-js') | |||||
import { applyVolunteer } from '../../../api/volunteer-service.js' | |||||
import {isPositiveInteger, isNoCharacters} from '../../../utils/check.js' | |||||
Page({ | |||||
/** | |||||
* 页面的初始数据 | |||||
*/ | |||||
data: { | |||||
checkAgree: false, // 是否勾选用户协议 | |||||
form: {}, | |||||
ossForm: {}, | |||||
imagePreviewList: [], // 上传图片预览列表 | |||||
imageList: [], // 图片列表 | |||||
// 表单验证 | |||||
formRules: { | |||||
activityNum: { | |||||
validator: function (value) { | |||||
return value && isPositiveInteger(value) && value<= 1000; | |||||
}, | |||||
warning: false | |||||
}, | |||||
activityDate: { | |||||
validator: function (value) { | |||||
return value; | |||||
}, | |||||
warning: false | |||||
}, | |||||
activityPoints: { | |||||
validator: function (value) { | |||||
return value && isPositiveInteger(value) && value<= 1000; | |||||
}, | |||||
warning: false | |||||
}, | |||||
companyName: { | |||||
validator: function (value) { | |||||
return value && isNoCharacters(value); | |||||
}, | |||||
warning: false | |||||
}, | |||||
contactName: { | |||||
validator: function (value) { | |||||
return value && isNoCharacters(value); | |||||
}, | |||||
warning: false | |||||
}, | |||||
contactPhone: { | |||||
validator: function (value) { | |||||
return value && isPositiveInteger(value); | |||||
}, | |||||
warning: false | |||||
}, | |||||
imageList: { | |||||
validator: function (value) { | |||||
return value.length != 0; | |||||
}, | |||||
warning: false | |||||
} | |||||
} | |||||
}, | |||||
/** | |||||
* 生命周期函数--监听页面显示 | |||||
*/ | |||||
onShow: function () { | |||||
this.getOssAuthForm() | |||||
const openid = wx.getStorageSync('openid') | |||||
let form = this.data.form | |||||
form.openid = openid | |||||
this.setData({form}) | |||||
}, | |||||
/* 获取图片上传鉴权 */ | |||||
getOssAuthForm() { | |||||
let that = this | |||||
getOssAuth().then((res) => { | |||||
let client = { | |||||
region: 'oss-cn-shanghai', | |||||
secure: true, | |||||
accessKeyId: res.accessKeyId, | |||||
accessKeySecret: res.accessKeySecret, | |||||
securityToken: res.securityToken, | |||||
bucket: 'ta-tech-image' | |||||
} | |||||
// 计算签名 | |||||
function computeSignature(accessKeySecret, canonicalString) { | |||||
return crypto.enc.Base64.stringify(crypto.HmacSHA1(canonicalString, accessKeySecret)); | |||||
} | |||||
const date = new Date(); | |||||
date.setHours(date.getHours() + 1); | |||||
const policyText = { | |||||
expiration: date.toISOString(), // 设置policy过期时间。 | |||||
conditions: [ | |||||
// 限制上传大小。 | |||||
["content-length-range", 0, 1024 * 1024 * 1024], | |||||
], | |||||
}; | |||||
// 获取签名 | |||||
const policy = Base64.encode(JSON.stringify(policyText)) // policy必须为base64的string。 | |||||
const signature = computeSignature(client.accessKeySecret, policy) | |||||
let ossForm = that.data.ossForm | |||||
ossForm = { | |||||
OSSAccessKeyId: client.accessKeyId, | |||||
signature, | |||||
policy, | |||||
SecurityToken: client.securityToken | |||||
} | |||||
that.setData({ | |||||
ossForm | |||||
}) | |||||
}) | |||||
}, | |||||
/* 上传图片 */ | |||||
uploadImage(){ | |||||
wx.chooseMedia({ | |||||
count: 5 - this.data.imageList.length, // 最多可以选择的图片张数,默认9 | |||||
mediaType: ['image'], // 图片 | |||||
sizeType: ['original'], // original 原图,compressed 压缩图,默认二者都有 | |||||
sourceType: ['album', 'camera'], // album 从相册选图,camera 使用相机,默认二者都有 | |||||
success:(res) =>{ | |||||
const list = res.tempFiles.map((item)=> { | |||||
return item.tempFilePath | |||||
}) | |||||
// success | |||||
let imagePreviewList = this.data.imagePreviewList.concat(list) | |||||
let imageList = this.data.imageList.concat(list); | |||||
this.setData({ imageList, imagePreviewList }) | |||||
this.validate('imageList') | |||||
}, | |||||
fail: (e) => { | |||||
console.log(e); | |||||
} | |||||
}) | |||||
}, | |||||
// 删除图片 | |||||
deleteImage(e) { | |||||
let index = this.getCurrentData(e); | |||||
let imageList = this.data.imageList | |||||
imageList.splice(index, 1) | |||||
let imagePreviewList = this.data.imagePreviewList | |||||
imagePreviewList.splice(index, 1) | |||||
this.setData({ imageList, imagePreviewList }) | |||||
this.validate('imageList') | |||||
}, | |||||
getCurrentData(e) { | |||||
return e.currentTarget.dataset.current; | |||||
}, | |||||
validate(name) { | |||||
let formRules = this.data.formRules; | |||||
let validator = formRules[name].validator | |||||
let result | |||||
if(name === 'imageList') { | |||||
result = validator ? !validator(this.data.imageList) : false | |||||
} else { | |||||
result = validator ? !validator(this.data.form[name]) : false; | |||||
} | |||||
formRules[name].warning = result | |||||
this.setData({formRules}) | |||||
return result | |||||
}, | |||||
// 表单验证 | |||||
validateForm() { | |||||
return new Promise((resolve, reject) => { | |||||
try { | |||||
let formRules = this.data.formRules; | |||||
let result = false; | |||||
for (let key in formRules) { | |||||
let temp = this.validate(key) | |||||
if (temp) { | |||||
result = temp | |||||
} | |||||
} | |||||
resolve(!result) | |||||
} catch (e) { | |||||
reject(e) | |||||
} | |||||
}) | |||||
}, | |||||
// 输入 | |||||
bindValue(e) { | |||||
let name = e.currentTarget.dataset.name; | |||||
let form = this.data.form; | |||||
form[name] = e.detail.value; | |||||
this.setData({ | |||||
form, | |||||
}) | |||||
this.validate(name) | |||||
}, | |||||
// 选择时间 | |||||
selectDate(e) { | |||||
let form = this.data.form | |||||
form.activityDate = e.detail.value | |||||
this.setData({form}) | |||||
}, | |||||
/* 表单上传 */ | |||||
submit(){ | |||||
if(this.data.checkAgree) { | |||||
this.validateForm().then(res => { | |||||
let ossForm = this.data.ossForm | |||||
let temp = [] | |||||
if (this.data.imageList.length > 0) { | |||||
wx.showLoading({title:"上传中",mask:true}) | |||||
temp = this.data.imageList.map(item => { | |||||
// 设置文件上传路径 | |||||
const randomString = Math.random().toString(36).slice(2) | |||||
const timestamp = new Date().getTime() | |||||
const key = `imagedir/${randomString}_${timestamp}.png` | |||||
// 上传图片 | |||||
return new Promise((resolve, reject)=> { | |||||
wx.uploadFile({ | |||||
url: 'https://ta-tech-image.oss-cn-shanghai.aliyuncs.com', | |||||
filePath: item, | |||||
name: 'file', | |||||
formData: { | |||||
key, | |||||
OSSAccessKeyId: ossForm.OSSAccessKeyId, | |||||
signature: ossForm.signature, | |||||
policy: ossForm.policy, | |||||
'x-oss-security-token': ossForm.SecurityToken | |||||
}, | |||||
success: (res)=> { | |||||
if(res.statusCode === 204) { | |||||
// 上传成功,将图片路径resolve出去 | |||||
resolve(key) | |||||
} else { | |||||
wx.showToast({ | |||||
title: '图片上传失败', | |||||
}) | |||||
reject('图片上传失败') | |||||
} | |||||
}, | |||||
fail: (e)=> { | |||||
console.log(e); | |||||
reject('图片上传失败') | |||||
} | |||||
}) | |||||
}) | |||||
}) | |||||
Promise.all(temp).then((res)=> { | |||||
let form = this.data.form; | |||||
form.photoUrl = res.join(',') | |||||
applyVolunteer(form).then(res => { | |||||
if (res.code === 0) { | |||||
wx.showModal({ | |||||
title: '提交成功', | |||||
confirmColor: '#3175E8', | |||||
showCancel: false, | |||||
content: '等待审核,审核通过后就可以获得积分。', | |||||
success(res) { | |||||
if(res.confirm) { | |||||
wx.navigateBack() | |||||
} | |||||
} | |||||
}) | |||||
} | |||||
}).finally(()=>{ | |||||
wx.hideLoading(); | |||||
}) | |||||
}).catch(()=> { | |||||
wx.hideLoading() | |||||
}) | |||||
} | |||||
}) | |||||
} else { | |||||
wx.showToast({ | |||||
icon: 'none', | |||||
title: '请先勾选用户服务协议以及隐私政策!', | |||||
}) | |||||
} | |||||
}, | |||||
/** | |||||
* 同意用户协议 | |||||
*/ | |||||
checkBoxChange() { | |||||
let checkAgree = this.data.checkAgree | |||||
this.setData({ | |||||
checkAgree: !checkAgree | |||||
}) | |||||
}, | |||||
// 用户服务协议 | |||||
showAgree() { | |||||
wx.navigateTo({ | |||||
url: '/package_A/pages/agree/index', | |||||
}) | |||||
}, | |||||
// 隐私政策 | |||||
showConceal() { | |||||
wx.navigateTo({ | |||||
url: '/package_A/pages/conceal/index', | |||||
}) | |||||
} | |||||
}) |
{ | |||||
"usingComponents": {}, | |||||
"navigationBarTitleText": "志愿者服务" | |||||
} |
<!--package_A/pages/volunteer-page/index.wxml--> | |||||
<view class="page_container"> | |||||
<image class="page_head" mode="widthFix" src="../../img/volunteer_head.png"></image> | |||||
<view class="form_container"> | |||||
<view class="form_item {{formRules.activityNum.warning ? 'warning' : ''}}"> | |||||
<text class="form_label"><text style="color: red;">*</text>活动人数</text> | |||||
<view class="value_box"> | |||||
<input style="width: 100%;height: 100%;" type="number" maxlength="100" placeholder="请输入活动人数" placeholder-style="font-size: 28rpx; color: #A6A6A6;" adjust-position="{{true}}" value="{{form.activityNum}}" data-name="activityNum" name="activityNum" bindblur="bindValue" /> | |||||
</view> | |||||
<text class="tips">请输入活动人数(仅可以输入数字,不超过1000)</text> | |||||
</view> | |||||
<view class="form_item {{formRules.activityDate.warning ? 'warning' : ''}}"> | |||||
<text class="form_label"><text style="color: red;">*</text>活动日期</text> | |||||
<picker class="picker_box" bindchange="selectDate" value="{{form.activityDate}}" mode="date"> | |||||
<view class="picker_value" wx:if="{{form.activityDate}}"> | |||||
<text>{{form.activityDate}}</text> | |||||
<image class="select_img" src="../../img/date.png"></image> | |||||
</view> | |||||
<view wx:else class="picker_value" style="color: #A6A6A6; font-size: 26rpx;"> | |||||
<text>请选择活动日期</text> | |||||
<image class="select_img" src="../../img/date.png"></image> | |||||
</view> | |||||
</picker> | |||||
<text class="tips">请选择活动日期</text> | |||||
</view> | |||||
<view class="form_item {{formRules.activityPoints.warning ? 'warning' : ''}}"> | |||||
<text class="form_label"><text style="color: red;">*</text>获取积分</text> | |||||
<view class="value_box"> | |||||
<input style="width: 100%;height: 100%;" type="number" maxlength="4" placeholder="请输入获取积分" placeholder-style="font-size: 28rpx; color: #A6A6A6;" adjust-position="{{true}}" value="{{form.activityPoints}}" data-name="activityPoints" name="activityPoints" bindblur="bindValue" /> | |||||
</view> | |||||
<text class="tips">请输入获取积分(仅可以输入数字,不超过1000)</text> | |||||
</view> | |||||
<view class="form_item {{formRules.companyName.warning ? 'warning' : ''}}"> | |||||
<text class="form_label"><text style="color: red;">*</text>单位名称</text> | |||||
<view class="value_box"> | |||||
<input style="width: 100%;height: 100%;" type="text" maxlength="100" placeholder="请输入单位名称" placeholder-style="font-size: 28rpx; color: #A6A6A6;" adjust-position="{{true}}" value="{{form.companyName}}" data-name="companyName" name="companyName" bindblur="bindValue" /> | |||||
</view> | |||||
<text class="tips">请输入单位名称(可输入数字、字母和汉字)</text> | |||||
</view> | |||||
<view class="form_item {{formRules.contactName.warning ? 'warning' : ''}}"> | |||||
<text class="form_label"><text style="color: red;">*</text>联系人</text> | |||||
<view class="value_box"> | |||||
<input style="width: 100%;height: 100%;" type="text" maxlength="100" placeholder="请输入联系人" placeholder-style="font-size: 28rpx; color: #A6A6A6;" adjust-position="{{true}}" value="{{form.contactName}}" data-name="contactName" name="contactName" bindblur="bindValue" /> | |||||
</view> | |||||
<text class="tips">请输入联系人(可输入数字、字母和汉字)</text> | |||||
</view> | |||||
<view class="form_item {{formRules.contactPhone.warning ? 'warning' : ''}}"> | |||||
<text class="form_label"><text style="color: red;">*</text>电话</text> | |||||
<view class="value_box"> | |||||
<input style="width: 100%;height: 100%;" type="text" maxlength="11" placeholder="请输入电话" placeholder-style="font-size: 28rpx; color: #A6A6A6;" adjust-position="{{true}}" value="{{form.contactPhone}}" data-name="contactPhone" name="contactPhone" bindblur="bindValue" /> | |||||
</view> | |||||
<text class="tips">请输入电话(仅可输入11位数字)</text> | |||||
</view> | |||||
<view class="upload_images {{formRules.imageList.warning? 'warning': ''}}"> | |||||
<text> | |||||
<text style="color: red;">* </text> | |||||
<text style="font-size:30rpx; color: #262E38;">上传照片</text> | |||||
<text style="font-size:26rpx; color: #999999;">(最多5张照片)</text> | |||||
</text> | |||||
<view class="image_list"> | |||||
<view class="image_preview" wx:for="{{imagePreviewList}}" wx:key="index"> | |||||
<image class="image_item" src="{{item}}" mode="aspectFill" data-item="{{item}}"> | |||||
</image> | |||||
<div class="close" data-current="{{index}}" catchtap="deleteImage"></div> | |||||
</view> | |||||
<view class="upload_image" bindtap="uploadImage" wx:if="{{imageList.length<5}}"> | |||||
<image style="height: 53rpx;width: 53rpx;" mode="widthFix" src="../../../assets/img/open_upload.png"></image> | |||||
</view> | |||||
</view> | |||||
<text class="tips">请上传照片</text> | |||||
</view> | |||||
<!-- 用户服务协议和隐私协议 --> | |||||
<view class="safe_box"> | |||||
<label class="checkbox" bindtap="checkBoxChange"> | |||||
<checkbox value="{{checkAgree}}" /><text>同意</text> | |||||
</label> | |||||
<text style="color: #2a82e4;" bindtap="showAgree">《用户服务协议》</text>和<text style="color: #2a82e4;" bindtap="showConceal">《隐私政策》</text> | |||||
</view> | |||||
<!-- 按钮 --> | |||||
<view class="{{checkAgree? 'btn_item submit_btn' : 'btn_item'}}" bindtap="submit">提交</view> | |||||
</view> | |||||
</view> |
/* package_A/pages/volunteer-page/index.wxss */ | |||||
.page_head { | |||||
width: 100vw; | |||||
} | |||||
.form_container { | |||||
width: 100%; | |||||
padding: 0 30rpx; | |||||
display: flex; | |||||
flex-direction: column; | |||||
justify-content: flex-start; | |||||
align-items: flex-start; | |||||
} | |||||
.form_item { | |||||
width: 100%; | |||||
padding: 30rpx 0; | |||||
display: flex; | |||||
justify-content: flex-start; | |||||
align-items: center; | |||||
border-bottom: 1rpx solid #EAE8E8; | |||||
position: relative; | |||||
} | |||||
.form_label { | |||||
width: 160rpx; | |||||
color: #262E38; | |||||
font-size: 30rpx; | |||||
} | |||||
.tips{ | |||||
position: absolute; | |||||
bottom: -6rpx; | |||||
left: -10rpx; | |||||
color: red; | |||||
font-size: 24rpx; | |||||
transform: translate(27rpx, -5rpx); | |||||
display: none; | |||||
} | |||||
.form_item.warning .tips, .use_purpose.warning .tips, .upload_images.warning .tips{ | |||||
display: block; | |||||
} | |||||
.value_box { | |||||
flex: 1; | |||||
display: flex; | |||||
justify-content: space-between; | |||||
align-items: center; | |||||
border-radius: 4rpx; | |||||
position: relative; | |||||
color: #333333; | |||||
font-size: 24rpx; | |||||
} | |||||
.picker_box { | |||||
flex: 1; | |||||
} | |||||
.picker_value { | |||||
width: 100%; | |||||
display: flex; | |||||
justify-content: space-between; | |||||
align-items: center; | |||||
} | |||||
.select_img { | |||||
width: 33rpx; | |||||
height: 37rpx; | |||||
} | |||||
/* 上传图片 */ | |||||
.upload_images { | |||||
width: 100%; | |||||
padding: 25rpx 0; | |||||
display: flex; | |||||
flex-direction: column; | |||||
justify-content: flex-start; | |||||
align-items: flex-start; | |||||
font-size: 28rpx; | |||||
color: #666666; | |||||
position: relative; | |||||
border-top: 1rpx solid #EDEDED; | |||||
} | |||||
.image_list { | |||||
width: 100%; | |||||
margin-top: 30rpx; | |||||
display: flex; | |||||
flex-wrap: wrap; | |||||
justify-content: flex-start; | |||||
align-content: flex-start; | |||||
} | |||||
.image_preview { | |||||
height: 150rpx; | |||||
width: 150rpx; | |||||
margin-right: 20rpx; | |||||
margin-bottom: 20rpx; | |||||
position: relative; | |||||
} | |||||
.image_item { | |||||
height:100%; | |||||
width: 100%; | |||||
margin-bottom: 20rpx; | |||||
margin-right: 30rpx; | |||||
border-radius: 10rpx; | |||||
} | |||||
.close{ | |||||
position: absolute; | |||||
top:0; | |||||
right:0; | |||||
transform: translate(40%,-40%); | |||||
height: 40rpx; | |||||
width: 40rpx; | |||||
background:rgba(179, 179, 179, 0.5); | |||||
border-radius: 50%; | |||||
z-index: 10; | |||||
} | |||||
.close::before{ | |||||
content: ""; | |||||
display: block; | |||||
position: absolute; | |||||
width: 60%; | |||||
height: 6rpx; | |||||
top:50%; | |||||
left:50%; | |||||
background:rgb(124, 124, 124); | |||||
transform-origin: center; | |||||
transform: translate(-50%,-50%) rotate(45deg); | |||||
} | |||||
.close::after{ | |||||
content: ""; | |||||
display: block; | |||||
position: absolute; | |||||
width: 60%; | |||||
height: 6rpx; | |||||
top:50%; | |||||
left:50%; | |||||
background:rgb(124, 124, 124); | |||||
transform-origin: center; | |||||
transform: translate(-50%,-50%) rotate(-45deg); | |||||
} | |||||
.upload_image { | |||||
width: 150rpx; | |||||
height: 150rpx; | |||||
display: flex; | |||||
flex-direction: column; | |||||
align-items: center; | |||||
justify-content: center; | |||||
border-radius: 4rpx; | |||||
background-color: #EEEEEE; | |||||
border: 1px solid #D2D2D2; | |||||
} | |||||
.note_mark { | |||||
margin-top: 20rpx; | |||||
font-size: 24rpx; | |||||
color: rgba(153, 153, 153, 1); | |||||
} | |||||
/* 用户协议确认框 */ | |||||
.safe_box { | |||||
width: 100%; | |||||
margin-top: 110rpx; | |||||
margin-bottom: 30rpx; | |||||
display: flex; | |||||
justify-content: flex-start; | |||||
align-items: center; | |||||
font-size: 26rpx; | |||||
color: #959595; | |||||
} | |||||
.checkbox { | |||||
display: flex; | |||||
justify-content: flex-start; | |||||
align-items: center; | |||||
} | |||||
checkbox { | |||||
transform: scale(0.8); | |||||
} | |||||
/* 按钮区 */ | |||||
.btn_item { | |||||
width: 100%; | |||||
height: 80rpx; | |||||
margin-bottom: 30rpx; | |||||
border-radius: 40rpx; | |||||
text-align: center; | |||||
line-height: 80rpx; | |||||
font-size: 32rpx; | |||||
color: #6F6F6F; | |||||
background-color: #EEEEEE; | |||||
} | |||||
.submit_btn { | |||||
color: #ffffff; | |||||
background-color: #2a82e4; | |||||
} |