222 lines
9.4 KiB
Vue
222 lines
9.4 KiB
Vue
<template>
|
||
<view class="hso-page">
|
||
<view class="hso-card">
|
||
<view class="hso-svc">{{ params.service_name }}</view>
|
||
<view class="hso-deposit">定金 ¥{{ params.deposit }}</view>
|
||
</view>
|
||
<view class="hso-form">
|
||
<view class="hso-row">
|
||
<text class="hso-label">联系人</text>
|
||
<input class="hso-input" v-model="contactName" placeholder="请输入联系人" />
|
||
</view>
|
||
<view class="hso-row">
|
||
<text class="hso-label">联系电话</text>
|
||
<input class="hso-input" type="number" v-model="contactPhone" placeholder="请输入联系电话" maxlength="11" />
|
||
</view>
|
||
<view class="hso-row">
|
||
<text class="hso-label">小区地址</text>
|
||
<input class="hso-input" v-model="serviceAddress" placeholder="请选择或输入小区地址" />
|
||
<text class="hso-map-btn" @tap="chooseLocation">地图选点</text>
|
||
</view>
|
||
<view class="hso-row">
|
||
<text class="hso-label">门牌号</text>
|
||
<input class="hso-input" v-model="houseNumber" placeholder="如:1栋1单元103室" />
|
||
</view>
|
||
<view class="hso-row" @tap="showDate = true">
|
||
<text class="hso-label">上门时间</text>
|
||
<text class="hso-input" :class="{ ph: !serviceTime }">{{ serviceTime || '请选择上门时间' }}</text>
|
||
</view>
|
||
<view class="hso-row hso-row--col">
|
||
<text class="hso-label">备注</text>
|
||
<textarea class="hso-textarea" v-model="remark" placeholder="选填" />
|
||
</view>
|
||
</view>
|
||
|
||
<view class="hso-bottom">
|
||
<view class="hso-bottom-price">定金 <text>¥{{ params.deposit }}</text></view>
|
||
<view class="hso-bottom-btn" @tap="submit">提交并支付</view>
|
||
</view>
|
||
|
||
<u-datetime-picker :show="showDate" :value="dateValue" mode="datetime" @confirm="onDate" @cancel="showDate = false" @close="showDate = false" />
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { request, NavgateTo, isPhone } from '@/utils'
|
||
import { apiArr } from '@/api/homeService'
|
||
import { apiArr as commApi } from '@/api/community'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
params: {},
|
||
contactName: '',
|
||
contactPhone: '',
|
||
serviceAddress: '',
|
||
houseNumber: '',
|
||
serviceTime: '',
|
||
dateValue: Date.now(),
|
||
remark: '',
|
||
showDate: false,
|
||
submitting: false
|
||
}
|
||
},
|
||
onLoad(options) {
|
||
if (options.params) {
|
||
try { this.params = JSON.parse(decodeURIComponent(options.params)) } catch (e) { this.params = {} }
|
||
}
|
||
// 先用本地缓存兜底(秒显),再异步拉后台房产数据覆盖为准确值
|
||
this.fillFromStorage()
|
||
this.loadBoundHouse()
|
||
// 默认上门时间:当前时间 3 小时之后的整点
|
||
this.initDefaultTime()
|
||
},
|
||
methods: {
|
||
// 本地缓存兜底:小区地址(详细地址在前 + 小区名)、门牌号
|
||
fillFromStorage() {
|
||
const addr = uni.getStorageSync('currentCommunityAddr')
|
||
const comm = uni.getStorageSync('changeCommData')
|
||
const commName = (comm && comm.name) ? comm.name : ''
|
||
if (addr) {
|
||
this.serviceAddress = (addr + ' ' + commName).trim()
|
||
} else if (commName) {
|
||
this.serviceAddress = commName
|
||
}
|
||
const roomNo = uni.getStorageSync('currentRoomNo')
|
||
if (roomNo) this.houseNumber = roomNo
|
||
},
|
||
// 拉取后台已绑定房产:用真实小区地址 + room_name 填充地址与门牌
|
||
loadBoundHouse() {
|
||
const loc = uni.getStorageSync('location') || {}
|
||
const comm = uni.getStorageSync('changeCommData')
|
||
request(commApi.commInfo, 'POST', {
|
||
user_id: uni.getStorageSync('userId'),
|
||
longitude: loc.lng,
|
||
latitude: loc.lat,
|
||
page_num: 1,
|
||
page_size: 20
|
||
}, { silent: false }).then(res => {
|
||
const rows = (res && res.rows) ? res.rows : []
|
||
if (!rows.length) return
|
||
// 优先匹配当前选中的小区,否则取第一个
|
||
let target = null
|
||
if (comm && comm.id) target = rows.find(r => r.community_id === comm.id)
|
||
if (!target) target = rows[0]
|
||
const commName = target.name || ''
|
||
if (target.addr) {
|
||
this.serviceAddress = (target.addr + ' ' + commName).trim()
|
||
} else if (commName) {
|
||
this.serviceAddress = commName
|
||
}
|
||
const owner = (target.room_owner_list && target.room_owner_list.length) ? target.room_owner_list[0] : null
|
||
if (owner && owner.room_name) this.houseNumber = owner.room_name
|
||
}).catch(() => {})
|
||
},
|
||
// 默认上门时间 = 当前时间 + 3 小时,并向上取整到整点
|
||
initDefaultTime() {
|
||
const d = new Date()
|
||
d.setHours(d.getHours() + 3)
|
||
// 有分/秒/毫秒则进位到下一个整点
|
||
if (d.getMinutes() > 0 || d.getSeconds() > 0 || d.getMilliseconds() > 0) {
|
||
d.setHours(d.getHours() + 1)
|
||
}
|
||
d.setMinutes(0, 0, 0)
|
||
this.dateValue = d.getTime()
|
||
this.serviceTime = this.formatTime(d)
|
||
},
|
||
formatTime(d) {
|
||
const p = n => String(n).padStart(2, '0')
|
||
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
|
||
},
|
||
// 地图选点:选择后回填地址(详细地址在前,小区/点位名称在后)
|
||
chooseLocation() {
|
||
const loc = uni.getStorageSync('location') || {}
|
||
uni.chooseLocation({
|
||
latitude: loc.lat,
|
||
longitude: loc.lng,
|
||
success: (res) => {
|
||
const detail = res.name ? (res.address + ' ' + res.name) : res.address
|
||
this.serviceAddress = (detail || '').trim()
|
||
},
|
||
fail: (err) => {
|
||
if (err && err.errMsg && err.errMsg.indexOf('cancel') === -1) {
|
||
uni.showToast({ title: '地图选点失败,请检查定位权限', icon: 'none' })
|
||
}
|
||
}
|
||
})
|
||
},
|
||
onDate(e) {
|
||
const d = new Date(e.value)
|
||
this.dateValue = d.getTime()
|
||
this.serviceTime = this.formatTime(d)
|
||
this.showDate = false
|
||
},
|
||
submit() {
|
||
if (!this.contactName) { uni.showToast({ title: '请输入联系人', icon: 'none' }); return }
|
||
if (!isPhone(this.contactPhone)) { uni.showToast({ title: '联系电话格式不正确', icon: 'none' }); return }
|
||
if (!this.serviceAddress) { uni.showToast({ title: '请选择或输入小区地址', icon: 'none' }); return }
|
||
if (!this.houseNumber) { uni.showToast({ title: '请输入门牌号', icon: 'none' }); return }
|
||
if (this.submitting) return
|
||
this.submitting = true
|
||
const userId = uni.getStorageSync('userId')
|
||
const fullAddress = (this.serviceAddress + ' ' + this.houseNumber).trim()
|
||
request(apiArr.orderCreate, 'POST', {
|
||
user_id: userId,
|
||
service_info_id: this.params.service_info_id,
|
||
sku_id: this.params.sku_id,
|
||
employee_id: this.params.employee_id,
|
||
service_address: fullAddress,
|
||
contact_name: this.contactName,
|
||
contact_phone: this.contactPhone,
|
||
service_time: this.serviceTime,
|
||
remark: this.remark
|
||
}).then(res => {
|
||
this.payOrder(res.id, userId)
|
||
}).catch(() => { this.submitting = false })
|
||
},
|
||
payOrder(orderId, userId) {
|
||
request(apiArr.preorder, 'POST', { order_id: orderId, user_id: userId, trans_type: '71' }).then(pay => {
|
||
uni.requestPayment({
|
||
provider: 'wxpay',
|
||
timeStamp: pay.timeStamp,
|
||
nonceStr: pay.nonceStr,
|
||
package: pay.package,
|
||
signType: pay.signType || 'RSA',
|
||
paySign: pay.paySign,
|
||
success: () => {
|
||
// 支付成功后查单回写
|
||
request(apiArr.tradeQuery, 'POST', { order_id: orderId }, {}, false).finally(() => {
|
||
uni.redirectTo({ url: '/packages/homeService/myOrders/index' })
|
||
})
|
||
},
|
||
fail: () => {
|
||
this.submitting = false
|
||
uni.showToast({ title: '支付已取消', icon: 'none' })
|
||
uni.redirectTo({ url: '/packages/homeService/myOrders/index' })
|
||
}
|
||
})
|
||
}).catch(() => { this.submitting = false })
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.hso-page { background: #f5f5f5; min-height: 100vh; padding-bottom: 130rpx; }
|
||
.hso-card { background: #fff; padding: 28rpx 24rpx; display: flex; align-items: center; justify-content: space-between; }
|
||
.hso-svc { font-size: 30rpx; color: #222; font-weight: 600; }
|
||
.hso-deposit { font-size: 30rpx; color: #FF370B; font-weight: 600; }
|
||
.hso-form { background: #fff; margin-top: 16rpx; padding: 0 24rpx; }
|
||
.hso-row { display: flex; align-items: center; padding: 26rpx 0; border-bottom: 1rpx solid #f2f2f2; }
|
||
.hso-row--col { flex-direction: column; align-items: flex-start; }
|
||
.hso-label { width: 160rpx; font-size: 28rpx; color: #333; flex-shrink: 0; }
|
||
.hso-input { flex: 1; font-size: 28rpx; color: #222; }
|
||
.hso-input.ph { color: #b7b7b7; }
|
||
.hso-map-btn { flex-shrink: 0; margin-left: 16rpx; font-size: 26rpx; color: #FF370B; padding: 6rpx 16rpx; border: 1rpx solid #FF370B; border-radius: 28rpx; }
|
||
.hso-textarea { width: 100%; height: 120rpx; font-size: 28rpx; margin-top: 12rpx; }
|
||
.hso-bottom { position: fixed; left: 0; right: 0; bottom: 0; background: #fff; display: flex; align-items: center; padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom)); box-shadow: 0 -4rpx 20rpx rgba(0,0,0,0.04); }
|
||
.hso-bottom-price { flex: 1; font-size: 26rpx; color: #333; }
|
||
.hso-bottom-price text { color: #FF370B; font-size: 36rpx; font-weight: 600; }
|
||
.hso-bottom-btn { background: linear-gradient(91deg, #FF7658, #FF370B); color: #fff; font-size: 30rpx; padding: 20rpx 60rpx; border-radius: 44rpx; }
|
||
</style>
|