- 新增订单取消弹窗组件,支持用户取消订单并填写取消原因 - 添加订单评价页面,用户可对已完成订单进行星级评分和文字评价 - 新增伴宠师选择页面,用户可选择之前服务过的伴宠师 - 在订单详情页和订单列表页集成取消订单和评价订单功能 - 优化订单操作流程,提升用户体验master
@ -0,0 +1,55 @@ | |||
<template> | |||
<view class="configPopup"> | |||
<uv-popup :show="showConfirmOrder" mode="bottom" @close="close" :round="10" :closeable="true" | |||
> | |||
<view class="content"> | |||
<up-parse :content="content"></up-parse> | |||
</view> | |||
</uv-popup> | |||
</view> | |||
</template> | |||
<script> | |||
import { mapState, mapGetters } from 'vuex' | |||
export default { | |||
name: 'configPoup', | |||
data() { | |||
return { | |||
content : '', | |||
showConfirmOrder : false, | |||
} | |||
}, | |||
onShow(){ | |||
}, | |||
methods: { | |||
//打开配置信息菜单 | |||
open(key){ | |||
this.content = this.configList[key].paramValueArea | |||
this.showConfirmOrder = true | |||
}, | |||
openText(content){ | |||
this.content = content | |||
this.showConfirmOrder = true | |||
}, | |||
close(){ | |||
this.showConfirmOrder = false | |||
}, | |||
}, | |||
computed : { | |||
...mapGetters(['configList']) | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.configPopup { | |||
.content{ | |||
min-height: 50vh; | |||
max-height: 70vh; | |||
padding: 30rpx 20rpx; | |||
overflow: scroll; | |||
height: 100%; | |||
box-sizing: border-box; | |||
} | |||
} | |||
</style> |
@ -0,0 +1,216 @@ | |||
<template> | |||
<uv-popup ref="popup" :safeAreaInsetBottom="false" :round="10"> | |||
<view class="cancel-popup"> | |||
<view class="popup-content"> | |||
<view class="popup-header"> | |||
<text class="popup-title">取消订单</text> | |||
</view> | |||
<view class="popup-body"> | |||
<text class="popup-text">请添加客服微信,方便为您解决订单疑问或退订服务</text> | |||
<view class="qrcode-container"> | |||
<image class="qrcode-image" :src="qrCodeUrl" mode="aspectFit" :show-menu-by-longpress="true"></image> | |||
</view> | |||
<text class="popup-text">取消订单原因(选填)</text> | |||
<view class="reason-container"> | |||
<textarea class="reason-input" v-model="cancelReason" placeholder="请填写取消原因,方便我们提升服务"></textarea> | |||
</view> | |||
</view> | |||
<view class="popup-footer"> | |||
<view class="popup-btn cancel-btn" @click="close"> | |||
<text>不取消</text> | |||
</view> | |||
<view class="popup-btn confirm-btn" @click="confirmCancel"> | |||
<text>确认取消</text> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
</uv-popup> | |||
</template> | |||
<script> | |||
import { cancelOrder } from "@/api/system/user.js" | |||
export default { | |||
props: { | |||
qrCodeUrl: { | |||
type: String, | |||
default: 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/details/QR_Code.png' | |||
} | |||
}, | |||
data(){ | |||
return { | |||
order : {}, | |||
cancelReason: '', | |||
} | |||
}, | |||
methods: { | |||
// 打开弹窗 | |||
open(order){ | |||
this.order = order; | |||
this.$refs.popup.open(); | |||
}, | |||
// 关闭弹窗 | |||
close() { | |||
this.cancelReason = ''; // 清空取消原因 | |||
this.$refs.popup.close(); | |||
}, | |||
// 确认取消订单 | |||
confirmCancel() { | |||
if (!this.order || !this.order.id) { | |||
uni.showToast({ | |||
title: '订单信息不完整', | |||
icon: 'none' | |||
}); | |||
return; | |||
} | |||
// 显示加载中 | |||
uni.showLoading({ | |||
title: '处理中...' | |||
}); | |||
// 调用取消订单API | |||
cancelOrder(this.order.id, this.cancelReason).then(res => { | |||
uni.hideLoading(); | |||
if (res && res.code === 200) { | |||
// 成功 | |||
uni.showToast({ | |||
title: '订单已取消', | |||
icon: 'success' | |||
}); | |||
// 通知父组件订单已取消 | |||
this.$emit('cancel', this.order, this.cancelReason); | |||
// 关闭弹窗 | |||
this.close(); | |||
} else { | |||
// 失败 | |||
uni.showToast({ | |||
title: res?.msg || '取消失败,请联系客服', | |||
icon: 'none' | |||
}); | |||
} | |||
}).catch(error => { | |||
uni.hideLoading(); | |||
uni.showToast({ | |||
title: '取消失败,请稍后再试', | |||
icon: 'none' | |||
}); | |||
}); | |||
} | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.cancel-popup { | |||
// position: fixed; | |||
// top: 0; | |||
// left: 0; | |||
// right: 0; | |||
// bottom: 0; | |||
// z-index: 999; | |||
display: flex; | |||
justify-content: center; | |||
align-items: center; | |||
.popup-content { | |||
position: relative; | |||
width: 600rpx; | |||
background-color: #FFFFFF; | |||
border-radius: 12rpx; | |||
overflow: hidden; | |||
z-index: 1000; | |||
} | |||
.popup-header { | |||
padding: 30rpx; | |||
text-align: center; | |||
background-color: #FFAA48; | |||
} | |||
.popup-title { | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #fff; | |||
} | |||
.popup-body { | |||
padding: 30rpx; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
} | |||
.popup-text { | |||
font-size: 28rpx; | |||
color: #333333; | |||
margin-bottom: 20rpx; | |||
text-align: center; | |||
} | |||
.popup-subtext { | |||
font-size: 24rpx; | |||
color: #999999; | |||
margin-bottom: 20rpx; | |||
text-align: center; | |||
} | |||
.qrcode-container { | |||
width: 300rpx; | |||
height: 300rpx; | |||
margin: 20rpx 0; | |||
display: flex; | |||
justify-content: center; | |||
align-items: center; | |||
} | |||
.qrcode-image { | |||
width: 100%; | |||
height: 100%; | |||
} | |||
.popup-footer { | |||
display: flex; | |||
border-top: 1px solid #EEEEEE; | |||
} | |||
.popup-btn { | |||
flex: 1; | |||
padding: 30rpx 0; | |||
text-align: center; | |||
font-size: 28rpx; | |||
} | |||
.cancel-btn { | |||
color: #666666; | |||
border-right: 1px solid #EEEEEE; | |||
} | |||
.confirm-btn { | |||
color: #FFAA48; | |||
} | |||
.reason-container { | |||
width: 100%; | |||
margin-bottom: 20rpx; | |||
background-color: #F7F7F7; | |||
border-radius: 8rpx; | |||
} | |||
.reason-input { | |||
width: 100%; | |||
height: 150rpx; | |||
font-size: 28rpx; | |||
color: #666; | |||
line-height: 1.5; | |||
padding: 20rpx; | |||
} | |||
} | |||
</style> |
@ -0,0 +1,190 @@ | |||
<template> | |||
<uv-popup ref="popup" :round="10"> | |||
<view class="companion-popup" v-if="type == 0"> | |||
<view class="popup-header"> | |||
<text class="popup-title">是否指定之前服务过的伴宠师</text> | |||
<view class="popup-close" @click="close"> | |||
<uni-icons type="close" size="20" color="#999"></uni-icons> | |||
</view> | |||
</view> | |||
<view class="popup-content"> | |||
<view class="option-item" @click="selectOption('yes')"> | |||
<text class="option-text">是</text> | |||
<view class="option-circle" :class="{'selected': selectedOption === 'yes'}"> | |||
<view class="option-inner" v-if="selectedOption === 'yes'"></view> | |||
</view> | |||
</view> | |||
<view class="option-item" @click="selectOption('no')"> | |||
<text class="option-text">否</text> | |||
<view class="option-circle" :class="{'selected': selectedOption === 'no'}"> | |||
<view class="option-inner" v-if="selectedOption === 'no'"></view> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
<view class="companion-popup" v-else> | |||
<view class="popup-header"> | |||
<text class="popup-title">请选择您喜欢的下单方式</text> | |||
<view class="popup-close" @click="close"> | |||
<uni-icons type="close" size="20" color="#999"></uni-icons> | |||
</view> | |||
</view> | |||
<view class="popup-content"> | |||
<view class="option-item" @click="selectOption('yes')"> | |||
<text class="option-text">系统下单</text> | |||
<view class="option-circle" :class="{'selected': selectedOption === 'yes'}"> | |||
<view class="option-inner" v-if="selectedOption === 'yes'"></view> | |||
</view> | |||
</view> | |||
<view class="option-item" @click="selectOption('no')"> | |||
<text class="option-text">指定伴宠师</text> | |||
<view class="option-circle" :class="{'selected': selectedOption === 'no'}"> | |||
<view class="option-inner" v-if="selectedOption === 'no'"></view> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
</uv-popup> | |||
</template> | |||
<script> | |||
export default { | |||
data() { | |||
return { | |||
selectedOption: '', | |||
type : 0, | |||
} | |||
}, | |||
methods: { | |||
// 打开弹窗 | |||
open() { | |||
this.selectedOption = ''; | |||
this.type = 0; | |||
this.$refs.popup.open('bottom'); | |||
}, | |||
// 关闭弹窗 | |||
close() { | |||
this.$refs.popup.close(); | |||
}, | |||
// 选择选项 | |||
selectOption(option) { | |||
this.selectedOption = option; | |||
if(this.type == 1){ | |||
if (option === 'yes') { | |||
this.close(); | |||
setTimeout(() => { | |||
uni.navigateTo({ | |||
url: '/pages/newOrder/serviceNew' | |||
}); | |||
}, 300); | |||
} else if (option === 'no') { | |||
this.close(); | |||
setTimeout(() => { | |||
uni.navigateTo({ | |||
url: '/pages/companionPetList/companionPetList' | |||
}); | |||
}, 300); | |||
} | |||
return | |||
} | |||
// 如果选择"是",跳转到伴宠师选择页面 | |||
if (option === 'yes') { | |||
this.close(); | |||
setTimeout(() => { | |||
uni.navigateTo({ | |||
url: '/pages_order/order/companionSelect' | |||
}); | |||
}, 300); | |||
} else if (option === 'no') { | |||
this.type = 1; | |||
this.selectedOption = ''; | |||
} | |||
} | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.companion-popup { | |||
position: relative; | |||
background-color: #FFFFFF; | |||
border-radius: 20rpx; | |||
.popup-header { | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
padding: 30rpx 20rpx; | |||
position: relative; | |||
.popup-title { | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #333; | |||
text-align: center; | |||
} | |||
.popup-close { | |||
position: absolute; | |||
right: 20rpx; | |||
top: 30rpx; | |||
} | |||
} | |||
.popup-content { | |||
padding: 20rpx 30rpx 50rpx; | |||
.option-item { | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
height: 100rpx; | |||
border-radius: 10rpx; | |||
background-color: #F8F8F8; | |||
margin-bottom: 20rpx; | |||
padding: 0 30rpx; | |||
// &:first-child { | |||
// background-color: #FFF9E6; | |||
// .option-text { | |||
// color: #FFAA48; | |||
// } | |||
// } | |||
&:last-child { | |||
margin-bottom: 0; | |||
} | |||
.option-text { | |||
font-size: 28rpx; | |||
color: #333; | |||
} | |||
.option-circle { | |||
width: 36rpx; | |||
height: 36rpx; | |||
border-radius: 50%; | |||
border: 2rpx solid #DDDDDD; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
&.selected { | |||
border-color: #FFAA48; | |||
} | |||
.option-inner { | |||
width: 24rpx; | |||
height: 24rpx; | |||
border-radius: 50%; | |||
background-color: #FFAA48; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
</style> |
@ -0,0 +1,434 @@ | |||
<template> | |||
<view class="companion-select-page"> | |||
<!-- 顶部警告提示 --> | |||
<view class="warning-tip"> | |||
<view class="warning-icon"> | |||
<uni-icons type="info" size="16" color="#A94F20"></uni-icons> | |||
</view> | |||
<view class="warning-text"> | |||
<text>相距距离仅供参考!伴宠师位置可能不是实时位置,请提前10天下单!</text> | |||
</view> | |||
</view> | |||
<!-- 伴宠师列表 --> | |||
<scroll-view scroll-y class="companion-scroll"> | |||
<view class="companion-list"> | |||
<view | |||
class="companion-item" | |||
v-for="(item, index) in companionList" | |||
:key="index" | |||
@click="selectCompanion(item)" | |||
:class="{'selected': selectedCompanionId === item.id}" | |||
> | |||
<!-- 左侧选中标记 --> | |||
<view class="select-icon"> | |||
<view class="radio-circle" :class="{'checked': selectedCompanionId === item.id}"> | |||
<view class="radio-inner" v-if="selectedCompanionId === item.id"></view> | |||
</view> | |||
</view> | |||
<!-- 伴宠师卡片内容 --> | |||
<view class="companion-card"> | |||
<!-- 头像和基本信息 --> | |||
<view class="card-header"> | |||
<view class="companion-avatar"> | |||
<image :src="item.avatar" mode="aspectFill"></image> | |||
<image v-if="item.verified" class="verified-icon" src="/static/images/details/verified.svg"></image> | |||
</view> | |||
<view class="companion-basic-info"> | |||
<view class="companion-name-row"> | |||
<text class="companion-name">{{item.name}}</text> | |||
<image :src="item.gender === '男生' ? '/static/images/details/boy.svg' : '/static/images/details/girl.svg'" class="gender-icon"></image> | |||
</view> | |||
<view class="companion-rating"> | |||
<text class="client-rating">客户点赞: {{item.likes}}</text> | |||
<image src="/static/images/details/like.svg" class="like-icon"></image> | |||
</view> | |||
<view class="companion-distance"> | |||
<text>距离您: {{item.distance}} km</text> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 描述信息 --> | |||
<view class="companion-desc"> | |||
<text>{{item.description || '简介:有一只3岁金猫-忽悠,热爱小宠物...'}}</text> | |||
</view> | |||
<!-- 伴宠师经验描述 --> | |||
<view class="companion-experience"> | |||
<text>{{item.experience}}</text> | |||
</view> | |||
<!-- 查看详情按钮 --> | |||
<view class="view-detail-btn" @click.stop="viewCompanionDetail(item.id)"> | |||
<text>查看详情</text> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 无数据提示 --> | |||
<view class="no-data" v-if="companionList.length === 0"> | |||
<image src="/static/images/personal/no-data.png" mode="aspectFit"></image> | |||
<text>暂无伴宠师数据</text> | |||
</view> | |||
</view> | |||
</scroll-view> | |||
<!-- 底部按钮 --> | |||
<view class="footer-buttons"> | |||
<view class="cancel-btn" @click="cancel"> | |||
<text>取消</text> | |||
</view> | |||
<view class="confirm-btn" @click="confirm"> | |||
<text>确定</text> | |||
</view> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
export default { | |||
data() { | |||
return { | |||
companionList: [ | |||
{ | |||
id: '1', | |||
name: '宠物宝贝', | |||
gender: '男生', | |||
avatar: 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/details/QR_Code.png', | |||
verified: true, | |||
rating: 5.0, | |||
likes: 5601, | |||
distance: 10.8, | |||
description: '简介:有一只3岁金猫-忽悠,热爱小宠物...', | |||
experience: '养宠4年 | 评价11条 | 服务小结13份' | |||
}, | |||
{ | |||
id: '2', | |||
name: '宠物宝贝', | |||
gender: '女生', | |||
avatar: 'https://catmdogf.oss-cn-shanghai.aliyuncs.com/CMDF/front/details/QR_Code.png', | |||
verified: true, | |||
rating: 5.0, | |||
likes: 5601, | |||
distance: 10.8, | |||
description: '简介:有一只3岁金猫-忽悠,热爱小宠物...', | |||
experience: '养宠4年 | 评价11条 | 服务小结13份' | |||
} | |||
], | |||
selectedCompanionId: '', | |||
}; | |||
}, | |||
computed: { | |||
// 获取当前选中的伴宠师 | |||
selectedCompanion() { | |||
if (!this.selectedCompanionId) return null; | |||
return this.companionList.find(item => item.id === this.selectedCompanionId); | |||
} | |||
}, | |||
onLoad(options) { | |||
// 获取选择过的伴宠师列表 | |||
this.getServicedCompanions(); | |||
}, | |||
methods: { | |||
// 获取服务过的伴宠师 | |||
getServicedCompanions() { | |||
// 实际项目中应调用API获取服务过的伴宠师列表 | |||
// 示例代码: | |||
/* | |||
getServicedCompanions().then(res => { | |||
if (res && res.code === 200) { | |||
this.companionList = res.data || []; | |||
} | |||
}).catch(err => { | |||
console.error('获取服务过的伴宠师失败', err); | |||
}); | |||
*/ | |||
// 使用模拟数据 | |||
console.log('获取服务过的伴宠师列表'); | |||
}, | |||
// 选择伴宠师 | |||
selectCompanion(companion) { | |||
this.selectedCompanionId = companion.id; | |||
}, | |||
// 取消选择 | |||
cancel() { | |||
uni.navigateBack(); | |||
}, | |||
// 确认选择 | |||
confirm() { | |||
if (!this.selectedCompanionId) { | |||
uni.showToast({ | |||
title: '请选择伴宠师', | |||
icon: 'none' | |||
}); | |||
return; | |||
} | |||
// 将选择的伴宠师信息返回给上一页 | |||
// 实际项目中应根据需求处理 | |||
// 示例代码: | |||
/* | |||
const pages = getCurrentPages(); | |||
const prevPage = pages[pages.length - 2]; | |||
prevPage.$vm.setSelectedCompanion(this.selectedCompanion); | |||
*/ | |||
uni.navigateBack(); | |||
}, | |||
// 查看伴宠师详情 | |||
viewCompanionDetail(companionId) { | |||
// 跳转到伴宠师详情页面 | |||
uni.navigateTo({ | |||
url: `/pages/companionPetList/companionPetInfo?id=${companionId}` | |||
}); | |||
} | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.companion-select-page { | |||
background-color: #F5F5F5; | |||
min-height: 100vh; | |||
display: flex; | |||
flex-direction: column; | |||
padding-bottom: 120rpx; | |||
} | |||
.warning-tip { | |||
background-color: #FFF4E5; | |||
padding: 20rpx 30rpx; | |||
display: flex; | |||
align-items: center; | |||
margin: 20rpx; | |||
.warning-icon { | |||
margin-right: 10rpx; | |||
} | |||
.warning-text { | |||
flex: 1; | |||
font-size: 24rpx; | |||
color: #A94F20; | |||
line-height: 1.4; | |||
} | |||
} | |||
.companion-scroll { | |||
flex: 1; | |||
height: calc(100vh - 250rpx); | |||
} | |||
.companion-list { | |||
padding: 20rpx; | |||
} | |||
.companion-item { | |||
background-color: #FFFFFF; | |||
border-radius: 16rpx; | |||
padding: 20rpx; | |||
margin-bottom: 20rpx; | |||
display: flex; | |||
align-items: flex-start; | |||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | |||
border: 2rpx solid #fff; | |||
.select-icon { | |||
width: 40rpx; | |||
height: 40rpx; | |||
margin-right: 20rpx; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
.radio-circle { | |||
width: 32rpx; | |||
height: 32rpx; | |||
border: 2rpx solid #DDDDDD; | |||
border-radius: 50%; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
&.checked { | |||
border-color: #FFAA48; | |||
} | |||
.radio-inner { | |||
width: 18rpx; | |||
height: 18rpx; | |||
background-color: #FFAA48; | |||
border-radius: 50%; | |||
} | |||
} | |||
} | |||
&.selected { | |||
border: 2rpx solid #FFAA48; | |||
} | |||
.companion-card { | |||
flex: 1; | |||
} | |||
.card-header { | |||
display: flex; | |||
margin-bottom: 16rpx; | |||
} | |||
.companion-avatar { | |||
position: relative; | |||
width: 100rpx; | |||
height: 100rpx; | |||
margin-right: 20rpx; | |||
image { | |||
width: 100%; | |||
height: 100%; | |||
border-radius: 10rpx; | |||
} | |||
.verified-icon { | |||
position: absolute; | |||
right: -10rpx; | |||
bottom: -10rpx; | |||
width: 30rpx; | |||
height: 30rpx; | |||
} | |||
} | |||
.companion-basic-info { | |||
flex: 1; | |||
display: flex; | |||
flex-direction: column; | |||
justify-content: center; | |||
} | |||
.companion-name-row { | |||
display: flex; | |||
align-items: center; | |||
margin-bottom: 6rpx; | |||
.companion-name { | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #333; | |||
margin-right: 10rpx; | |||
} | |||
.gender-icon { | |||
width: 24rpx; | |||
height: 24rpx; | |||
} | |||
} | |||
.companion-rating { | |||
display: flex; | |||
align-items: center; | |||
margin-bottom: 6rpx; | |||
.client-rating { | |||
font-size: 24rpx; | |||
color: #666; | |||
margin-right: 6rpx; | |||
} | |||
.like-icon { | |||
width: 24rpx; | |||
height: 24rpx; | |||
} | |||
} | |||
.companion-distance { | |||
font-size: 24rpx; | |||
color: #999; | |||
} | |||
.companion-desc { | |||
font-size: 24rpx; | |||
color: #666; | |||
margin-bottom: 16rpx; | |||
line-height: 1.4; | |||
} | |||
.companion-experience { | |||
font-size: 24rpx; | |||
color: #999; | |||
background-color: #FFF9E6; | |||
padding: 16rpx; | |||
border-radius: 8rpx; | |||
margin-bottom: 16rpx; | |||
color: #be721b; | |||
text-align: center; | |||
} | |||
.view-detail-btn { | |||
align-self: flex-end; | |||
background-color: #FFAA48; | |||
color: #FFFFFF; | |||
font-size: 26rpx; | |||
padding: 12rpx 30rpx; | |||
border-radius: 30rpx; | |||
text-align: center; | |||
width: fit-content; | |||
margin-left: auto; | |||
} | |||
} | |||
.no-data { | |||
text-align: center; | |||
padding: 100rpx 0; | |||
image { | |||
width: 200rpx; | |||
height: 200rpx; | |||
margin-bottom: 20rpx; | |||
} | |||
text { | |||
font-size: 28rpx; | |||
color: #999; | |||
} | |||
} | |||
.footer-buttons { | |||
position: fixed; | |||
bottom: 0; | |||
left: 0; | |||
right: 0; | |||
display: flex; | |||
padding: 20rpx 30rpx; | |||
background-color: #FFFFFF; | |||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05); | |||
.cancel-btn, .confirm-btn { | |||
flex: 1; | |||
height: 88rpx; | |||
line-height: 88rpx; | |||
text-align: center; | |||
border-radius: 44rpx; | |||
font-size: 30rpx; | |||
} | |||
.cancel-btn { | |||
background-color: #FFFFFF; | |||
color: #666; | |||
border: 1px solid #DDDDDD; | |||
margin-right: 20rpx; | |||
} | |||
.confirm-btn { | |||
background-color: #FFAA48; | |||
color: #FFFFFF; | |||
box-shadow: 0 4rpx 8rpx rgba(255, 170, 72, 0.3); | |||
} | |||
} | |||
</style> |
@ -0,0 +1,407 @@ | |||
<template> | |||
<view class="order-modify-page"> | |||
<!-- 页面头部 --> | |||
<view class="page-header"> | |||
<text class="header-title">修改订单</text> | |||
</view> | |||
<!-- 订单内容区域 --> | |||
<view class="order-modify-content"> | |||
<!-- 订单修改说明 --> | |||
<view class="modify-notice"> | |||
<view class="notice-item"> | |||
<text class="notice-icon">🐾</text> | |||
<text class="notice-text">您可以对<text class="highlight-text">未服务或未支付</text>的订单进行修改或取消</text> | |||
</view> | |||
<view class="notice-item"> | |||
<text class="notice-icon">🐾</text> | |||
<text class="notice-text">若需修改已支付的服务项目,如「增加服务时间/服务项目」,可点击下方按钮【<text class="highlight-text">联系客服</text>】寻求帮助,感谢!</text> | |||
</view> | |||
</view> | |||
<!-- 服务修改信息 --> | |||
<view class="modify-info-card"> | |||
<view class="card-title"> | |||
<text>服务修改信息</text> | |||
</view> | |||
<view class="info-content"> | |||
<view class="info-item"> | |||
<text class="info-label">联系人</text> | |||
<text class="info-value">{{modifyInfo.contactName}}</text> | |||
</view> | |||
<view class="info-item"> | |||
<text class="info-label">联系方式</text> | |||
<text class="info-value">{{modifyInfo.contactPhone}}</text> | |||
</view> | |||
<view class="info-item payment-method"> | |||
<text class="info-label">销售支持方式</text> | |||
<view class="info-value payment-value"> | |||
<text>{{modifyInfo.paymentMethod}}</text> | |||
<text class="arrow-right">></text> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 修改原因 --> | |||
<view class="modify-reason-card"> | |||
<view class="card-title"> | |||
<text>修改原因</text> | |||
</view> | |||
<view class="reason-content"> | |||
<textarea | |||
class="reason-input" | |||
v-model="modifyReason" | |||
placeholder="请输入修改原因(选填)" | |||
maxlength="200" | |||
></textarea> | |||
<view class="word-count"> | |||
<text>{{modifyReason.length}}/200</text> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 底部按钮区域 --> | |||
<view class="order-modify-footer"> | |||
<view class="footer-btn cancel-service-btn" @click="$refs.cancelPopup.open()"> | |||
<text>取消服务</text> | |||
</view> | |||
<view class="footer-btn confirm-modify-btn" @click="confirmModify"> | |||
<text>确认修改</text> | |||
</view> | |||
</view> | |||
<!-- 取消订单弹窗 --> | |||
<cancel-order-popup | |||
ref="cancelPopup" | |||
@cancel="handleCancelOrder" | |||
></cancel-order-popup> | |||
<!-- 客服组件 --> | |||
<Kefu></Kefu> | |||
</view> | |||
</template> | |||
<script> | |||
import CancelOrderPopup from '../../components/order/CancelOrderPopup.vue'; | |||
export default { | |||
components: { | |||
CancelOrderPopup | |||
}, | |||
data() { | |||
return { | |||
orderId: '', // 订单ID | |||
modifyInfo: { | |||
contactName: '张小二', | |||
contactPhone: '18888888888', | |||
paymentMethod: '存子快递宝' | |||
}, | |||
modifyReason: '', // 修改原因 | |||
showCancelOrderPopup: false // 是否显示取消订单弹窗 | |||
}; | |||
}, | |||
onLoad(options) { | |||
// 获取路由参数中的订单ID | |||
if (options.id) { | |||
this.orderId = options.id; | |||
// 加载订单数据 | |||
this.loadOrderData(); | |||
} | |||
}, | |||
methods: { | |||
// 加载订单数据 | |||
loadOrderData() { | |||
// 这里应该调用API获取订单详情 | |||
// 示例代码: | |||
/* | |||
const params = { | |||
openId: getOpenIdKey(), | |||
orderId: this.orderId | |||
}; | |||
getOrderDetail(params).then(res => { | |||
if (res && res.code === 200) { | |||
// 设置订单信息 | |||
this.modifyInfo = { | |||
contactName: res.data.contactName, | |||
contactPhone: res.data.contactPhone, | |||
paymentMethod: res.data.paymentMethod | |||
}; | |||
} | |||
}).catch(err => { | |||
console.error('获取订单详情失败', err); | |||
uni.showToast({ | |||
title: '获取订单信息失败', | |||
icon: 'none' | |||
}); | |||
}); | |||
*/ | |||
// 这里使用模拟数据 | |||
console.log('加载订单数据,ID:', this.orderId); | |||
}, | |||
// 确认修改订单 | |||
confirmModify() { | |||
// 这里应该调用API修改订单 | |||
// 示例代码: | |||
/* | |||
const params = { | |||
openId: getOpenIdKey(), | |||
orderId: this.orderId, | |||
reason: this.modifyReason | |||
}; | |||
modifyOrder(params).then(res => { | |||
if (res && res.code === 200) { | |||
uni.showToast({ | |||
title: '订单修改成功', | |||
icon: 'success' | |||
}); | |||
// 修改成功后返回订单详情页 | |||
setTimeout(() => { | |||
uni.navigateBack(); | |||
}, 1500); | |||
} | |||
}).catch(err => { | |||
console.error('修改订单失败', err); | |||
uni.showToast({ | |||
title: '修改订单失败', | |||
icon: 'none' | |||
}); | |||
}); | |||
*/ | |||
// 这里使用模拟数据,显示修改成功提示 | |||
uni.showToast({ | |||
title: '订单修改成功', | |||
icon: 'success' | |||
}); | |||
// 1.5秒后返回订单详情页 | |||
setTimeout(() => { | |||
uni.navigateBack(); | |||
}, 1500); | |||
}, | |||
// 处理取消订单 | |||
handleCancelOrder(orderId) { | |||
this.hideCancelPopup(); | |||
// 这里应该调用API取消订单 | |||
// 示例代码: | |||
/* | |||
const params = { | |||
openId: getOpenIdKey(), | |||
orderId: this.orderId, | |||
reason: this.modifyReason | |||
}; | |||
cancelOrder(params).then(res => { | |||
if (res && res.code === 200) { | |||
uni.showToast({ | |||
title: '订单已取消', | |||
icon: 'success' | |||
}); | |||
// 取消成功后返回订单列表页 | |||
setTimeout(() => { | |||
uni.navigateBack({delta: 2}); | |||
}, 1500); | |||
} | |||
}).catch(err => { | |||
console.error('取消订单失败', err); | |||
}); | |||
*/ | |||
// 这里使用模拟数据,显示取消成功提示 | |||
uni.showToast({ | |||
title: '订单已取消', | |||
icon: 'success' | |||
}); | |||
// 1.5秒后返回订单列表页 | |||
setTimeout(() => { | |||
uni.navigateBack({delta: 2}); | |||
}, 1500); | |||
} | |||
} | |||
}; | |||
</script> | |||
<style lang="scss" scoped> | |||
.order-modify-page { | |||
background-color: #f5f5f5; | |||
min-height: 100vh; | |||
display: flex; | |||
flex-direction: column; | |||
} | |||
.page-header { | |||
background-color: #FFAA48; | |||
padding: 20rpx 30rpx; | |||
color: #FFFFFF; | |||
.header-title { | |||
font-size: 36rpx; | |||
font-weight: bold; | |||
} | |||
} | |||
.order-modify-content { | |||
flex: 1; | |||
padding: 20rpx; | |||
} | |||
.modify-notice { | |||
background-color: #FFF9F0; | |||
border-radius: 20rpx; | |||
padding: 20rpx; | |||
margin-bottom: 20rpx; | |||
.notice-item { | |||
display: flex; | |||
align-items: flex-start; | |||
margin-bottom: 10rpx; | |||
&:last-child { | |||
margin-bottom: 0; | |||
} | |||
.notice-icon { | |||
margin-right: 10rpx; | |||
font-size: 28rpx; | |||
} | |||
.notice-text { | |||
font-size: 26rpx; | |||
color: #666; | |||
line-height: 1.5; | |||
flex: 1; | |||
.highlight-text { | |||
color: #FFAA48; | |||
} | |||
} | |||
} | |||
} | |||
.modify-info-card, .modify-reason-card { | |||
background-color: #FFFFFF; | |||
border-radius: 20rpx; | |||
padding: 30rpx; | |||
margin-bottom: 20rpx; | |||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | |||
} | |||
.card-title { | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #333; | |||
margin-bottom: 20rpx; | |||
display: flex; | |||
align-items: center; | |||
&::before { | |||
content: ''; | |||
display: inline-block; | |||
width: 8rpx; | |||
height: 32rpx; | |||
background-color: #FFAA48; | |||
margin-right: 16rpx; | |||
border-radius: 4rpx; | |||
} | |||
} | |||
.info-content { | |||
.info-item { | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
padding: 20rpx 0; | |||
border-bottom: 1px solid #EEEEEE; | |||
&:last-child { | |||
border-bottom: none; | |||
} | |||
.info-label { | |||
font-size: 28rpx; | |||
color: #666; | |||
} | |||
.info-value { | |||
font-size: 28rpx; | |||
color: #333; | |||
} | |||
&.payment-method { | |||
.payment-value { | |||
display: flex; | |||
align-items: center; | |||
.arrow-right { | |||
margin-left: 10rpx; | |||
color: #999; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.reason-content { | |||
.reason-input { | |||
width: 100%; | |||
height: 200rpx; | |||
background-color: #F8F8F8; | |||
border-radius: 10rpx; | |||
padding: 20rpx; | |||
font-size: 28rpx; | |||
color: #333; | |||
box-sizing: border-box; | |||
} | |||
.word-count { | |||
text-align: right; | |||
margin-top: 10rpx; | |||
text { | |||
font-size: 24rpx; | |||
color: #999; | |||
} | |||
} | |||
} | |||
.order-modify-footer { | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
padding: 20rpx 30rpx; | |||
background-color: #FFFFFF; | |||
border-top: 1px solid #EEEEEE; | |||
.footer-btn { | |||
padding: 16rpx 30rpx; | |||
border-radius: 30rpx; | |||
font-size: 28rpx; | |||
text-align: center; | |||
width: 45%; | |||
} | |||
.cancel-service-btn { | |||
background-color: #F5F5F5; | |||
color: #666; | |||
border: 1px solid #DDDDDD; | |||
} | |||
.confirm-modify-btn { | |||
background-color: #FFAA48; | |||
color: #FFFFFF; | |||
} | |||
} | |||
</style> |
@ -0,0 +1,308 @@ | |||
<template> | |||
<view class="order-review-page"> | |||
<!-- 伴宠师信息区域 --> | |||
<view class="companion-info-card"> | |||
<view class="profile-header"> | |||
<view class="companion-avatar"> | |||
<image :src="companion.avatar" mode="aspectFill"></image> | |||
</view> | |||
<view class="companion-detail"> | |||
<view class="companion-name"> | |||
<text>{{companion.name}}</text> | |||
<image v-if="companion.gender" :src="companion.gender === '男生' ? '/static/images/details/boy.svg' : '/static/images/details/girl.svg'" class="gender-icon"></image> | |||
<view class="companion-tag" v-if="companion.isOfficial"> | |||
<text>初级伴宠师</text> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 评价内容区域 --> | |||
<view class="review-content"> | |||
<!-- 评分 --> | |||
<view class="review-item"> | |||
<text class="review-label">评价星级</text> | |||
<view class="review-stars"> | |||
<uni-rate | |||
v-model="rating" | |||
:size="24" | |||
:value="rating" | |||
:max="5" | |||
:margin="5" | |||
:is-fill="true" | |||
:touchable="true" | |||
@change="ratingChange" | |||
></uni-rate> | |||
</view> | |||
</view> | |||
<!-- 评价内容 --> | |||
<view class="review-item"> | |||
<text class="review-label">评价内容</text> | |||
<view class="review-textarea-box"> | |||
<textarea | |||
class="review-textarea" | |||
v-model="reviewContent" | |||
placeholder="服务态度,态度满意,环境满意" | |||
maxlength="500" | |||
></textarea> | |||
<view class="word-count"> | |||
<text>{{reviewContent.length}}/500</text> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 底部按钮区域 --> | |||
<view class="review-footer"> | |||
<view class="submit-btn" @click="submitReview"> | |||
<text>提交评价</text> | |||
</view> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
import { getOpenIdKey } from '@/utils/auth' | |||
export default { | |||
data() { | |||
return { | |||
orderId: null, | |||
rating: 5, | |||
reviewContent: '', | |||
companion: { | |||
name: '宠小二', | |||
avatar: '/static/images/personal/pet.png', | |||
isOfficial: true, | |||
gender: '女生' | |||
} | |||
}; | |||
}, | |||
onLoad(options) { | |||
if (options.id) { | |||
this.orderId = options.id; | |||
this.getOrderInfo(); | |||
} | |||
}, | |||
methods: { | |||
// 获取订单信息 | |||
getOrderInfo() { | |||
// 实际项目中应调用API获取订单详情和伴宠师信息 | |||
// 示例代码: | |||
/* | |||
const params = { | |||
openId: getOpenIdKey(), | |||
orderId: this.orderId | |||
}; | |||
getOrderDetail(params).then(res => { | |||
if (res && res.code === 200) { | |||
this.companion = res.data.companion; | |||
} | |||
}).catch(err => { | |||
console.error('获取订单详情失败', err); | |||
}); | |||
*/ | |||
// 这里使用模拟数据 | |||
console.log('获取订单详情,ID:', this.orderId); | |||
}, | |||
// 评分变化 | |||
ratingChange(e) { | |||
this.rating = e.value; | |||
}, | |||
// 提交评价 | |||
submitReview() { | |||
if (this.rating === 0) { | |||
uni.showToast({ | |||
title: '请选择评分', | |||
icon: 'none' | |||
}); | |||
return; | |||
} | |||
if (!this.reviewContent.trim()) { | |||
uni.showToast({ | |||
title: '请输入评价内容', | |||
icon: 'none' | |||
}); | |||
return; | |||
} | |||
// 实际项目中应调用API提交评价 | |||
// 示例代码: | |||
/* | |||
const params = { | |||
openId: getOpenIdKey(), | |||
orderId: this.orderId, | |||
rating: this.rating, | |||
content: this.reviewContent | |||
}; | |||
submitOrderReview(params).then(res => { | |||
if (res && res.code === 200) { | |||
uni.showToast({ | |||
title: '评价成功', | |||
icon: 'success' | |||
}); | |||
// 评价成功后返回订单列表页 | |||
setTimeout(() => { | |||
uni.navigateBack(); | |||
}, 1500); | |||
} | |||
}).catch(err => { | |||
console.error('提交评价失败', err); | |||
}); | |||
*/ | |||
// 这里使用模拟数据,显示评价成功提示 | |||
uni.showToast({ | |||
title: '评价成功', | |||
icon: 'success' | |||
}); | |||
// 1.5秒后返回订单列表页 | |||
setTimeout(() => { | |||
uni.navigateBack(); | |||
}, 1500); | |||
} | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.order-review-page { | |||
background: linear-gradient(180deg, #FFBF60 0%, #FFF5E6 20%, #FFFFFF 50%); | |||
min-height: 100vh; | |||
display: flex; | |||
flex-direction: column; | |||
padding-bottom: 120rpx; | |||
} | |||
.companion-info-card { | |||
padding: 30rpx; | |||
margin-bottom: 20rpx; | |||
background-color: transparent; | |||
.profile-header { | |||
display: flex; | |||
align-items: center; | |||
} | |||
.companion-avatar { | |||
width: 100rpx; | |||
height: 100rpx; | |||
border-radius: 50%; | |||
overflow: hidden; | |||
margin-right: 20rpx; | |||
border: 2rpx solid #FFFFFF; | |||
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1); | |||
image { | |||
width: 100%; | |||
height: 100%; | |||
} | |||
} | |||
.companion-detail { | |||
flex: 1; | |||
.companion-name { | |||
display: flex; | |||
align-items: center; | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #333; | |||
.gender-icon { | |||
width: 32rpx; | |||
height: 32rpx; | |||
margin-left: 10rpx; | |||
} | |||
.companion-tag { | |||
background-color: #FF9500; | |||
color: #FFFFFF; | |||
font-size: 20rpx; | |||
padding: 4rpx 10rpx; | |||
border-radius: 20rpx; | |||
margin-left: 16rpx; | |||
} | |||
} | |||
} | |||
} | |||
.review-content { | |||
background-color: #FFFFFF; | |||
padding: 30rpx; | |||
border-radius: 16rpx 16rpx 0 0; | |||
margin-top: 20rpx; | |||
.review-item { | |||
margin-bottom: 40rpx; | |||
.review-label { | |||
font-size: 28rpx; | |||
color: #333; | |||
margin-bottom: 20rpx; | |||
display: block; | |||
font-weight: bold; | |||
} | |||
.review-stars { | |||
display: flex; | |||
align-items: center; | |||
} | |||
.review-textarea-box { | |||
position: relative; | |||
.review-textarea { | |||
width: 100%; | |||
height: 200rpx; | |||
background-color: #F7F7F7; | |||
border-radius: 12rpx; | |||
padding: 20rpx; | |||
font-size: 28rpx; | |||
color: #666; | |||
box-sizing: border-box; | |||
} | |||
.word-count { | |||
position: absolute; | |||
bottom: 10rpx; | |||
right: 20rpx; | |||
font-size: 24rpx; | |||
color: #999; | |||
} | |||
} | |||
} | |||
} | |||
.review-footer { | |||
position: fixed; | |||
bottom: 0; | |||
left: 0; | |||
right: 0; | |||
background-color: #FFFFFF; | |||
padding: 20rpx 30rpx; | |||
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05); | |||
.submit-btn { | |||
background-color: #FFAA48; | |||
color: #FFFFFF; | |||
height: 88rpx; | |||
line-height: 88rpx; | |||
border-radius: 44rpx; | |||
text-align: center; | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
box-shadow: 0 4rpx 8rpx rgba(255, 170, 72, 0.3); | |||
} | |||
} | |||
</style> |