974 lines
23 KiB
Vue
974 lines
23 KiB
Vue
<template>
|
||
<view class="container">
|
||
<!-- 设备信息卡片 -->
|
||
<view class="card device-info-card">
|
||
<view class="device-location">
|
||
<view class="location-left">
|
||
<image src="/static/images/location-map.svg" mode="aspectFit" class="location-icon"></image>
|
||
<text class="location-name">{{ deviceLocation }}</text>
|
||
</view>
|
||
<view class="device-status" :class="deviceStatus.class">
|
||
<text class="status-text">{{ deviceStatus.text }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="device-id">
|
||
<text class="id-label">{{ $t('device.deviceNo') }}:</text>
|
||
<text class="id-value">{{ deviceId }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 计费规则 -->
|
||
<view class="card pricing-card">
|
||
<view class="card-header">
|
||
<text class="card-title">{{ $t('device.pricingRules') }}</text>
|
||
</view>
|
||
|
||
<view class="pricing-banner">
|
||
<view class="pricing-main">
|
||
<text class="price-symbol">¥</text>
|
||
<text class="price">{{ deviceFeeConfig.maxHourPrice || '5.00' }}</text>
|
||
<text class="unit">/{{ getPriceUnit() }}</text>
|
||
</view>
|
||
<view class="cap-badge">
|
||
<text class="cap-text">{{ deviceInfo.depositAmount || '99' }}{{ $t('device.capLimit') }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="pricing-info">
|
||
<view class="info-icon">
|
||
<text class="icon-text">ⓘ</text>
|
||
</view>
|
||
<text class="info-text">{{ getPricingInfoText() }}</text>
|
||
</view>
|
||
<view class="pricing-info">
|
||
<view class="info-icon">
|
||
<text class="icon-text">ⓘ</text>
|
||
</view>
|
||
<text class="info-text">{{ getDetailInfoText() }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 使用说明 -->
|
||
<view class="card notice-card">
|
||
<view class="card-header">
|
||
<text class="card-title">{{ $t('device.usageInstructions') }}</text>
|
||
</view>
|
||
<view class="notice-items">
|
||
<view class="notice-item">
|
||
<view class="notice-dot"></view>
|
||
<text class="notice-text">{{ $t('device.checkBeforeUse') }}</text>
|
||
</view>
|
||
<view class="notice-item">
|
||
<view class="notice-dot"></view>
|
||
<text class="notice-text">{{ $t('device.autoChargeOvertime') }}</text>
|
||
</view>
|
||
<view class="notice-item">
|
||
<view class="notice-dot"></view>
|
||
<text class="notice-text">{{ $t('device.useInDesignatedArea') }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部操作区 -->
|
||
<view class="footer">
|
||
<button class="rent-button" :class="{ 'return-button': hasActiveOrder }"
|
||
@click="handleRent('alipay-score-pay')">
|
||
<text>{{ hasActiveOrder ? $t('order.returnDevice') : $t('device.rentDepositFree') }}</text>
|
||
</button>
|
||
<view class="alipay-credit">
|
||
<image src="/static/images/alipay.svg" mode="aspectFit" class="alipay-icon"></image>
|
||
<text class="credit-text">{{ $t('device.alipayScoreDesc') }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 手机号授权弹窗 -->
|
||
<view class="phone-auth-popup" v-if="showPhoneAuthPopup">
|
||
<view class="popup-mask" @click.stop></view>
|
||
<view class="popup-content">
|
||
<view class="popup-header">
|
||
<text class="popup-title">{{ $t('auth.authTitle') }}</text>
|
||
</view>
|
||
<view class="popup-body">
|
||
<view class="auth-desc">
|
||
<text>{{ $t('auth.authDescShort') }}</text>
|
||
</view>
|
||
<button class="auth-btn" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">
|
||
{{ $t('auth.getPhoneNumber') }}
|
||
</button>
|
||
<view class="auth-cancel" @click="showPhoneAuthPopup = false">
|
||
<text>{{ $t('auth.notNow') }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
reactive,
|
||
onMounted
|
||
} from 'vue'
|
||
import {
|
||
onLoad
|
||
} from '@dcloudio/uni-app'
|
||
import {
|
||
getDeviceInfo,
|
||
rentPowerBank
|
||
} from '@/config/api/device.js'
|
||
import {
|
||
getOrderByOrderNoScore,
|
||
getOrderByOrderNo,
|
||
cancelOrder
|
||
} from '@/config/api/order.js'
|
||
import {
|
||
initiateAlipayPayment,
|
||
getUserInfo,
|
||
getUserPhoneNumber
|
||
} from '@/util/index.js'
|
||
import {
|
||
useI18n
|
||
} from '@/utils/i18n.js'
|
||
|
||
const {
|
||
t: $t
|
||
} = useI18n()
|
||
|
||
// 响应式状态
|
||
const deviceInfo = ref({})
|
||
const deviceId = ref('')
|
||
const deviceFeeConfig = ref({})
|
||
const positionInfo = ref({})
|
||
const deviceLocation = ref('一号教学楼大厅')
|
||
const hasActiveOrder = ref(false)
|
||
const deviceStatus = reactive({
|
||
text: $t('device.available'),
|
||
class: 'available'
|
||
})
|
||
const isLoggedIn = ref(true)
|
||
const phoneNumber = ref('')
|
||
const showPhoneAuthPopup = ref(false)
|
||
|
||
// 生命周期 onLoad 钩子
|
||
onLoad(async (options) => {
|
||
if (options.deviceNo != uni.getStorageSync('deviceId') || !uni.getStorageSync('deviceId')) {
|
||
deviceId.value = options.deviceNo
|
||
uni.setStorageSync('deviceId', options.deviceNo)
|
||
} else {
|
||
deviceId.value = uni.getStorageSync('deviceId')
|
||
}
|
||
await checkOrderStatus()
|
||
await fetchDeviceInfo()
|
||
})
|
||
|
||
onMounted(async () => {
|
||
uni.setNavigationBarTitle({
|
||
title: $t('device.deviceInfo')
|
||
})
|
||
await checkUserPhone()
|
||
await fetchDeviceInfo()
|
||
})
|
||
|
||
const checkUserPhone = async () => {
|
||
try {
|
||
const userInfoRes = await getUserInfo()
|
||
console.log(userInfoRes.data.phone, 'getUserInfoPhone')
|
||
|
||
if (userInfoRes.code == 200 && userInfoRes.data && userInfoRes.data.phone) {
|
||
phoneNumber.value = userInfoRes.data.phone
|
||
} else {
|
||
// 如果没有手机号,显示授权弹窗
|
||
showPhoneAuthPopup.value = true
|
||
}
|
||
} catch (error) {
|
||
console.error('获取用户信息失败:', error)
|
||
}
|
||
}
|
||
|
||
// 处理获取手机号
|
||
const onGetPhoneNumber = (e) => {
|
||
console.log('getPhoneNumber event:', e.detail)
|
||
|
||
// 用户拒绝授权的情况
|
||
if (e.detail.errMsg && e.detail.errMsg.includes('deny')) {
|
||
uni.showToast({
|
||
title: $t('auth.phoneRequired'),
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
// 获取到授权code
|
||
if (e.detail.code) {
|
||
uni.showLoading({
|
||
title: $t('auth.getting')
|
||
})
|
||
|
||
console.log('获取到的授权code:', e.detail.code)
|
||
|
||
// 添加 try-catch 以捕获任何 Promise 外部的错误
|
||
try {
|
||
getUserPhoneNumber(e.detail.code)
|
||
.then(res => {
|
||
console.log('获取手机号API响应原始数据:', JSON.stringify(res))
|
||
uni.hideLoading()
|
||
|
||
// 不立即抛出错误,而是记录问题并继续处理
|
||
if (!res) {
|
||
console.error('API返回数据为空')
|
||
uni.showModal({
|
||
title: '数据异常',
|
||
content: 'API返回为空',
|
||
showCancel: false
|
||
})
|
||
return
|
||
}
|
||
|
||
// 检查响应格式
|
||
console.log('响应code:', res.code, '响应类型:', typeof res.code)
|
||
console.log('是否有data:', !!res.data, '是否有phone:', res.data && !!res.data.phone)
|
||
|
||
if (res.code == 200 && res.data && res.data.phoneNumber) {
|
||
phoneNumber.value = res.data.phoneNumber
|
||
showPhoneAuthPopup.value = false
|
||
|
||
uni.showToast({
|
||
title: $t('auth.phoneSuccess'),
|
||
icon: 'success'
|
||
})
|
||
} else {
|
||
// 记录详细信息,不抛出错误
|
||
console.warn('获取手机号响应异常:', res.msg || '未知错误')
|
||
uni.showModal({
|
||
title: $t('auth.phoneError'),
|
||
content: `${$t('common.statusCode')}: ${res.code}, ${$t('common.message')}: ${res.msg || $t('common.none')}`,
|
||
showCancel: false
|
||
})
|
||
}
|
||
})
|
||
.catch(err => {
|
||
uni.hideLoading()
|
||
console.error('获取手机号码失败(catch):', err)
|
||
|
||
// 显示更详细的错误信息
|
||
let errMsg = err.message || err.toString()
|
||
uni.showModal({
|
||
title: $t('auth.phoneGetFailed'),
|
||
content: $t('common.errorInfo') + ': ' + errMsg,
|
||
showCancel: false
|
||
})
|
||
})
|
||
} catch (outerError) {
|
||
uni.hideLoading()
|
||
console.error('获取手机号外部错误:', outerError)
|
||
uni.showModal({
|
||
title: $t('common.unexpectedError'),
|
||
content: $t('common.processException') + ': ' + (outerError.message || outerError),
|
||
showCancel: false
|
||
})
|
||
}
|
||
} else {
|
||
uni.showToast({
|
||
title: $t('auth.authCodeFailed'),
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
|
||
// 检查登录状态和订单
|
||
const fetchDeviceInfo = async () => {
|
||
const res = await getDeviceInfo(deviceId.value)
|
||
if (res.code == 200) {
|
||
deviceInfo.value = res.data.device || {}
|
||
|
||
// 保存 position 信息
|
||
if (res.data.position) {
|
||
positionInfo.value = res.data.position
|
||
}
|
||
|
||
// 更新设备位置信息
|
||
if (deviceInfo.value.deviceLocation) {
|
||
deviceLocation.value = deviceInfo.value.deviceLocation
|
||
} else if (res.data.position && res.data.position.name) {
|
||
deviceLocation.value = res.data.position.name
|
||
}
|
||
|
||
|
||
// 更新设备状态
|
||
if (deviceInfo.value.status) {
|
||
if (deviceInfo.value.status === 'online') {
|
||
deviceStatus.text = $t('device.available')
|
||
deviceStatus.class = 'available'
|
||
} else if (deviceInfo.value.status === 'offline') {
|
||
deviceStatus.text = $t('device.offline')
|
||
deviceStatus.class = 'offline'
|
||
}
|
||
}
|
||
|
||
if (deviceInfo.value.feeConfig) {
|
||
deviceFeeConfig.value = JSON.parse(deviceInfo.value.feeConfig)[0] || {}
|
||
console.log('deviceFeeConfig', deviceFeeConfig.value);
|
||
} else {
|
||
deviceFeeConfig.value = {
|
||
maxHourPrice: '5.00',
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
}
|
||
|
||
// 显示登录提示
|
||
const showLoginTip = () => {
|
||
uni.showModal({
|
||
title: $t('common.tips'),
|
||
content: $t('common.loginRequired'),
|
||
confirmText: $t('auth.goToLogin'),
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.navigateTo({
|
||
url: '/pages/login/index'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 检查订单状态
|
||
const checkOrderStatus = async () => {
|
||
try {
|
||
// 调用接口检查是否有进行中的订单
|
||
const result = await uni.$api.checkActiveOrder()
|
||
|
||
if (result.hasOrder) {
|
||
const order = result.order // 假设后端返回 order 对象
|
||
|
||
// 检查订单状态
|
||
if (order.status === 'waiting_for_payment') {
|
||
// 跳转支付页面,带上订单ID
|
||
uni.redirectTo({
|
||
url: `/pages/order/payment?orderId=${order.orderId}&deviceId=${deviceId.value}`
|
||
})
|
||
} else if (order.status === 'in_used') {
|
||
// 如果有正在进行的订单,跳转到归还页面,带上设备ID
|
||
uni.redirectTo({
|
||
url: `/pages/device/return?deviceId=${deviceId.value}`
|
||
})
|
||
}
|
||
}
|
||
} catch (error) {
|
||
uni.showToast({
|
||
title: $t('order.getOrderStatusFailed'),
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
|
||
// 处理租借操作
|
||
const handleRent = (payWay) => {
|
||
if (!isLoggedIn.value) {
|
||
showLoginTip()
|
||
return
|
||
}
|
||
|
||
// 检查是否有手机号,如果没有则提示授权
|
||
if (!phoneNumber.value) {
|
||
showPhoneAuthPopup.value = true
|
||
return
|
||
}
|
||
|
||
// 提交订单
|
||
submitRentOrder(payWay)
|
||
}
|
||
|
||
// 获取价格单位文本
|
||
const getPriceUnit = () => {
|
||
console.log(deviceInfo.value);
|
||
// 按分钟计费
|
||
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
||
return '分钟'
|
||
}else if(deviceInfo.value && deviceFeeConfig.value.hourPrice == '0.5'){
|
||
return '30分钟'
|
||
}
|
||
// 按小时计费(默认)
|
||
return $t('time.hour')
|
||
}
|
||
|
||
// 计算计费单位时间(分钟)
|
||
const getBillingUnitMinutes = () => {
|
||
// 按分钟计费时,单位为1分钟
|
||
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
||
return 1
|
||
}
|
||
// 按小时计费
|
||
if (!deviceFeeConfig.value || !deviceFeeConfig.value.hourPrice) return 60
|
||
const hourPrice = parseFloat(deviceFeeConfig.value.hourPrice)
|
||
// hourPrice 为 0.5 时表示 30 分钟,为 1 时表示 60 分钟
|
||
return hourPrice === 0.5 ? 30 : 60
|
||
}
|
||
|
||
// 计算每个计费单位的价格
|
||
const getBillingUnitPrice = () => {
|
||
if (!deviceFeeConfig.value || !deviceFeeConfig.value.maxHourPrice) return '5'
|
||
const maxHourPrice = parseFloat(deviceFeeConfig.value.maxHourPrice)
|
||
|
||
// 按分钟计费时,直接返回每分钟价格
|
||
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
||
return maxHourPrice.toFixed(2)
|
||
}
|
||
|
||
// 按小时计费
|
||
const hourPrice = parseFloat(deviceFeeConfig.value.hourPrice || 1)
|
||
const unitPrice = maxHourPrice
|
||
return unitPrice.toFixed(2)
|
||
}
|
||
|
||
// 获取免费时间(分钟)
|
||
const getFreeMinutes = () => {
|
||
if (!positionInfo.value || !positionInfo.value.freeRentTime) return 15
|
||
return parseInt(positionInfo.value.freeRentTime)
|
||
}
|
||
|
||
// 生成计费说明文本
|
||
const getPricingInfoText = () => {
|
||
const unitPrice = getBillingUnitPrice()
|
||
const maxHourPrice = deviceFeeConfig.value.maxHourPrice || '5'
|
||
|
||
// 按分钟计费
|
||
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
||
return `${unitPrice}元/分钟`
|
||
}
|
||
|
||
// 按小时计费
|
||
const unitMinutes = getBillingUnitMinutes()
|
||
return deviceInfo.value.remark;
|
||
}
|
||
|
||
// 生成详细说明文本
|
||
const getDetailInfoText = () => {
|
||
const freeMinutes = getFreeMinutes()
|
||
const unitMinutes = getBillingUnitMinutes()
|
||
const depositAmount = deviceInfo.value.depositAmount || '99'
|
||
|
||
// 按分钟计费
|
||
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
||
return `不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断`
|
||
}
|
||
|
||
// 按小时计费
|
||
return `不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断`
|
||
}
|
||
|
||
// 提交租借订单
|
||
const submitRentOrder = async (payWay) => {
|
||
try {
|
||
uni.showLoading({
|
||
title: $t('common.processing')
|
||
})
|
||
// --- 支付宝小程序不需要订阅消息,移除相关代码 ---
|
||
// 支付宝小程序使用消息推送,不需要订阅消息
|
||
|
||
console.log(deviceId.value);
|
||
// 调用设备租借接口
|
||
const rentResult = await rentPowerBank(deviceId.value, phoneNumber.value)
|
||
if (rentResult.code !== 200) {
|
||
throw new Error(rentResult.msg || $t('device.rentFailed'))
|
||
}
|
||
|
||
// 获取后端返回的订单信息
|
||
const order = rentResult.data
|
||
console.log('订单信息', order);
|
||
|
||
if (payWay == 'alipay-pay') {
|
||
// 当支付方式为押金支付时
|
||
uni.hideLoading()
|
||
const res = await getOrderByOrderNo(order.orderNo);
|
||
console.log(res);
|
||
|
||
const deposit = parseFloat(order.depositAmount);
|
||
const packagePrice = parseFloat(order.unitPrice);
|
||
const totalAmount = deposit.toFixed(2);
|
||
|
||
// 跳转到订单支付页面
|
||
uni.redirectTo({
|
||
url: `/pages/order/payment?orderId=${order.orderId}&packagePrice=${packagePrice}&totalAmount=${totalAmount}&depositAmount=${deposit}${deviceInfo.value && deviceInfo.value.feeConfig ? '&feeConfig=' + encodeURIComponent(deviceInfo.value.feeConfig) : ''}`
|
||
})
|
||
|
||
} else if (payWay == 'alipay-score-pay') {
|
||
// 当支付方式为支付宝信用免押支付时
|
||
uni.hideLoading()
|
||
// 获取支付宝信用免押所需参数
|
||
const res = await getOrderByOrderNoScore(order.orderNo);
|
||
uni.hideLoading()
|
||
|
||
if (res && res.code === 200) {
|
||
try {
|
||
// 调用支付宝信用免押小程序
|
||
const payResult = await initiateAlipayPayment(res);
|
||
console.log('支付宝信用免押调用结果', payResult);
|
||
// 成功则跳转到等待页面
|
||
if (payResult && payResult.success !== false) {
|
||
console.log('支付宝信用免押授权成功,准备跳转到等待页,时间:', new Date().toLocaleTimeString());
|
||
// 跳转到等待页面
|
||
uni.redirectTo({
|
||
url: `/pages/waiting/index?orderNo=${order.orderNo}&orderId=${order.orderId}&deviceId=${deviceId.value}`
|
||
});
|
||
return;
|
||
} else {
|
||
console.log('支付宝信用免押未完成授权或用户取消:', payResult);
|
||
// 用户取消授权,需要取消订单
|
||
try {
|
||
uni.showLoading({
|
||
title: $t('order.cancelling')
|
||
});
|
||
const cancelRes = await cancelOrder({
|
||
orderId: order.orderNo
|
||
});
|
||
console.log('订单取消结果:', cancelRes);
|
||
uni.hideLoading();
|
||
|
||
uni.showToast({
|
||
title: $t('order.orderCancelled'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
|
||
// 延迟返回首页
|
||
setTimeout(() => {
|
||
uni.switchTab({
|
||
url: '/pages/index/index'
|
||
});
|
||
}, 2000);
|
||
} catch (cancelError) {
|
||
console.error('取消订单失败:', cancelError);
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: $t('order.cancelFailedContactService'),
|
||
icon: 'none'
|
||
});
|
||
}
|
||
return;
|
||
}
|
||
} catch (payError) {
|
||
console.error('支付分调用异常:', payError);
|
||
// 支付分调用异常,也需要取消订单
|
||
try {
|
||
uni.showLoading({
|
||
title: $t('order.cancelling')
|
||
});
|
||
const cancelRes = await cancelOrder({
|
||
orderId: order.orderNo
|
||
});
|
||
console.log('订单取消结果:', cancelRes);
|
||
uni.hideLoading();
|
||
} catch (cancelError) {
|
||
console.error('取消订单失败:', cancelError);
|
||
uni.hideLoading();
|
||
}
|
||
|
||
uni.showToast({
|
||
title: $t('device.payScoreFailedCancelled'),
|
||
icon: 'none'
|
||
});
|
||
|
||
setTimeout(() => {
|
||
uni.switchTab({
|
||
url: '/pages/index/index'
|
||
});
|
||
}, 2000);
|
||
}
|
||
} else {
|
||
uni.showToast({
|
||
title: res?.msg || $t('device.getPayParamsFailed'),
|
||
icon: 'none'
|
||
});
|
||
}
|
||
}
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: error.message || $t('device.rentFailedRetry'),
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.container {
|
||
min-height: 100vh;
|
||
background-color: #f5f7fa;
|
||
padding: 30rpx 30rpx 300rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
// 卡片通用样式
|
||
.card {
|
||
background-color: #fff;
|
||
border-radius: 24rpx;
|
||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||
padding: 30rpx;
|
||
margin-bottom: 30rpx;
|
||
|
||
.card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 24rpx;
|
||
|
||
.card-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 设备信息卡片
|
||
.device-info-card {
|
||
|
||
.device-location {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 20rpx;
|
||
|
||
.location-left {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.location-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
margin-right: 12rpx;
|
||
background-color: #10d673;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.location-name {
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.device-status {
|
||
padding: 8rpx 24rpx;
|
||
border-radius: 30rpx;
|
||
font-size: 24rpx;
|
||
|
||
&.available {
|
||
background-color: #d4f4dd;
|
||
|
||
.status-text {
|
||
color: #07c160;
|
||
}
|
||
}
|
||
|
||
&.offline {
|
||
background-color: #f0f0f0;
|
||
|
||
.status-text {
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.device-id {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.id-label {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
}
|
||
|
||
.id-value {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 计费规则卡片
|
||
.pricing-card {
|
||
.pricing-banner {
|
||
background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
|
||
border-radius: 20rpx;
|
||
padding: 40rpx 30rpx;
|
||
margin-bottom: 24rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.pricing-main {
|
||
display: flex;
|
||
align-items: baseline;
|
||
margin-bottom: 16rpx;
|
||
|
||
.price-symbol {
|
||
font-size: 48rpx;
|
||
font-weight: bold;
|
||
color: #07c160;
|
||
margin-right: 4rpx;
|
||
}
|
||
|
||
.price {
|
||
font-size: 80rpx;
|
||
font-weight: bold;
|
||
color: #07c160;
|
||
line-height: 1;
|
||
}
|
||
|
||
.unit {
|
||
font-size: 32rpx;
|
||
color: #07c160;
|
||
margin-left: 8rpx;
|
||
}
|
||
}
|
||
|
||
.cap-badge {
|
||
background-color: #07c160;
|
||
padding: 10rpx 32rpx;
|
||
border-radius: 30rpx;
|
||
|
||
.cap-text {
|
||
font-size: 26rpx;
|
||
color: #fff;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
|
||
.pricing-info {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
margin-bottom: 16rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.info-icon {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 12rpx;
|
||
margin-top: 2rpx;
|
||
|
||
.icon-text {
|
||
font-size: 28rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.info-text {
|
||
flex: 1;
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 使用说明卡片
|
||
.notice-card {
|
||
.notice-items {
|
||
.notice-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
margin-bottom: 20rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.notice-dot {
|
||
width: 12rpx;
|
||
height: 12rpx;
|
||
border-radius: 50%;
|
||
background-color: #07c160;
|
||
margin-right: 16rpx;
|
||
margin-top: 10rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.notice-text {
|
||
flex: 1;
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 底部操作区
|
||
.footer {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background-color: #fff;
|
||
padding: 24rpx 30rpx;
|
||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||
z-index: 100;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.rent-button {
|
||
width: 100%;
|
||
height: 96rpx;
|
||
border-radius: 48rpx;
|
||
background: linear-gradient(135deg, #07c160, #10d673);
|
||
color: #fff;
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: none;
|
||
box-shadow: 0 8rpx 20rpx rgba(7, 193, 96, 0.3);
|
||
|
||
&.return-button {
|
||
background: linear-gradient(135deg, #FF9800, #FFB74D);
|
||
box-shadow: 0 8rpx 20rpx rgba(255, 152, 0, 0.3);
|
||
}
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
|
||
.alipay-credit {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 16rpx;
|
||
|
||
.alipay-icon {
|
||
width: 48rpx;
|
||
height: 38rpx;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
.credit-text {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 手机号授权弹窗样式 */
|
||
.phone-auth-popup {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
z-index: 1000;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
.popup-mask {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
}
|
||
|
||
.popup-content {
|
||
background-color: #fff;
|
||
border-radius: 24rpx;
|
||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);
|
||
width: 90%;
|
||
max-width: 500rpx;
|
||
padding: 40rpx 30rpx;
|
||
position: relative;
|
||
z-index: 1001;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.popup-header {
|
||
margin-bottom: 30rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.popup-title {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.popup-body {
|
||
width: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.auth-desc {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
text-align: center;
|
||
margin-bottom: 30rpx;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.auth-btn {
|
||
width: 100%;
|
||
height: 92rpx;
|
||
border-radius: 46rpx;
|
||
background: linear-gradient(135deg, #07c160, #10d673);
|
||
color: #fff;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: none;
|
||
margin-bottom: 20rpx;
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
|
||
.auth-cancel {
|
||
width: 100%;
|
||
height: 92rpx;
|
||
border-radius: 46rpx;
|
||
background-color: #f5f7fa;
|
||
color: #333;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: none;
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
</style> |