2026-06-12 11:02:01 +08:00

222 lines
9.4 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>