<template>
|
<view class="page">
|
<view class="page-header">
|
<view class="safe-space" :style="'height:' + navTop + 'px'"></view>
|
<view class="nav-row" :style="'height:' + navHeight + 'px;padding-right:' + navRight + 'px'">
|
<view class="back-button" hover-class="back-button-active" @tap="goBack"><image src="/static/news-assets/arrow-left.svg" mode="aspectFit"></image></view>
|
<text class="nav-title">{{merchantName}}</text>
|
<view class="capsule-space" :style="'width:' + navWidth + 'px'"></view>
|
</view>
|
</view>
|
|
<view v-if="merchantLoading" class="state-block"><view class="loading-spinner"></view><text>正在加载洗车服务</text></view>
|
<view v-else-if="merchantError" class="state-block error-state"><text>洗车服务加载失败</text><text class="retry-text" @tap="loadMerchantDetail">重新加载</text></view>
|
<view v-else class="content">
|
<view class="service-card">
|
<view class="service-title-row">
|
<view class="service-icon"><image src="/static/merchant-assets/car-wash-service-orange.svg" mode="aspectFit"></image></view>
|
<text class="service-title">{{merchantName}}</text>
|
</view>
|
<view class="talent-tag"><text>{{applicableCards}}</text></view>
|
<view v-if="externalLinkType != 'miniProgram'" class="rights-card">
|
<text class="rights-label">权益详情</text>
|
<text class="rights-content">{{rightsText}}</text>
|
</view>
|
<text v-else class="service-desc">服务点:{{servicePointText}}</text>
|
<text v-if="externalLinkType != 'miniProgram' && tipText != ''" class="service-tip">{{tipText}}</text>
|
<view v-if="!available" class="unavailable-note"><text>{{unavailableReason}}</text></view>
|
</view>
|
<text v-if="externalLinkType == 'miniProgram' && tipText != ''" class="service-tip mini-program-tip">{{tipText}}</text>
|
|
|
</view>
|
|
<view class="action-bar">
|
<view class="wash-button" :class="!available || merchantLoading || merchantError ? 'is-disabled' : ''" @tap="openExternalService">
|
<image src="/static/merchant-assets/car-wash-white.svg" mode="aspectFit"></image>
|
<text v-if="externalLinkType == 'miniProgram' || externalLinkType == 'web'">去洗车</text>
|
<text v-else>提交手机号</text>
|
</view>
|
</view>
|
|
<view v-if="showPhoneForm" class="phone-mask" @tap="closePhoneForm">
|
<view class="phone-dialog" @tap.stop>
|
<view class="phone-dialog-close" @tap="closePhoneForm"><text>×</text></view>
|
<text class="phone-title">填写手机号</text>
|
<text class="phone-desc">提交后商家将根据手机号与您联系</text>
|
<view class="phone-input-box"><input v-model="phone" type="number" maxlength="11" placeholder="请输入手机号" placeholder-class="phone-placeholder" /></view>
|
<text v-if="phoneError != ''" class="phone-error">{{phoneError}}</text>
|
<view class="phone-submit" @tap="submitPhone"><text>提交</text></view>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script setup lang="uts">
|
import { isApiConfigured } from '../../config/api.uts'
|
import { getCarWashMerchantDetail, getTalentCardProfile, submitCarWashCredential } from '../../services/portal.uts'
|
import { getSession } from '../../services/session.uts'
|
import { safeBack } from '../../utils/navigation.uts'
|
|
const navTop = ref(28)
|
const navHeight = ref(32)
|
const navWidth = ref(88)
|
const navRight = ref(10)
|
const merchantId = ref('')
|
const merchantName = ref('自助洗车服务')
|
const applicableCards = ref('')
|
const servicePointText = ref('')
|
const rightsText = ref('')
|
const tipText = ref('')
|
const available = ref(false)
|
const unavailableReason = ref('洗车服务当前不可用')
|
const externalLinkType = ref('')
|
const externalMiniProgramShortLink = ref('')
|
const externalMiniProgramPath = ref('')
|
const externalWebUrl = ref('')
|
const talentId = ref('')
|
const cardId = ref('')
|
const showPhoneForm = ref(false)
|
const phone = ref('')
|
const phoneSubmitting = ref(false)
|
const phoneError = ref('')
|
const merchantLoading = ref(true)
|
const merchantError = ref(false)
|
|
onLoad((options: any) => {
|
// #ifdef MP-WEIXIN
|
const capsule = uni.getMenuButtonBoundingClientRect()
|
const info = uni.getSystemInfoSync()
|
navTop.value = capsule.top
|
navHeight.value = capsule.height
|
navWidth.value = capsule.width
|
navRight.value = info.windowWidth - capsule.right
|
// #endif
|
if (options != null && options['merchantId'] != null) merchantId.value = options['merchantId'] as string
|
if (options != null && options['name'] != null && merchantName.value == '自助洗车服务') merchantName.value = decodeURIComponent(options['name'] as string)
|
const session = getSession()
|
if (session != null && session.talentId != null) talentId.value = session.talentId as string
|
if (talentId.value != '') {
|
getTalentCardProfile(talentId.value, (data: any) => {
|
const card = data != null ? data.card : null
|
cardId.value = card != null && card.id != null ? '' + card.id : ''
|
loadMerchantDetail()
|
}, () => { loadMerchantDetail() })
|
} else {
|
loadMerchantDetail()
|
}
|
})
|
|
const goBack = () => safeBack('/pages/transport-benefits/transport-benefits')
|
const loadMerchantDetail = () => {
|
merchantLoading.value = true
|
merchantError.value = false
|
available.value = false
|
if (merchantId.value == '' || !isApiConfigured()) {
|
merchantLoading.value = false
|
merchantError.value = true
|
return
|
}
|
getCarWashMerchantDetail(merchantId.value, cardId.value, (data: any) => {
|
if (data == null) { merchantLoading.value = false; merchantError.value = true; return }
|
merchantName.value = data.merchantName != null && data.merchantName != '' ? data.merchantName as string : merchantName.value
|
const linkType = data.externalLinkType != null ? data.externalLinkType as string : ''
|
if (linkType == '') {
|
uni.redirectTo({ url: '/pages/merchant/merchant?merchantType=carWash&merchantId=' + encodeURIComponent(merchantId.value) + '&name=' + encodeURIComponent(merchantName.value) })
|
return
|
}
|
applicableCards.value = data.applicableCards != null && data.applicableCards != '' ? data.applicableCards as string : '请咨询商家'
|
servicePointText.value = data.servicePointText != null && data.servicePointText != '' ? data.servicePointText as string : '请咨询商家'
|
rightsText.value = data.rightsText != null && data.rightsText != '' ? data.rightsText as string : servicePointText.value
|
tipText.value = data.tipText != null ? data.tipText as string : ''
|
available.value = data.available === true
|
unavailableReason.value = data.unavailableReason != null && data.unavailableReason != '' ? data.unavailableReason as string : '洗车服务当前不可用'
|
externalLinkType.value = linkType
|
externalMiniProgramShortLink.value = data.externalMiniProgramShortLink != null ? data.externalMiniProgramShortLink as string : ''
|
externalMiniProgramPath.value = data.externalMiniProgramPath != null ? data.externalMiniProgramPath as string : ''
|
externalWebUrl.value = data.externalWebUrl != null ? data.externalWebUrl as string : ''
|
merchantLoading.value = false
|
}, () => { merchantLoading.value = false; merchantError.value = true })
|
}
|
const closePhoneForm = () => { showPhoneForm.value = false; phoneError.value = '' }
|
const submitPhone = () => {
|
if (phoneSubmitting.value) return
|
const value = phone.value.trim()
|
if (!/^1[3-9][0-9]{9}$/.test(value)) {
|
phoneError.value = '请输入正确的 11 位手机号'
|
return
|
}
|
if (merchantId.value == '' || talentId.value == '' || cardId.value == '') {
|
phoneError.value = '请登录后提交手机号'
|
return
|
}
|
phoneSubmitting.value = true
|
phoneError.value = ''
|
const key = 'car-wash-credential-' + merchantId.value + '-' + talentId.value + '-' + Date.now()
|
submitCarWashCredential(merchantId.value, talentId.value, cardId.value, value, key, (data: any) => {
|
phoneSubmitting.value = false
|
showPhoneForm.value = false
|
phone.value = ''
|
uni.showToast({ title: '手机号已提交', icon: 'success' })
|
setTimeout(() => safeBack('/pages/transport-benefits/transport-benefits'), 800)
|
}, (error) => {
|
phoneSubmitting.value = false
|
phoneError.value = error.message
|
})
|
}
|
const openExternalService = () => {
|
if (merchantLoading.value || merchantError.value) return
|
if (!available.value) { uni.showToast({ title: unavailableReason.value, icon: 'none' }); return }
|
if (externalLinkType.value == 'miniProgram' && externalMiniProgramShortLink.value != '') {
|
uni.navigateToMiniProgram({
|
shortLink: externalMiniProgramShortLink.value,
|
envVersion: 'release',
|
fail: () => uni.showToast({ title: '暂时无法打开洗车服务', icon: 'none' })
|
})
|
return
|
}
|
if (externalLinkType.value == 'web' && externalWebUrl.value.startsWith('https://')) {
|
uni.navigateTo({ url: '/pages/external-webview/external-webview?source=carWash&merchantId=' + encodeURIComponent(merchantId.value) + '&talentCardId=' + encodeURIComponent(cardId.value) })
|
return
|
}
|
showPhoneForm.value = true
|
}
|
</script>
|
|
<style>
|
.page { display: flex; width: 100%; height: 100vh; background-color: #F4F7FB; flex-direction: column; }
|
.page-header { position: relative; z-index: 10; flex-shrink: 0; padding-bottom: 20rpx; background-image: linear-gradient(180deg, #FFE6BD 0%, #FFF0D9 100%); }
|
.safe-space { height: 28px; }
|
.nav-row { display: flex; height: 32px; box-sizing: border-box; padding-left: 20rpx; padding-right: 10px; position: relative; flex-direction: row; align-items: center; justify-content: space-between; }
|
.back-button { display: flex; width: 70rpx; height: 64rpx; margin-left: -4rpx; justify-content: center; align-items: center; }
|
.back-button image { width: 36rpx; height: 36rpx; }
|
.back-button-active { opacity: .55; }
|
.nav-title { position: absolute; left: 50%; top: 50%; color: #171B22; font-size: 37rpx; line-height: 48rpx; font-weight: 700; transform: translate(-50%, -50%); white-space: nowrap; }
|
.capsule-space { width: 88px; height: 100%; flex-shrink: 0; }
|
.state-block { display: flex; min-height: 360rpx; color: #8E98A6; font-size: 26rpx; flex-direction: column; justify-content: center; align-items: center; }
|
.error-state { flex-direction: column; }
|
.retry-text { margin-top: 22rpx; color: #F26A38; }
|
.loading-spinner { width: 28rpx; height: 28rpx; margin-right: 14rpx; box-sizing: border-box; border-width: 3rpx; border-style: solid; border-color: #FFD6C4; border-top-color: #FF6C2B; border-radius: 50%; animation: wash-loading 720ms linear infinite; }
|
@keyframes wash-loading { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
.content { min-height: 0; box-sizing: border-box; padding: 30rpx 40rpx 30rpx; flex: 1; overflow-y: auto; }
|
.service-card { box-sizing: border-box; padding: 31rpx 34rpx 27rpx; border-radius: 28rpx; background-color: #FFFFFF; box-shadow: 0 13rpx 30rpx rgba(63,74,89,.07); }
|
.service-title-row { display: flex; flex-direction: row; align-items: center; }
|
.service-icon { display: flex; width: 80rpx; height: 80rpx; border-radius: 50%; background-color: #FFF3E6; justify-content: center; align-items: center; flex-shrink: 0; }
|
.service-icon image { width: 48rpx; height: 48rpx; }
|
.service-title { min-width: 0; flex: 1; margin-left: 21rpx; color: #202631; font-size: 34rpx; line-height: 47rpx; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
.talent-tag { display: flex; max-width: 100%; width: fit-content; min-height: 47rpx; margin-top: 25rpx; box-sizing: border-box; padding: 8rpx 17rpx; border-radius: 24rpx; background-image: linear-gradient(90deg, #FFF0E5 0%, #FFE8D7 100%); justify-content: center; align-items: center; }
|
.talent-tag text { max-width: 540rpx; color: #D86B35; font-size: 23rpx; line-height: 31rpx; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
.service-desc { display: block; margin-top: 24rpx; color: #6F7887; font-size: 25rpx; line-height: 39rpx; }
|
.rights-card { display: flex; margin-top: 24rpx; box-sizing: border-box; padding: 24rpx; border-radius: 20rpx; background-color: #FFF8F2; flex-direction: column; }
|
.rights-label { color: #D86B35; font-size: 24rpx; line-height: 34rpx; font-weight: 700; }
|
.rights-content { margin-top: 10rpx; color: #4D5663; font-size: 26rpx; line-height: 40rpx; }
|
.service-tip { display: block; margin-top: 16rpx; color: #9CA6B5; font-size: 22rpx; line-height: 34rpx; }
|
.mini-program-tip { margin: 27rpx 40rpx 0; text-align: center; }
|
.unavailable-note { margin-top: 16rpx; text-align: center; }
|
.unavailable-note text { color: #D86B35; font-size: 23rpx; line-height: 34rpx; }
|
.phone-mask { display: flex; position: fixed; z-index: 50; left: 0; right: 0; top: 0; bottom: 0; background-color: rgba(0,0,0,.45); justify-content: center; align-items: center; }
|
.phone-dialog { display: flex; width: 590rpx; box-sizing: border-box; padding: 44rpx 40rpx 36rpx; border-radius: 26rpx; background-color: #FFFFFF; position: relative; flex-direction: column; align-items: center; }
|
.phone-dialog-close { display: flex; width: 60rpx; height: 60rpx; position: absolute; top: 12rpx; right: 12rpx; justify-content: center; align-items: center; }
|
.phone-dialog-close text { color: #9AA3AE; font-size: 36rpx; line-height: 60rpx; }
|
.phone-title { color: #262D36; font-size: 31rpx; font-weight: 700; }
|
.phone-desc { margin-top: 12rpx; color: #8A94A1; font-size: 24rpx; }
|
.phone-input-box { display: flex; width: 100%; height: 88rpx; margin-top: 28rpx; box-sizing: border-box; padding: 0 24rpx; border-radius: 16rpx; background-color: #F5F7FA; justify-content: center; align-items: center; }
|
.phone-input-box input { width: 100%; color: #2D333C; font-size: 28rpx; }
|
.phone-placeholder { color: #BBC1CA; }
|
.phone-error { margin-top: 12rpx; color: #F05C3A; font-size: 22rpx; }
|
.phone-submit { display: flex; width: 100%; height: 84rpx; margin-top: 28rpx; border-radius: 42rpx; background-image: linear-gradient(90deg, #FF4B32 0%, #FF8A1E 100%); justify-content: center; align-items: center; }
|
.phone-submit text { color: #FFFFFF; font-size: 28rpx; font-weight: 600; }
|
.action-bar { display: flex; flex-shrink: 0; box-sizing: border-box; padding: 17rpx 40rpx calc(17rpx + env(safe-area-inset-bottom)); background-color: #FFFFFF; box-shadow: 0 -5rpx 16rpx rgba(0,0,0,.04); }
|
.wash-button { display: flex; width: 100%; height: 88rpx; border-radius: 45rpx; background-image: linear-gradient(90deg, #FF4B32 0%, #FF8A1E 100%); flex-direction: row; justify-content: center; align-items: center; }
|
.wash-button.is-disabled { background-image: none; background-color: #C7CDD5; }
|
.wash-button image { width: 36rpx; height: 36rpx; margin-right: 18rpx; }
|
.wash-button text { color: #FFFFFF; font-size: 30rpx; font-weight: 600; }
|
</style>
|