908 lines
25 KiB
Vue
908 lines
25 KiB
Vue
<template>
|
||
<view class="page">
|
||
<u-navbar title="代客下单" :border-bottom="false" :background="bgc" title-color="#2E4975" title-size="36"
|
||
back-icon-color="#2E4975" height="45"></u-navbar>
|
||
|
||
<scroll-view scroll-y class="scroll-body">
|
||
<view class="scroll-inner">
|
||
<!-- 车辆信息 -->
|
||
<view class="vehicle-wrap" v-if="deviceInfo" hover-class="app-tap-hover" @click="openDeviceSelector">
|
||
<view class="change-device-icon">
|
||
<u-icon name="reload" color="#4C97E7" size="28"></u-icon>
|
||
</view>
|
||
<VehicleCard :bikeobj="deviceInfo" :iconobj="iconobj" />
|
||
</view>
|
||
<view class="device-loading" v-else-if="deviceLoading">
|
||
<u-loading mode="circle" size="40" color="#4C97E7"></u-loading>
|
||
<text>车辆信息加载中...</text>
|
||
</view>
|
||
|
||
<CustomerSearchCard
|
||
:phone-number="phoneNumber"
|
||
:target-user="targetUser"
|
||
@update:phoneNumber="phoneNumber = $event"
|
||
@search="searchUser"
|
||
/>
|
||
|
||
<block v-if="targetUser">
|
||
<!-- 套餐选择(与用户端一致) -->
|
||
<view class="package-section">
|
||
<view class="section-head">
|
||
<text class="section-title">选择套餐</text>
|
||
</view>
|
||
<view class="package-wrap" v-if="suitList.length > 0">
|
||
<PackageSelector
|
||
:taocanlist="suitList"
|
||
:fanganindex="fanganindex"
|
||
:bikeobj="packageBikeobj"
|
||
:actiobj="packageActiobj"
|
||
:instructions="packageInstructions"
|
||
@select="onPackageSelect"
|
||
/>
|
||
</view>
|
||
<view class="empty-tip" v-else>暂无可用套餐</view>
|
||
</view>
|
||
|
||
<PayChannelSelector
|
||
:channel-list="channelList"
|
||
:selected-channel-id="selectedChannelId"
|
||
@select="selectChannel"
|
||
/>
|
||
|
||
<InsuranceSelector
|
||
:insurance-device-current="insuranceDeviceCurrent"
|
||
:selected-insurance-id="selectedInsuranceId"
|
||
@toggle="toggleInsurance"
|
||
@skip="skipInsurance"
|
||
/>
|
||
|
||
<view class="scroll-bottom-spacer"></view>
|
||
</block>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<AgentOrderFooter
|
||
v-if="targetUser"
|
||
:display-deposit="displayDeposit"
|
||
:selected-suit-id="selectedSuitId"
|
||
:submitting="submitting"
|
||
@open-detail="openPriceDetail"
|
||
@submit="submitOrder"
|
||
/>
|
||
|
||
<DeviceSelectorPopup
|
||
:visible="deviceSelectorShow"
|
||
:area-id="areaId"
|
||
:current-device-id="deviceId"
|
||
@update:visible="deviceSelectorShow = $event"
|
||
@select="switchDevice"
|
||
/>
|
||
|
||
<AgentOrderConfirmModal
|
||
:visible="showConfirmModal"
|
||
:target-user="targetUser"
|
||
:selected-suit="selectedSuit"
|
||
:display-deposit="displayDeposit"
|
||
:selected-channel-obj="selectedChannelObj"
|
||
:price-info="priceInfo"
|
||
@close="closeConfirmModal"
|
||
@confirm="confirmScanOrder"
|
||
/>
|
||
|
||
<PriceDetailPopup
|
||
:visible="priceDetailShow"
|
||
:price-info="priceInfo"
|
||
:display-deposit="displayDeposit"
|
||
@update:visible="priceDetailShow = $event"
|
||
/>
|
||
|
||
<BluetoothProcessPanel
|
||
:visible="btProcessVisible"
|
||
:progress="btProgress"
|
||
:step-message="btStepMessage"
|
||
:error-message="btErrorMessage"
|
||
:error-type="btErrorType"
|
||
@close="btProcessVisible = false"
|
||
@go-phone-setting="goPhoneBluetoothSetting"
|
||
@go-wechat-auth="openWechatBluetoothAuthSetting"
|
||
/>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { ChannelApiType } from '@/common/enums/channel';
|
||
import PackageSelector from '@/components/order/PackageSelector.vue';
|
||
import VehicleCard from '@/components/order/VehicleCard.vue';
|
||
import AgentOrderConfirmModal from './components/AgentOrderConfirmModal.vue';
|
||
import AgentOrderFooter from './components/AgentOrderFooter.vue';
|
||
import CustomerSearchCard from './components/CustomerSearchCard.vue';
|
||
import DeviceSelectorPopup from './components/DeviceSelectorPopup.vue';
|
||
import InsuranceSelector from './components/InsuranceSelector.vue';
|
||
import PayChannelSelector from './components/PayChannelSelector.vue';
|
||
import PriceDetailPopup from './components/PriceDetailPopup.vue';
|
||
import BluetoothProcessPanel from './components/BluetoothProcessPanel.vue';
|
||
import merchantDeviceUnlockMixin from './mixins/merchantDeviceUnlockMixin.js';
|
||
|
||
export default {
|
||
mixins: [merchantDeviceUnlockMixin],
|
||
components: {
|
||
PackageSelector,
|
||
VehicleCard,
|
||
AgentOrderConfirmModal,
|
||
AgentOrderFooter,
|
||
CustomerSearchCard,
|
||
DeviceSelectorPopup,
|
||
InsuranceSelector,
|
||
PayChannelSelector,
|
||
PriceDetailPopup,
|
||
BluetoothProcessPanel
|
||
},
|
||
data() {
|
||
return {
|
||
bgc: { backgroundColor: '#EEF3FA' },
|
||
deviceId: '',
|
||
areaId: '',
|
||
deviceInfo: null,
|
||
deviceLoading: false,
|
||
iconobj: {},
|
||
phoneNumber: '',
|
||
targetUser: null,
|
||
suitList: [],
|
||
fanganindex: 0,
|
||
channelList: [],
|
||
/** /app/insuranceDevice/current 有 data 时非空,与用户端一致 */
|
||
insuranceDeviceCurrent: null,
|
||
selectedSuitId: null,
|
||
selectedSuit: null,
|
||
selectedChannelId: null,
|
||
selectedInsuranceId: null,
|
||
priceInfo: null,
|
||
submitting: false,
|
||
priceDetailShow: false,
|
||
showConfirmModal: false,
|
||
deviceSelectorShow: false,
|
||
deviceLoadSeq: 0,
|
||
priceCalcSeq: 0,
|
||
offlinePayNo: '',
|
||
pendingOfflineAudit: false,
|
||
offlineAuditHandled: false
|
||
}
|
||
},
|
||
computed: {
|
||
selectedChannelObj() {
|
||
if (!this.selectedChannelId) return null
|
||
return this.channelList.find(c => c.id === this.selectedChannelId) || null
|
||
},
|
||
displayDeposit() {
|
||
if (this.priceInfo && this.priceInfo.depositPrice != null) {
|
||
let total = Number(this.priceInfo.depositPrice) || 0
|
||
if (this.priceInfo.insureFee) {
|
||
total += Number(this.priceInfo.insureFee) || 0
|
||
}
|
||
return total
|
||
}
|
||
return '--'
|
||
},
|
||
packageBikeobj() {
|
||
return this.deviceInfo || { areaId: this.areaId }
|
||
},
|
||
packageActiobj() {
|
||
const suit = this.selectedSuit
|
||
return {
|
||
freeRideTime: suit ? suit.freeRideTime : null
|
||
}
|
||
},
|
||
packageInstructions() {
|
||
return this.selectedSuit && this.selectedSuit.instructions ? this.selectedSuit.instructions : ''
|
||
}
|
||
},
|
||
onLoad(e) {
|
||
this.deviceId = e.deviceId || ''
|
||
this.areaId = e.areaId || ''
|
||
this.iconobj = this.$store.state.iconobj || {}
|
||
this.loadDevice()
|
||
},
|
||
onShow() {
|
||
this.handleOfflineAuditReturn()
|
||
},
|
||
methods: {
|
||
readOfflineAuditResult() {
|
||
let result = uni.getStorageSync('agentOrderOfflineResult')
|
||
if (!result) return null
|
||
if (typeof result === 'string') {
|
||
try {
|
||
result = JSON.parse(result)
|
||
} catch (e) {
|
||
return null
|
||
}
|
||
}
|
||
return result && typeof result === 'object' ? result : null
|
||
},
|
||
handleOfflineAuditReturn() {
|
||
if (!this.pendingOfflineAudit) {
|
||
uni.removeStorageSync('agentOrderOfflineResult')
|
||
return
|
||
}
|
||
const result = this.readOfflineAuditResult()
|
||
if (!result) return
|
||
this.processOfflineAuditResult(result)
|
||
},
|
||
processOfflineAuditResult(result) {
|
||
if (!result || this.offlineAuditHandled) return
|
||
this.offlineAuditHandled = true
|
||
this.pendingOfflineAudit = false
|
||
uni.removeStorageSync('agentOrderOfflineResult')
|
||
if (result.pass) {
|
||
const payNo = result.payNo || this.offlinePayNo
|
||
this.offlinePayNo = payNo || this.offlinePayNo
|
||
if (payNo) {
|
||
this.submitting = true
|
||
uni.showLoading({ title: '确认支付结果...', mask: true })
|
||
this.pollPayResult(payNo)
|
||
return
|
||
}
|
||
this.handlePaymentSuccess()
|
||
return
|
||
}
|
||
this.submitting = false
|
||
this.offlinePayNo = ''
|
||
if (result.rejected) {
|
||
uni.showToast({ title: '线下支付审核已驳回', icon: 'none', duration: 2500 })
|
||
return
|
||
}
|
||
if (result.canceled) {
|
||
uni.showToast({ title: '已取消线下支付审核', icon: 'none', duration: 2000 })
|
||
}
|
||
},
|
||
// 加载车辆信息
|
||
loadDevice() {
|
||
if (!this.deviceId) return
|
||
const seq = ++this.deviceLoadSeq
|
||
this.deviceLoading = true
|
||
this.deviceInfo = null
|
||
this.resetDeviceRelatedState()
|
||
this.$u.get(`/bst/device?id=${this.deviceId}`).then(res => {
|
||
if (seq !== this.deviceLoadSeq) return
|
||
this.deviceLoading = false
|
||
if (res.code === 200 && res.data) {
|
||
this.deviceInfo = res.data
|
||
if (res.data.areaId) {
|
||
this.areaId = res.data.areaId
|
||
}
|
||
this.loadSuits()
|
||
this.fetchInsuranceDeviceCurrent()
|
||
this.loadChannels()
|
||
}
|
||
}).catch(() => {
|
||
if (seq !== this.deviceLoadSeq) return
|
||
this.deviceLoading = false
|
||
uni.showToast({ title: '车辆信息加载失败', icon: 'none', duration: 2000 })
|
||
})
|
||
},
|
||
resetDeviceRelatedState() {
|
||
this.suitList = []
|
||
this.fanganindex = 0
|
||
this.channelList = []
|
||
this.insuranceDeviceCurrent = null
|
||
this.selectedSuitId = null
|
||
this.selectedSuit = null
|
||
this.selectedChannelId = null
|
||
this.selectedInsuranceId = null
|
||
this.priceInfo = null
|
||
this.priceCalcSeq++
|
||
this.priceDetailShow = false
|
||
this.showConfirmModal = false
|
||
},
|
||
openDeviceSelector() {
|
||
if (this.submitting) return
|
||
this.deviceSelectorShow = true
|
||
},
|
||
switchDevice(device) {
|
||
const id = device && (device.id != null ? device.id : device.deviceId)
|
||
if (id === undefined || id === null || String(id).trim() === '') {
|
||
uni.showToast({ title: '车辆信息异常', icon: 'none', duration: 2000 })
|
||
return
|
||
}
|
||
if (String(id) === String(this.deviceId)) {
|
||
return
|
||
}
|
||
this.deviceId = id
|
||
if (device.areaId) {
|
||
this.areaId = device.areaId
|
||
}
|
||
this.loadDevice()
|
||
},
|
||
// 按车型加载套餐(与用户端一致)
|
||
loadSuits() {
|
||
const seq = this.deviceLoadSeq
|
||
const modelId = this.deviceInfo && this.deviceInfo.modelId
|
||
if (!modelId) {
|
||
this.suitList = []
|
||
this.selectedSuitId = null
|
||
this.selectedSuit = null
|
||
this.priceInfo = null
|
||
return
|
||
}
|
||
this.$u.get(`/app/suit/listByModel?temp=false&modelId=${modelId}`).then(res => {
|
||
if (seq !== this.deviceLoadSeq) return
|
||
if (res.code === 200) {
|
||
const list = res.data || []
|
||
this.suitList = this.normalizeTaocanList(list)
|
||
if (this.suitList.length > 0) {
|
||
this.fanganindex = 0
|
||
this.selectSuit(this.suitList[0])
|
||
} else {
|
||
this.selectedSuitId = null
|
||
this.selectedSuit = null
|
||
this.priceInfo = null
|
||
}
|
||
}
|
||
})
|
||
},
|
||
// 加载支付渠道列表(scene=MCH 商户场景)
|
||
loadChannels() {
|
||
const seq = this.deviceLoadSeq
|
||
const appId = this.$store.state.appid || ''
|
||
let url = `/app/channel/list?scene=MCH&bstType=1&appType=1&appId=${appId}`
|
||
if (this.areaId) url += `&areaId=${this.areaId}`
|
||
this.$u.get(url).then(res => {
|
||
if (seq !== this.deviceLoadSeq) return
|
||
if (res.code === 200) {
|
||
this.channelList = res.data || []
|
||
if (this.channelList.length > 0) {
|
||
this.selectChannel(this.channelList[0])
|
||
} else {
|
||
this.selectedChannelId = null
|
||
this.priceInfo = null
|
||
}
|
||
}
|
||
})
|
||
},
|
||
// 加载当前设备保险(与用户端一致)
|
||
fetchInsuranceDeviceCurrent() {
|
||
const seq = this.deviceLoadSeq
|
||
const raw = this.deviceInfo
|
||
if (!raw || typeof raw !== 'object') {
|
||
this.insuranceDeviceCurrent = null
|
||
this.selectedInsuranceId = null
|
||
return
|
||
}
|
||
const did = raw.id != null && raw.id !== '' ? raw.id : raw.deviceId
|
||
if (did === undefined || did === null || String(did).trim() === '') {
|
||
this.insuranceDeviceCurrent = null
|
||
this.selectedInsuranceId = null
|
||
return
|
||
}
|
||
this.insuranceDeviceCurrent = null
|
||
this.selectedInsuranceId = null
|
||
this.$u
|
||
.get(`/app/insuranceDevice/current?deviceId=${encodeURIComponent(String(did))}`)
|
||
.then((res) => {
|
||
if (seq !== this.deviceLoadSeq) return
|
||
if (res.code != 200) {
|
||
this.insuranceDeviceCurrent = null
|
||
this.selectedInsuranceId = null
|
||
return
|
||
}
|
||
if (!this._insuranceCurrentDataHasValue(res.data)) {
|
||
this.insuranceDeviceCurrent = null
|
||
this.selectedInsuranceId = null
|
||
return
|
||
}
|
||
this.insuranceDeviceCurrent = res.data
|
||
this.selectedInsuranceId = null
|
||
})
|
||
.catch(() => {
|
||
if (seq !== this.deviceLoadSeq) return
|
||
this.insuranceDeviceCurrent = null
|
||
this.selectedInsuranceId = null
|
||
})
|
||
},
|
||
_insuranceCurrentDataHasValue(d) {
|
||
if (d === null || d === undefined) return false
|
||
if (typeof d === 'string') return d.trim() !== ''
|
||
if (typeof d === 'number' || typeof d === 'boolean') return true
|
||
if (Array.isArray(d)) return d.length > 0
|
||
if (typeof d === 'object') return Object.keys(d).length > 0
|
||
return false
|
||
},
|
||
// 预计算价格(套餐/渠道/保险变更后调用)
|
||
calculatePrice() {
|
||
if (!this.selectedSuitId || !this.selectedChannelId) {
|
||
this.priceInfo = null
|
||
return
|
||
}
|
||
const seq = ++this.priceCalcSeq
|
||
const appId = this.$store.state.appid || ''
|
||
const data = {
|
||
suitId: this.selectedSuitId,
|
||
channelId: this.selectedChannelId,
|
||
appId
|
||
}
|
||
if (this.selectedInsuranceId) {
|
||
data.insuranceDeviceId = this.selectedInsuranceId
|
||
}
|
||
this.$u.post('/app/order/calculatePrice', data).then(res => {
|
||
if (seq !== this.priceCalcSeq) return
|
||
if (res.code === 200) {
|
||
this.priceInfo = res.data
|
||
}
|
||
})
|
||
},
|
||
// 查询客户信息
|
||
searchUser() {
|
||
if (!this.phoneNumber || this.phoneNumber.length !== 11) {
|
||
uni.showToast({ title: '请输入正确的11位手机号', icon: 'none', duration: 2000 })
|
||
return
|
||
}
|
||
uni.showLoading({ title: '查询中...', mask: true })
|
||
this.$u.get(`/app/user/getByUserName?userName=${this.phoneNumber}`).then(res => {
|
||
uni.hideLoading()
|
||
if (res.code === 200 && res.data) {
|
||
this.targetUser = res.data
|
||
uni.showToast({ title: '查询成功', icon: 'success', duration: 1500 })
|
||
} else {
|
||
this.targetUser = null
|
||
uni.showToast({ title: '未找到该手机号对应用户', icon: 'none', duration: 2000 })
|
||
}
|
||
}).catch(() => {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '查询失败,请重试', icon: 'none', duration: 2000 })
|
||
})
|
||
},
|
||
parseRuleValue(ruleValue) {
|
||
if (!ruleValue) return null
|
||
if (typeof ruleValue === 'object') return ruleValue
|
||
if (typeof ruleValue !== 'string') return null
|
||
try {
|
||
return JSON.parse(ruleValue)
|
||
} catch (e) {
|
||
return null
|
||
}
|
||
},
|
||
normalizeTaocanList(list) {
|
||
return (list || []).map((item) => {
|
||
const ridingRule = Number(item.ridingRule || 1)
|
||
const parsedRule = this.parseRuleValue(item.rule)
|
||
const normalized = { ...item, ridingRule }
|
||
if (ridingRule === 1) {
|
||
normalized.startRule = parsedRule && !Array.isArray(parsedRule)
|
||
? parsedRule
|
||
: (this.parseRuleValue(item.startRule) || item.startRule || {})
|
||
normalized.intervalRule = []
|
||
normalized.deadlineRule = null
|
||
} else if (ridingRule === 2) {
|
||
normalized.intervalRule = Array.isArray(parsedRule)
|
||
? parsedRule
|
||
: (this.parseRuleValue(item.intervalRule) || item.intervalRule || [])
|
||
normalized.startRule = {}
|
||
normalized.deadlineRule = null
|
||
} else if (ridingRule === 3) {
|
||
normalized.deadlineRule = parsedRule && !Array.isArray(parsedRule)
|
||
? parsedRule
|
||
: {}
|
||
normalized.startRule = {}
|
||
normalized.intervalRule = []
|
||
} else {
|
||
normalized.startRule = {}
|
||
normalized.intervalRule = []
|
||
normalized.deadlineRule = null
|
||
}
|
||
return normalized
|
||
})
|
||
},
|
||
onPackageSelect(index, item) {
|
||
this.fanganindex = index
|
||
this.selectSuit(item)
|
||
},
|
||
openPriceDetail() {
|
||
if (!this.selectedSuitId) {
|
||
uni.showToast({ title: '请先选择套餐', icon: 'none', duration: 2000 })
|
||
return
|
||
}
|
||
this.priceDetailShow = true
|
||
},
|
||
// 选择套餐
|
||
selectSuit(item) {
|
||
this.selectedSuitId = item.id
|
||
this.selectedSuit = item
|
||
const idx = this.suitList.findIndex(s => s.id === item.id)
|
||
if (idx >= 0) {
|
||
this.fanganindex = idx
|
||
}
|
||
this.calculatePrice()
|
||
},
|
||
// 选择支付渠道
|
||
selectChannel(item) {
|
||
this.selectedChannelId = item.id
|
||
this.calculatePrice()
|
||
},
|
||
// 切换保险选中状态
|
||
toggleInsurance() {
|
||
if (!this.insuranceDeviceCurrent) return
|
||
if (this.selectedInsuranceId === this.insuranceDeviceCurrent.id) {
|
||
this.selectedInsuranceId = null
|
||
} else {
|
||
this.selectedInsuranceId = this.insuranceDeviceCurrent.id
|
||
}
|
||
this.calculatePrice()
|
||
},
|
||
// 不投保
|
||
skipInsurance() {
|
||
this.selectedInsuranceId = null
|
||
this.calculatePrice()
|
||
},
|
||
// 扫客户付款码,返回 Promise<authCode>
|
||
scanPayCode() {
|
||
return new Promise((resolve, reject) => {
|
||
uni.scanCode({
|
||
scanType: ['barCode', 'qrCode'],
|
||
onlyFromCamera: false,
|
||
success: (res) => {
|
||
const code = (res && res.result || '').trim()
|
||
if (code) {
|
||
resolve(code)
|
||
} else {
|
||
reject(new Error('未识别到付款码'))
|
||
}
|
||
},
|
||
fail: () => {
|
||
reject(new Error('已取消扫码'))
|
||
}
|
||
})
|
||
})
|
||
},
|
||
// 轮询支付结果,最长约 30 秒
|
||
pollPayResult(no) {
|
||
const maxTimes = 15
|
||
const interval = 2000
|
||
let times = 0
|
||
const queryDetail = () => {
|
||
this.$u.get(`/app/pay/detail?no=${no}`).then(res => {
|
||
const status = res && res.data ? res.data.status : ''
|
||
if (status === 'PAYED') {
|
||
uni.hideLoading()
|
||
this.handlePaymentSuccess()
|
||
return
|
||
}
|
||
if (status === 'CANCELED' || status === 'REFUNDED') {
|
||
uni.hideLoading()
|
||
this.submitting = false
|
||
uni.showToast({ title: '支付未完成,订单已取消', icon: 'none', duration: 2500 })
|
||
return
|
||
}
|
||
if (times >= maxTimes) {
|
||
uni.hideLoading()
|
||
this.submitting = false
|
||
uni.showModal({
|
||
title: '支付处理中',
|
||
content: '支付结果尚未确认,请稍后在订单列表中查看。',
|
||
showCancel: false,
|
||
success: () => { uni.navigateBack() }
|
||
})
|
||
return
|
||
}
|
||
setTimeout(checkOnce, interval)
|
||
}).catch(() => {
|
||
if (times >= maxTimes) {
|
||
uni.hideLoading()
|
||
this.submitting = false
|
||
uni.showToast({ title: '支付结果查询失败,请稍后在订单中查看', icon: 'none', duration: 2500 })
|
||
return
|
||
}
|
||
setTimeout(checkOnce, interval)
|
||
})
|
||
}
|
||
const checkOnce = () => {
|
||
times++
|
||
this.$u.put(`/app/pay/refreshPayResult?no=${no}`).then(queryDetail).catch(queryDetail)
|
||
}
|
||
setTimeout(checkOnce, interval)
|
||
},
|
||
// 提交代客下单
|
||
submitOrder() {
|
||
if (this.submitting) return
|
||
if (!this.targetUser) {
|
||
uni.showToast({ title: '请先查询并确认客户信息', icon: 'none', duration: 2000 })
|
||
return
|
||
}
|
||
if (!this.selectedSuitId) {
|
||
uni.showToast({ title: '请选择套餐', icon: 'none', duration: 2000 })
|
||
return
|
||
}
|
||
if (!this.selectedChannelId) {
|
||
uni.showToast({ title: '请选择支付渠道', icon: 'none', duration: 2000 })
|
||
return
|
||
}
|
||
const apiType = this.selectedChannelObj && this.selectedChannelObj.apiType
|
||
if (this.isScanPayChannel(apiType)) {
|
||
this.showConfirmModal = true
|
||
} else if (this.isOfflinePayChannel(apiType)) {
|
||
this.doAgentCreate()
|
||
} else {
|
||
uni.showToast({ title: '当前支付方式暂不支持', icon: 'none', duration: 2000 })
|
||
}
|
||
},
|
||
isScanPayChannel(apiType) {
|
||
return apiType === ChannelApiType.TM_MICRO.value
|
||
},
|
||
isOfflinePayChannel(apiType) {
|
||
return apiType === ChannelApiType.OFFLINE.value
|
||
},
|
||
// 关闭确认弹窗
|
||
closeConfirmModal() {
|
||
this.showConfirmModal = false
|
||
},
|
||
// 确认扫码下单
|
||
confirmScanOrder() {
|
||
this.showConfirmModal = false
|
||
this.scanPayCode().then(payAuthCode => {
|
||
this.doAgentCreate(payAuthCode)
|
||
}).catch(err => {
|
||
uni.showToast({ title: (err && err.message) || '扫码失败', icon: 'none', duration: 2000 })
|
||
})
|
||
},
|
||
// 携带付款码创建订单并发起收款
|
||
doAgentCreate(payAuthCode) {
|
||
const apiType = this.selectedChannelObj && this.selectedChannelObj.apiType
|
||
const isOfflinePay = this.isOfflinePayChannel(apiType)
|
||
this.submitting = true
|
||
uni.showLoading({ title: isOfflinePay ? '创建审核中...' : '收款中...', mask: true })
|
||
const appId = this.$store.state.appid || ''
|
||
const areaPromotionId = uni.getStorageSync('areaPromotionId') || ''
|
||
const data = {
|
||
deviceId: this.deviceId,
|
||
suitId: this.selectedSuitId,
|
||
channelId: this.selectedChannelId,
|
||
userId: this.targetUser.userId,
|
||
appId,
|
||
appType: 1,
|
||
areaPromotionId: areaPromotionId || undefined,
|
||
price: this.priceInfo || undefined
|
||
}
|
||
if (payAuthCode) {
|
||
data.payAuthCode = payAuthCode
|
||
}
|
||
if (this.selectedInsuranceId) {
|
||
data.insuranceDeviceId = this.selectedInsuranceId
|
||
}
|
||
this.$u.post('/bst/order/agentCreate', data).then(res => {
|
||
if (res.code === 200) {
|
||
const createResult = res.data || {}
|
||
if (createResult.needPay === false) {
|
||
uni.hideLoading()
|
||
this.handlePaymentSuccess()
|
||
return
|
||
}
|
||
if (isOfflinePay) {
|
||
uni.hideLoading()
|
||
this.submitting = false
|
||
this.handleOfflinePay(createResult)
|
||
return
|
||
}
|
||
const payNo = createResult.pay ? createResult.pay.no : ''
|
||
if (payNo) {
|
||
uni.showLoading({ title: '等待支付结果...', mask: true })
|
||
this.pollPayResult(payNo)
|
||
} else if (createResult.needPay === true) {
|
||
uni.hideLoading()
|
||
this.submitting = false
|
||
uni.showToast({ title: '支付单信息异常,请稍后在订单中查看', icon: 'none', duration: 2500 })
|
||
} else {
|
||
uni.hideLoading()
|
||
this.handlePaymentSuccess()
|
||
}
|
||
} else {
|
||
uni.hideLoading()
|
||
this.submitting = false
|
||
uni.showToast({ title: res.msg || '下单失败', icon: 'none', duration: 3000 })
|
||
}
|
||
}).catch(() => {
|
||
uni.hideLoading()
|
||
this.submitting = false
|
||
uni.showToast({ title: '网络异常,请重试', icon: 'none', duration: 2000 })
|
||
})
|
||
},
|
||
handlePaymentSuccess() {
|
||
this.submitting = true
|
||
uni.showToast({ title: '收款成功', icon: 'success', duration: 1500 })
|
||
this.startAdminUnlockAfterPay({
|
||
deviceInfo: this.deviceInfo,
|
||
adminFinalStatus: '3',
|
||
onComplete: (success) => {
|
||
this.submitting = false
|
||
this.finishAgentOrderFlow(success)
|
||
}
|
||
})
|
||
},
|
||
finishAgentOrderFlow(unlockSuccess) {
|
||
if (unlockSuccess === false) {
|
||
uni.showModal({
|
||
title: '开锁未完成',
|
||
content: '支付已成功,但车辆开锁失败。请前往车辆详情页手动开锁。',
|
||
showCancel: false,
|
||
success: () => {
|
||
uni.navigateBack()
|
||
}
|
||
})
|
||
return
|
||
}
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
},
|
||
handleOfflinePay(data) {
|
||
const applyNo = data.payParams && data.payParams.no ? String(data.payParams.no) : ''
|
||
const payNo = data.pay && data.pay.no ? String(data.pay.no) : ''
|
||
if (!applyNo) {
|
||
uni.showToast({ title: '线下支付申请单创建失败', icon: 'none', duration: 2500 })
|
||
return
|
||
}
|
||
const query = `no=${encodeURIComponent(applyNo)}&payNo=${encodeURIComponent(payNo)}`
|
||
this.offlinePayNo = payNo
|
||
this.pendingOfflineAudit = true
|
||
this.offlineAuditHandled = false
|
||
uni.navigateTo({
|
||
url: `/page_shanghu/offlinePay/audit?${query}`,
|
||
events: {
|
||
offlineAuditResult: (payload) => {
|
||
this.processOfflineAuditResult(payload)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import './components/agent-order-theme.scss';
|
||
|
||
page {
|
||
background-color: $ao-bg-page;
|
||
}
|
||
|
||
.page {
|
||
height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
background-color: $ao-bg-page;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.scroll-body {
|
||
flex: 1;
|
||
height: 0;
|
||
width: 100%;
|
||
}
|
||
|
||
.scroll-inner {
|
||
padding: 24rpx 32rpx 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.scroll-bottom-spacer {
|
||
height: 24rpx;
|
||
}
|
||
|
||
.vehicle-wrap {
|
||
@include ao-section;
|
||
position: relative;
|
||
|
||
::v-deep .jieshao {
|
||
width: 100%;
|
||
margin: 0;
|
||
margin-top: 0;
|
||
border: 1rpx solid rgba(76, 151, 231, 0.08);
|
||
}
|
||
|
||
::v-deep .vehicle-info {
|
||
padding-left: 48rpx;
|
||
}
|
||
}
|
||
|
||
.change-device-icon {
|
||
position: absolute;
|
||
left: 24rpx;
|
||
top: 24rpx;
|
||
z-index: 2;
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
border-radius: 999rpx;
|
||
background: #fff;
|
||
border: 1rpx solid $ao-border;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 4rpx 12rpx rgba(46, 73, 117, 0.08);
|
||
}
|
||
|
||
.device-loading {
|
||
@include ao-section;
|
||
@include ao-empty-tip;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 16rpx;
|
||
padding: 48rpx 0;
|
||
}
|
||
|
||
.section-head {
|
||
@include ao-section-head;
|
||
}
|
||
|
||
.section-title {
|
||
@include ao-section-title;
|
||
}
|
||
|
||
.package-section {
|
||
@include ao-section;
|
||
}
|
||
|
||
.package-wrap {
|
||
margin: 0;
|
||
|
||
::v-deep .zcfangan {
|
||
width: 100%;
|
||
margin: 0;
|
||
margin-top: 0;
|
||
padding-right: 0 !important;
|
||
}
|
||
|
||
::v-deep .zcfangan > .name {
|
||
display: none;
|
||
}
|
||
|
||
::v-deep .package-info-box {
|
||
width: 100%;
|
||
margin-left: 0;
|
||
margin-right: 0;
|
||
}
|
||
|
||
::v-deep .package-header .package-buttons {
|
||
display: none;
|
||
}
|
||
|
||
::v-deep .package-content .info-item {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: flex-start;
|
||
padding: 8rpx 0;
|
||
}
|
||
|
||
::v-deep .package-content .info-icon-wrap {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
::v-deep .package-content .info-text {
|
||
flex: 1;
|
||
min-width: 0;
|
||
font-size: 28rpx;
|
||
line-height: 40rpx;
|
||
margin-left: 0;
|
||
}
|
||
|
||
::v-deep .fanganlist_item.gaoliang {
|
||
background: #fff !important;
|
||
}
|
||
|
||
::v-deep .package-info-box,
|
||
::v-deep .fanganlist_item {
|
||
background: #fff;
|
||
}
|
||
}
|
||
|
||
.empty-tip {
|
||
@include ao-empty-tip;
|
||
}
|
||
|
||
</style>
|