Browse Source

feat(订单): 添加快捷下单语音播放组件和订单状态显示

新增VoicePlayer组件实现语音播放功能,优化订单页面显示快捷下单内容
在订单确认页添加语音播放和图片预览功能,增加已下单状态显示
修复分类页和订单页的快捷下单刷新逻辑,更新环境配置为生产环境
master
前端-胡立永 1 week ago
parent
commit
4e1c3b1329
11 changed files with 323 additions and 12 deletions
  1. +189
    -0
      components/VoicePlayer.vue
  2. +1
    -1
      config.js
  3. +7
    -1
      mixins/order.js
  4. +5
    -3
      pages/index/category.vue
  5. +1
    -0
      pages/index/center.vue
  6. +1
    -0
      pages/index/index.vue
  7. +1
    -0
      pages/index/order.vue
  8. +5
    -0
      pages_order/auth/wxLogin.vue
  9. +4
    -1
      pages_order/mine/purse.vue
  10. +107
    -4
      pages_order/order/firmOrder.vue
  11. +2
    -2
      utils/position.js

+ 189
- 0
components/VoicePlayer.vue View File

@ -0,0 +1,189 @@
<template>
<view class="voice-content">
<view class="voice-item">
<view class="voice-icon">
<uv-icon name="mic" size="40rpx" color="#D03F25"></uv-icon>
</view>
<view class="voice-info">
<text class="voice-desc">通过语音识别下单</text>
<text class="voice-tip">点击播放语音内容</text>
</view>
<view class="voice-play" @click="playVoice">
<uv-icon :name="isPlaying ? 'pause-circle-fill' : 'play-circle-fill'" size="50rpx" color="#D03F25"></uv-icon>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'VoicePlayer',
props: {
voiceUrl: {
type: String,
default: ''
},
autoPlay: {
type: Boolean,
default: false
}
},
data() {
return {
innerAudioContext: null, //
isPlaying: false //
};
},
mounted() {
this.initAudioPlayer();
},
beforeDestroy() {
this.destroyAudioPlayer();
},
methods: {
//
initAudioPlayer() {
this.innerAudioContext = uni.createInnerAudioContext();
//
this.innerAudioContext.onEnded(() => {
console.log('播放结束');
this.isPlaying = false;
this.$emit('playEnded');
});
//
this.innerAudioContext.onPlay(() => {
console.log('开始播放');
this.isPlaying = true;
this.$emit('playStarted');
});
//
this.innerAudioContext.onPause(() => {
console.log('播放暂停');
this.isPlaying = false;
this.$emit('playPaused');
});
//
this.innerAudioContext.onStop(() => {
console.log('播放停止');
this.isPlaying = false;
this.$emit('playStopped');
});
//
this.innerAudioContext.onError((err) => {
console.error('播放错误', err);
uni.showToast({
title: '播放失败',
icon: 'none'
});
this.isPlaying = false;
this.$emit('playError', err);
});
},
//
destroyAudioPlayer() {
if (this.innerAudioContext) {
this.innerAudioContext.destroy();
this.innerAudioContext = null;
}
},
//
playVoice() {
if (!this.voiceUrl) {
uni.showToast({
title: '语音文件不存在',
icon: 'none'
});
return;
}
if (this.isPlaying) {
//
this.innerAudioContext.stop();
this.isPlaying = false;
return;
}
//
this.innerAudioContext.src = this.voiceUrl;
this.innerAudioContext.play();
uni.showToast({
title: '正在播放语音',
icon: 'none'
});
},
//
stopPlay() {
if (this.innerAudioContext && this.isPlaying) {
this.innerAudioContext.stop();
}
},
//
pausePlay() {
if (this.innerAudioContext && this.isPlaying) {
this.innerAudioContext.pause();
}
}
}
}
</script>
<style scoped lang="scss">
.voice-content {
padding: 30rpx;
.voice-item {
display: flex;
align-items: center;
padding: 30rpx;
background-color: #f9f9f9;
border-radius: 12rpx;
.voice-icon {
width: 80rpx;
height: 80rpx;
background-color: rgba(208, 63, 37, 0.1);
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 30rpx;
}
.voice-info {
flex: 1;
.voice-desc {
display: block;
font-size: 30rpx;
color: #333;
margin-bottom: 8rpx;
}
.voice-tip {
font-size: 24rpx;
color: #999;
}
}
.voice-play {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(208, 63, 37, 0.1);
border-radius: 40rpx;
}
}
}
</style>

