订单加价

This commit is contained in:
2025-03-07 21:53:17 +08:00
parent f7c29b2905
commit 9008626d3d
22 changed files with 592 additions and 33 deletions

View File

@ -26,5 +26,15 @@ export default {
pickup:(orderid)=>request.post(`/api/order/${orderid}/deliveryman/pickup`),
complete:(orderid,images)=>request.post(`/api/order/${orderid}/deliveryman/complete`,{images:images}),
cancel:(orderid,reason)=>request.post(`/api/order/${orderid}/deliveryman/cancel`,{reason}),
check:()=>request.get('/api/order/deliveryman/check_new_order')
check:()=>request.get('/api/order/deliveryman/check_new_order'),
markUp:{
status:{
pending:"PENDING",
accepted:"ACCEPTED"
},
add:(data)=>request.post('/api/order-additional-fee',data),
get:(orderid)=>request.get(`/api/order-additional-fee/order/${orderid}`,{},{noTips:true}),
update:(data)=>request.put(`/api/order-additional-fee/${data.id}`,data)
}
}

9
app.js
View File

@ -59,7 +59,10 @@ App({
for(var key in rules){
((rules[key] instanceof Array)?rules[key]:[rules[key]]).map((item)=>{
let valid = true;
let value = (page.data[key]||'').trim();
let value = page.data[key];
if(typeof value=='string'){
value = value.trim();
}
//非空
if(item.required){
if(value==''){
@ -78,6 +81,10 @@ App({
if(value.length>(item.maxLength||Infinity)||value.length<item.minLength||0){
valid = false
}
}else if(item.min){
if(value<item.min){
valid = false;
}
}
if(valid){
page.setData({

View File

@ -0,0 +1,127 @@
import userApi from '../../api/user';
Component({
/**
* 组件的属性列表
*/
properties: {
loading:{
type:Boolean,
value:false
},
maxImgCount:{
type:Number,
value:10
}
},
/**
* 组件的初始数据
*/
data: {
tempImgs:[]
},
/**
* 组件的方法列表
*/
methods: {
chooseImage(){
wx.chooseMedia({
count:this.properties.maxImgCount - this.data.tempImgs.length,
mediaType:['image'],
sourceType:['camera'],
success:(res)=>{
this.setData({
tempImgs:this.data.tempImgs.concat(res.tempFiles)
});
this.uploadImages();
}
});
},
async uploadImages(){
let imgIndex = -1;
const file = this.data.tempImgs.find((item,index)=>{
imgIndex = index;
return !item.uploaded;
});
if(!file){
this.setData({
loading:false
})
return;
}
this.setData({
loading:true
})
let onProgress = (res)=>{
//进度
this.setData({
[`tempImgs[${imgIndex}].progress`]:res.progress
})
}
//无奈之举不大范围改动代码的同时我需要获取到上传任务task来中断上传操作不然要出问题task在上传时被附加到了onProgress
this.data.tempImgs[imgIndex].onProgress = onProgress;
let uploadResult = {};
try {
// uploadResult = await userApi.uploadImg({tempFilePath:'123'},onProgress);
uploadResult = await userApi.uploadImg(file,onProgress);
} catch (error) {
//这里要死循环
// await this.uploadImages();
// return;
}
if(uploadResult.url){
this.setData({
[`tempImgs[${imgIndex}].uploaded`]:true,
[`tempImgs[${imgIndex}].serverUrl`]:uploadResult.url
})
await this.uploadImages();
}else{
//上传失败
this.setData({
loading:false
})
wx.showToast({
icon:'error',
title: '上传失败',
})
return new Error('失败')
}
},
removeImage(event){
const index = event.currentTarget.dataset.index;
if(this.data.tempImgs[index].onProgress&&this.data.tempImgs[index].onProgress.task){
this.data.tempImgs[index].onProgress.task.abort();
}
console.log('remove',new Date().getTime());
this.data.tempImgs.splice(index,1);
this.setData({
tempImgs:this.data.tempImgs
});
},
async getUploadedUrl(){
await this.uploadImages();
let urls = [],loadAll = true;
this.data.tempImgs.map((item)=>{
if(!item.uploaded)loadAll = false;
urls.push(item.serverUrl);
})
if(loadAll){
return urls
}
throw new Error('图片上传失败');
},
setUploadedImgs(imgs){
this.setData({
tempImgs:imgs.concat(this.data.tempImgs)
})
}
},
lifetimes:{
attached(){
console.log('img uploader init');
}
}
})

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,14 @@
<view class="img-area">
{{uploadedImgs.length}}
<view class="item {{item.loading?'current':''}}" wx:for="{{tempImgs}}" wx:key="index">
<image class="image" src="{{item.tempFilePath||item.serverUrl}}"/>
<progress wx:if="{{!item.uploaded}}" class="progress" percent="{{item.progress}}" stroke-width="4"/>
<view class="close-area" bind:tap="removeImage" data-index="{{index}}">
<image src="/assets/icon/close-btn.png" class="icon"/>
</view>
</view>
<view class="take-photo item" bind:tap="chooseImage">
<image class="icon" src="/assets/icon/camera.png"/>
<view>点击拍照</view>
</view>
</view>

View File

@ -0,0 +1,58 @@
.img-area{
margin-top:60rpx;
display: flex;
flex-wrap: wrap;
gap: 20rpx;
display: flex;
justify-content: flex-end;
}
.img-area .item{
width: 120rpx;height:120rpx;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
border-radius: 12rpx;
position: relative;
}
.img-area .item .progress{
position: absolute;
top:0;left:0;
width:100%;
z-index: 1;
}
.img-area .item .close-area{
position: absolute;
right:-16rpx;top:-16rpx;
z-index: 2;
padding:5rpx;
display: flex;
align-items: center;
}
.img-area .item .close-area .icon{
width:28rpx;height:28rpx;
}
.img-area .item.loading::after{
content: '';
position: absolute;
width:100%;height:100%;
left:0;top:0;
background-color: rgba(0, 0, 0, 0.3);
z-index: 0;
}
.img-area .item .image{
width:100%;height:100%;
border-radius: 12rpx;
}
.img-area .take-photo{
font-size: 20rpx;
color: rgba(153, 153, 153, 0.5);
border: 1.2px solid rgba(124, 134, 149, 0.3);
}
.img-area .take-photo .icon{
width:32rpx;height:32rpx;
margin-bottom:14rpx;
}

View File

@ -1,7 +1,6 @@
const app = getApp();
Component({
/**
* 组件的属性列表
*/
@ -38,6 +37,10 @@ Component({
type:Boolean,
value:true
},
isShowOk:{
type:Boolean,
value:true
},
cancelButtonText:{
type:String,
value:'取消'

View File

@ -1,4 +1,5 @@
{
"component": true,
"usingComponents": {}
"usingComponents": {},
"styleIsolation": "apply-shared"
}

View File

@ -8,9 +8,10 @@
placeholder="{{contentPlaceholder}}" focus="{{contentFocus}}" animation="{{contentAnimation}}" cursor-spacing="200rpx"></textarea>
<view class="text" wx:else>{{content}}</view>
<view class="btns">
<slot/>
<view class="btns" wx:if="{{isShowCancel||isShowOk}}">
<button class="button cancel" plain wx:if="{{isShowCancel}}" bind:tap="cancelButtonTap">{{cancelButtonText}}</button>
<button class="button confirm" type="primary" bind:tap="okButtonTap">{{okButtonText}}</button>
<button class="button confirm" wx:if="{{isShowOk}}" type="primary" bind:tap="okButtonTap">{{okButtonText}}</button>
</view>
</view>
</page-container>

View File

@ -5,7 +5,6 @@
font-size: 34rpx;
font-weight: 500;
line-height: 50rpx;
padding:0 20rpx;
}
.custom-modal-view .title.center{
text-align: center;

View File

@ -0,0 +1,42 @@
// components/number-box/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
value:{
type:Number,
value:0
},
disabled:{
type:Boolean,
value:false
}
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
reduce(){
if(this.properties.value>1){
this.setData({
value:this.properties.value-1
})
}
},
plus(){
this.setData({
value:this.properties.value+1
})
}
}
})

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,5 @@
<view class="number-box {{disabled?'disabled':''}}">
<view class="button reduce {{value<=1?'disabled':''}}" bind:tap="reduce"></view>
<view class="value">{{value}}</view>
<view class="button plus" bind:tap="plus"></view>
</view>

View File

@ -0,0 +1,46 @@
.number-box{
display: flex;
align-items: center;
}
.number-box .value{
font-size: 36rpx;
padding:0 28rpx;
}
.number-box .button{
width:48rpx;height:48rpx;
line-height: 1;
padding:0;
text-align: center;
color: #fff;
position: relative;
background-color: var(--main-color);
border-radius: 50%;
border: 1rpx solid #FFC300;
}
.number-box .button.disabled{
background-color: #fff;
}
.number-box .reduce::before,
.number-box .plus::before,
.number-box .plus::after{
content: '';
width:50%;
height:4rpx;
background-color: #fff;
position:absolute;
left:25%;top:22rpx;
}
.number-box .reduce.disabled::before,
.number-box .plus.disabled::before,
.number-box .plus.disabled::after{
background-color: var(--main-color);
}
.number-box .plus::after{
width:4rpx;
height:50%;
left:22rpx;top:25%;
z-index: 100;
}
.number-box.disabled{
opacity: .4;
}

View File

@ -61,7 +61,9 @@ Page({
error:false,
loading:true
},
unReadOrderCount:0
unReadOrderCount:0,
selectedOrderId:''
},
onLoad(){
this.orderBackgroundNotice = this.selectComponent('#orderBackgroundNotice');
@ -296,23 +298,59 @@ Page({
})
},400)
},
showRefundConfirm(event){
showMoreAS(event){
const item = event.currentTarget.dataset.item;
const index = event.currentTarget.dataset.index;
if(item.receiving)return;
this.refundOrderIndex = index;
this.setData({
isShowRefundConfirm:true
//这里不能记录 index 因为列表可能会因为网络等各种原因刷新,后续的操作就可能找到其他订单
this.currentOrder = item;
const markupAction = ()=>{
const markupView = this.selectComponent('#markupView');
markupView.show(this.currentOrder)
}
const makePhoneCallAction = ()=>{
wx.makePhoneCall({
phoneNumber: this.currentOrder.address.phone,
});
}
const refundAction = ()=>{
if(this.currentOrder.receiving)return;
this.setData({
isShowRefundConfirm:true
});
}
let items = [];
let actions = [];
if(item.status==this.data.orderStatus.created){
items = ['联系用户','取消订单'];
actions = [makePhoneCallAction,refundAction]
}else if(item.status==this.data.orderStatus.received){
items = ['包裹加价','联系用户','取消订单'];
actions = [markupAction,makePhoneCallAction,refundAction]
}else if(item.status==this.data.orderStatus.delivering){
items = ['联系用户'];
actions = [makePhoneCallAction]
}
if(items.length!=actions.length){
throw new Error('sb')
}
wx.showActionSheet({
itemList: items,
success:(res)=>{
console.log(res);
actions[res.tapIndex]();
}
})
},
refund(event){
const index = this.refundOrderIndex;
const item = this.data.list[index];
const item = this.currentOrder;
if(item.receiving)return;
this.setData({
[`list[${index}].receiving`]:true
})
const index = this.data.list.findIndex((item)=>item.orderid==this.currentOrder.orderid);
if(index>-1){
this.setData({
[`list[${index}].receiving`]:true
})
}
orderApi.cancel(item.orderid,event.detail).then((data)=>{
wx.showToast({
title: '取消成功',
@ -405,7 +443,7 @@ Page({
this.setData({
completing:true
})
this.uploadImages().then(()=>{
this.uploadImages().then((data)=>{
let urls = [];
this.data.tempImgs.map((item)=>{
urls.push(item.serverUrl);
@ -459,9 +497,8 @@ Page({
try {
uploadResult = await userApi.uploadImg(file,onProgress);
} catch (error) {
await this.uploadImages();
console.log(new Date().getTime());
return;
// await this.uploadImages();
// return;
}
if(uploadResult.url){
this.setData({
@ -471,7 +508,14 @@ Page({
await this.uploadImages();
}else{
//上传失败
return new Error('失败')
this.setData({
completing:false
})
wx.showToast({
icon:'error',
title: '上传失败',
})
throw new Error('失败')
}
},
removeImage(event){

View File

@ -3,7 +3,8 @@
"list-view":"/components/listView",
"swipe-button":"/components/swipeButton",
"modal-view":"/components/modalView",
"background-notice":"/components/background-notice"
"background-notice":"/components/background-notice",
"mark-up":"./mark-up"
},
"navigationStyle": "custom",
"navigationBarTextStyle": "white"

View File

@ -71,24 +71,32 @@
</view>
</view>
<view class="btns" wx:if="{{item.status==orderStatus.created}}">
<button disabled="{{item.receiving}}" class="button refund-btn" plain
capture-catch:tap="showRefundConfirm" data-item="{{item}}" data-index="{{index}}">退单</button>
<button disabled="{{item.receiving}}" class="button more-btn" plain
capture-catch:tap="showMoreAS" data-item="{{item}}" data-index="{{index}}">
<view class="icon"></view>
</button>
<swipe-button class="swipe-button" loading="{{item.receiving}}" bind:done="getOrder"
data-item="{{item}}" data-index="{{index}}" button-text="我要接单"
button-loading-text="接单中..." capture-catch:tap="emptyFun"/>
</view>
<view class="btns" wx:if="{{item.status==orderStatus.received}}">
<button disabled="{{item.receiving}}" class="button refund-btn" capture-catch:tap="showRefundConfirm"
plain data-index="{{index}}" data-item="{{item}}">退单</button>
<button disabled="{{item.receiving}}" class="button more-btn" plain
capture-catch:tap="showMoreAS" data-item="{{item}}" data-index="{{index}}">
<view class="icon"></view>
</button>
<swipe-button class="swipe-button" loading="{{item.receiving}}" bind:done="receivedOrder"
data-item="{{item}}" data-index="{{index}}" button-text="我已取货"
button-loading-text="取货中..." capture-catch:tap="emptyFun"/>
</view>
<view class="btns" wx:if="{{item.status==orderStatus.delivering}}">
<button class="button concat-user-btn" capture-catch:tap="concatUser"
<!-- <button class="button concat-user-btn" capture-catch:tap="concatUser"
data-item="{{item}}">
<image class="icon" src="/assets/icon/phone.png"></image>
<label>联系用户</label>
</button> -->
<button disabled="{{item.receiving}}" class="button more-btn" plain
capture-catch:tap="showMoreAS" data-item="{{item}}" data-index="{{index}}">
<view class="icon"></view>
</button>
<button type="primary" class="confirm-send-btn"
capture-catch:tap="confirmSend" data-item="{{item}}">我已送达</button>
@ -198,7 +206,9 @@
</movable-area>
<modal-view titleText="取消订单需联系客户说明原因" editable content-placeholder="请输入退款原因" bind:ok="refund" model:show="{{isShowRefundConfirm}}" wx:if="{{isShowRefundConfirm}}" titleTextCenter="{{false}}" editRequired/>
<modal-view titleText="取消订单需联系客户说明原因" editable content-placeholder="请输入退单原因" bind:ok="refund" model:show="{{isShowRefundConfirm}}" wx:if="{{isShowRefundConfirm}}" titleTextCenter="{{false}}" editRequired/>
<mark-up id="markupView" class="mark-up"/>
<background-notice bind:onTrigger="findNewOrder" id="orderBackgroundNotice"
start="{{isStartLoopOrder}}" bind:initSuccess="bgNoticeSuccess"

View File

@ -258,10 +258,32 @@
padding:30rpx 40rpx;
margin:0;
}
.package-list .item .btns .refund-btn{
.package-list .item .btns .more-btn{
width:200rpx;
display: flex;
align-items: center;
justify-content: center;
}
.package-list .item .btns .refund-btn[disabled]{
.package-list .item .btns .more-btn .icon,
.package-list .item .btns .more-btn .icon::before,
.package-list .item .btns .more-btn .icon::after{
width:16rpx;height:16rpx;
background: #555555;
border-radius: 50%;
position: relative;
overflow: visible;
}
.package-list .item .btns .more-btn .icon::before{
content:'';
position:absolute;
left:-36rpx;top:0;
}
.package-list .item .btns .more-btn .icon::after{
content:'';
position:absolute;
left:36rpx;top:0;
}
.package-list .item .btns .more-btn[disabled]{
color:#999;
border-color:rgb(221, 219, 219);
}

View File

@ -0,0 +1,116 @@
import orderApi from '../../../api/order';
const app = getApp();
Component({
relations:{
"/components/img-uploader":{
type:'child'
}
},
/**
* 组件的属性列表
*/
properties: {
orderId:{
type:String,
value:''
}
},
/**
* 组件的初始数据
*/
data: {
imgUploading:false,
money:1,
reason:'',
requestId:'',
isShowMarkup:false,
},
/**
* 组件的方法列表
*/
methods: {
async apply(){
const imgUploader = this.selectComponent('#imgUploader');
const urls = await imgUploader.getUploadedUrl();
const valid = app.validateForm({
reason:{required:true,autoFocus:true,shake:true},
money:{min:1,shake:true}
},this)
if(valid.length>0){
return;
}
if(this.data.requestId){
orderApi.markUp.update({
id:this.data.requestId,
reason:this.data.reason,
photo_urls:urls,
additional_fee_amount:this.data.money
}).then(()=>{
this.setData({
reason:'',
money:0,
});
this.setData({
isShowMarkup:false
})
wx.showToast({
title: '保存成功',
})
});
}else{
orderApi.markUp.add({
orderid:this.currentOrder.orderid,
reason:this.data.reason,
photo_urls:urls,
additional_fee_amount:this.data.money
}).then((data)=>{
this.setData({
reason:'',
money:0,
});
this.setData({
isShowMarkup:false
})
wx.showToast({
title: '保存成功',
})
})
}
},
show(order){
this.setData({
isShowMarkup:true
})
this.currentOrder = order;
orderApi.markUp.get(order.orderid).then((data)=>{
let imgs = [];
data.photo_urls.map((item)=>{
imgs.push({
serverUrl:item,
uploaded:true
});
});
this.setData({
reason:data.reason,
money:data.additional_fee_amount,
requestId:data.id
})
this.selectComponent('#imgUploader').setUploadedImgs(imgs);
}).catch((error)=>{
this.setData({
reason:'',
money:0,
});
})
}
},
lifetimes:{
attached(){
}
}
})

View File

@ -0,0 +1,9 @@
{
"component": true,
"usingComponents": {
"modal-view":"/components/modalView",
"number-box":"/components/number-box",
"img-uploader":"/components/img-uploader"
},
"styleIsolation": "apply-shared"
}

View File

@ -0,0 +1,16 @@
<modal-view titleText="申请加价" isShowOk="{{false}}" isShowCancel="{{false}}"
okButtonText="提交申请" model:show="{{isShowMarkup}}" wx:if="{{isShowMarkup}}"
class="mark-up-view" titleTextCenter="{{false}}">
<view class="input-area">
<textarea model:value="{{reason}}" focus="{{reasonFocus}}" class="textarea"
placeholder="请输入加价原因" animation="{{reasonAnimation}}"/>
<img-uploader model:loading="{{imgUploading}}" id="imgUploader" maxImgCount="3"/>
</view>
<view class="amount-area" animation="{{moneyAnimation}}">
<view>加价金额 (元)</view>
<number-box model:value="{{money}}"/>
</view>
<button type="primary" loading="{{imgUploading}}" disabled="{{imgUploading}}"
bind:tap="apply">{{requestId?'修改申请':'提交申请'}}</button>
</modal-view>

View File

@ -0,0 +1,20 @@
.mark-up-view .input-area{
border: 1.2px solid rgba(153, 153, 153, 0.5);
border-radius: 12rpx;
padding:20rpx 16rpx 16rpx 24rpx;
margin-top:30rpx;
}
.mark-up-view .input-area .textarea{
line-height: 40rpx;
height:120rpx;
width: 100%;
}
.mark-up-view .amount-area{
display: flex;
align-items: center;
justify-content: space-between;
margin:40rpx 0 48rpx 0;
}