+ 1
- 1
config.js View File

@ -7,7 +7,7 @@ import uvUI from '@/uni_modules/uv-ui-tools'
Vue.use(uvUI);
// 当前环境
const type = 'local'
const type = 'prod'
// 环境配置


+ 7
- 1
mixins/order.js View File

@ -23,7 +23,13 @@ export default {
type : 1,
}, res => {
if(res.code == 200){
uni.requestPaymentWxPay(res)
if(res.result.id){
this.id = res.result.id
}
this.getData()
uni.requestPaymentWxPay({
result : res.result.pay || res.result
})
.then(res => {
uni.showToast({
title: '支付成功',


+ 5
- 3
pages/index/category.vue View File

@ -92,14 +92,16 @@
return {}
}
return this.category[this.current].children[this.currentChildren]
return this.category[this.current].children[this.currentChildren] || this.category[this.current]
},
},
onLoad() {
this.getCategory()
},
onShow() {
this.$refs.quickOrderEntry.refresh();
if(uni.getStorageSync('token')){
this.$refs.quickOrderEntry.refresh();
}
},
methods: {
clickTabs({index}){
@ -137,7 +139,7 @@
},
change(index) {
this.currentChildren = index;
if(this.category[index]) {
if(this.selectCategory.id) {
this.$nextTick(() => {
this.getProductList()
})


+ 1
- 0
pages/index/center.vue View File

@ -163,6 +163,7 @@
if(uni.getStorageSync('token')){
//
this.$store.commit('getUserInfo')
this.$refs.quickOrderEntry.refresh()
}
},
methods: {


+ 1
- 0
pages/index/index.vue View File

@ -119,6 +119,7 @@ export default {
this.getMenu()
if(uni.getStorageSync('token')){
this.$store.commit('getUserInfo')
this.$refs.quickOrderEntry.refresh()
}
},
methods: {


+ 1
- 0
pages/index/order.vue View File

@ -115,6 +115,7 @@ export default {
},
onShow() {
this.getData()
this.$refs.quickOrderEntry.refresh()
},
//
onReachBottom() {


+ 5
- 0
pages_order/auth/wxLogin.vue View File

@ -58,6 +58,11 @@
checkboxValue : []
}
},
onLoad(query) {
if (query.shareId) {
uni.setStorageSync('shareId', query.shareId)
}
},
methods: {
qux(){
uni.reLaunch({


+ 4
- 1
pages_order/mine/purse.vue View File

@ -75,8 +75,11 @@
<input placeholder="请输入银行卡卡号" />
</view> -->
<view class="mt56">提现说明</view>
<view style="line-height: 45rpx; font-size: 24rpx;color: #666666;" v-html="notice">
<view style="color: #777;font-size: 26rpx;">
<uv-parse :content="configList.withdrawal_instructions"></uv-parse>
</view>
<!-- <p>1本次提现必须通过银行卡提现暂不支持其他途径</p>
<p>2如若遇到24小时提现未到账请联系客服</p> -->
</view>


+ 107
- 4
pages_order/order/firmOrder.vue View File

@ -9,6 +9,30 @@
<!-- 商品信息 -->
<view class="content-wrapper">
<!-- 快捷下单内容展示 -->
<view class="quick-order-content" v-if="quickOrderContent.type">
<view class="section-wrapper">
<view class="section-title">
<view class="title-bar"></view>
<text>{{getOrderTypeName(quickOrderContent.type)}}</text>
</view>
<!-- 拍照订单显示图片 -->
<view class="photo-content" v-if="quickOrderContent.type === '0' && quickOrderContent.imageUrl">
<image :src="quickOrderContent.imageUrl" mode="aspectFill" class="order-image" @click="previewImage"></image>
</view>
<!-- 语音订单显示语音信息 -->
<voice-player
v-if="quickOrderContent.type === '2' && quickOrderContent.voiceUrl"
:voiceUrl="quickOrderContent.voiceUrl"
@playStarted="onVoicePlayStarted"
@playEnded="onVoicePlayEnded"
@playError="onVoicePlayError"
></voice-player>
</view>
</view>
<uv-checkbox-group
shape="circle"
v-model="checkboxValue">
@ -93,10 +117,14 @@
</view>
<!-- 底部按钮 -->
<view class="order-submit">
<view class="order-submit" v-ifan class="s">="productInfo.isPay != 'Y'">
<button class="submit-btn-close" @click="cancelOrderFast">取消</button>
<button class="submit-btn" @click="submitOrder">快捷下单{{ totalPrice }}</button>
</view>
<view class="order-submit" v-else>
<button class="submit-btn" style="background-color: #999999; color: #cccccc;">已下单</button>
</view>
<!-- 地址选择弹窗 -->
<uv-popup ref="addressPopup" :round="30" style="padding-bottom: 90rpx;">
@ -113,10 +141,12 @@
<script>
import redactAddressForm from '../components/address/redactAddressForm.vue';
import addressList from '../components/address/addressList.vue';
import VoicePlayer from '@/components/VoicePlayer.vue';
export default {
components: {
redactAddressForm,
addressList
addressList,
VoicePlayer
},
data() {
return {
@ -133,7 +163,12 @@
addressTotal: 0,
orderInfo: [],
isLoading: false, //
shouldSubmitOrder: false //
shouldSubmitOrder: false, //
quickOrderContent: { //
type: '',
imageUrl: '',
voiceUrl: ''
}
};
},
computed: {
@ -161,6 +196,7 @@
//
this.getAddressList();
},
methods: {
//
getOrderInfo() {
@ -197,6 +233,13 @@
console.log(res.result);
this.productInfo = res.result;
//
this.quickOrderContent = {
type: res.result.type || '',
imageUrl: res.result.imageUrl || '',
voiceUrl: res.result.voiceUrl || ''
};
}
});
},
@ -335,6 +378,7 @@
this.$api('createSumOrder', data, res => {
uni.hideLoading();
if (res.code === 200) {
this.productInfo.isPay = 'Y'
uni.$emit('getQuickOrderInfo')
uni.requestPaymentWxPay(res)
.then(e => {
@ -383,13 +427,52 @@
}
})
},
//
getOrderTypeName(type) {
const typeMap = {
'0': '拍照下单',
'2': '语音下单'
};
return typeMap[type] || '快捷下单';
},
//
previewImage() {
if (!this.quickOrderContent.imageUrl) {
uni.showToast({
title: '图片不存在',
icon: 'none'
});
return;
}
uni.previewImage({
current: this.quickOrderContent.imageUrl,
urls: [this.quickOrderContent.imageUrl]
});
},
//
onVoicePlayStarted() {
console.log('语音播放开始');
},
//
onVoicePlayEnded() {
console.log('语音播放结束');
},
//
onVoicePlayError(error) {
console.error('语音播放错误', error);
},
}
}
</script>
<style scoped lang="scss">
.hand-firm {
min-height: 100vh;
background-color: #f6f6f6;
padding-bottom: 120rpx;
@ -598,5 +681,25 @@
border-radius: 45rpx;
}
}
.quick-order-content {
.photo-content {
padding: 30rpx;
text-align: center;
.order-image {
width: 200rpx;
height: 200rpx;
border-radius: 12rpx;
margin-bottom: 20rpx;
background-color: #f9f9f9;
}
.image-desc {
font-size: 26rpx;
color: #666;
}
}
}
}
</style>

+ 2
- 2
utils/position.js View File

@ -144,8 +144,8 @@ function getUserAddressByIp(key) { //根据IP获取当前用户位置
//打开地图让用户选择位置
function selectAddress(longitude, latitude, successCallback) {
uni.chooseLocation({
longitude, //经度
latitude, //纬度
// longitude, //经度
// latitude, //纬度
success: function(res) {
successCallback && successCallback(res)
}


Loading…
Cancel
Save