style:调整样式

This commit is contained in:
2025-10-28 18:32:23 +08:00
parent 449c63ecc4
commit 985d739324
9 changed files with 345 additions and 74 deletions
+9
View File
@@ -18,3 +18,12 @@ export const getNoticeTextData = (data) => {
})
}
// 根据品牌名称获取客服信息
export const getCommonByBrand = (brandName) => {
return request({
url: `/device/common/by/${brandName}`,
method: 'get',
hideLoading: true
})
}
+2 -2
View File
@@ -1,6 +1,6 @@
export const URL = "https://my.gxfs123.com/api" //正式服务器
// export const URL = "https://my.gxfs123.com/api" //正式服务器
// export const URL = "https://fansdev.gxfs123.com/api" //测试服务器
// export const URL = "http://192.168.5.37:8080" //本地调试
export const URL = "http://192.168.5.120:8080" //本地调试
// export const URL = "http://127.0.0.1:8080" //本地调试
export const appid = "wx2165f0be356ae7a9" //小程序appid
+1 -1
View File
@@ -11,7 +11,7 @@
"style": {
"navigationBarTitleText": "",
"navigationBarBackgroundColor": "#ffffff",
"navigationStyle": "default",
"navigationStyle": "custom",
"enableShareAppMessage": true,
"enableShareTimeline": true
}
+63 -3
View File
@@ -38,13 +38,13 @@
<view class="info-icon">
<text class="icon-text"></text>
</view>
<text class="info-text">5/小时45/24小时</text>
<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">前15分钟内归还免费不足60分钟按60分钟计费点总封顶99元持续计费至99元视为买断</text>
<text class="info-text">{{ getDetailInfoText() }}</text>
</view>
</view>
@@ -138,6 +138,7 @@
const deviceInfo = ref({})
const deviceId = ref('')
const deviceFeeConfig = ref({})
const positionInfo = ref({})
const deviceLocation = ref('一号教学楼大厅')
const batteryLevel = ref(95)
const hasActiveOrder = ref(false)
@@ -283,6 +284,11 @@
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
@@ -377,10 +383,64 @@
const selectedPkg = reactive({
time: '1小时',
price: '5.00'
price: '5'
})
const depositAmount = ref('99.00')
// 计算计费单位时间(分钟)
const getBillingUnitMinutes = () => {
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)
const hourPrice = parseFloat(deviceFeeConfig.value.hourPrice || 1)
// maxHourPrice 是1小时的价格,需要根据 hourPrice 换算
const unitPrice = maxHourPrice * hourPrice
return unitPrice.toFixed(2)
}
// 获取免费时间(分钟)
const getFreeMinutes = () => {
if (!positionInfo.value || !positionInfo.value.freeRentTime) return 15
return parseInt(positionInfo.value.freeRentTime)
}
// 计算24小时封顶价格
const get24HourCapPrice = () => {
if (!deviceFeeConfig.value || !deviceFeeConfig.value.maxHourPrice || !deviceFeeConfig.value.maxHour) return '45.00'
const maxHourPrice = parseFloat(deviceFeeConfig.value.maxHourPrice)
const maxHour = parseFloat(deviceFeeConfig.value.maxHour)
return (maxHourPrice * maxHour).toFixed(2)
}
// 生成计费说明文本
const getPricingInfoText = () => {
const unitMinutes = getBillingUnitMinutes()
const unitPrice = getBillingUnitPrice()
const maxHourPrice = deviceFeeConfig.value.maxHourPrice || '5'
if (unitMinutes === 30) {
return `${unitPrice}元/${unitMinutes}分钟,${maxHourPrice}元/小时`
} else {
return `${maxHourPrice}元/小时`
}
}
// 生成详细说明文本
const getDetailInfoText = () => {
const freeMinutes = getFreeMinutes()
const unitMinutes = getBillingUnitMinutes()
const depositAmount = deviceInfo.value.depositAmount || '99'
return `${freeMinutes}分钟内归还免费,不足${unitMinutes}分钟按${unitMinutes}分钟计费,封顶${depositAmount}元,持续计费至${depositAmount}元视为买断`
}
// 提交租借订单
const submitRentOrder = async (payWay) => {
try {
+4 -2
View File
@@ -88,6 +88,7 @@
<script setup>
import { ref, onMounted } from 'vue'
import { getExpressReturnDetail } from '@/config/api/expressReturn.js'
import { getCustomerPhone } from '@/util/index.js'
// 详情数据
const detailData = ref({
@@ -159,15 +160,16 @@ const handleCopyTracking = () => {
// 联系客服
const handleContactService = () => {
const customerPhone = getCustomerPhone()
uni.showModal({
title: '联系客服',
content: '客服电话:400-123-4567\n工作时间:9:00-18:00',
content: `客服电话:${customerPhone}\n工作时间:周一至周日 09:00-22:00`,
confirmText: '拨打',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
uni.makePhoneCall({
phoneNumber: '400-123-4567'
phoneNumber: customerPhone
})
}
}
+1 -1
View File
@@ -146,7 +146,7 @@
type: paramsType.value,
content: description.value,
phone: contact.value,
picturePath: images.value
picturePath: images.value[0]
}
uni.request({
+9 -3
View File
@@ -24,7 +24,7 @@
<view class="contact-content">
<view class="contact-item">
<text class="label">{{ HELP_CONTENT.CONTACT.PHONE.LABEL }}</text>
<text class="value" @click="makePhoneCall">{{ HELP_CONTENT.CONTACT.PHONE.VALUE }}</text>
<text class="value" @click="makePhoneCall">{{ customerPhone }}</text>
</view>
<view class="contact-item">
<text class="label">{{ HELP_CONTENT.CONTACT.SERVICE_TIME.LABEL }}</text>
@@ -37,6 +37,7 @@
<script>
import { HELP_CONTENT } from '@/constants/help'
import { getCustomerPhone } from '@/util/index.js'
export default {
data() {
@@ -45,16 +46,21 @@ export default {
faqList: HELP_CONTENT.FAQ_LIST.map(item => ({
...item,
isOpen: false
}))
})),
customerPhone: HELP_CONTENT.CONTACT.PHONE.VALUE // 默认客服电话
}
},
onLoad() {
// 从缓存读取客服电话
this.customerPhone = getCustomerPhone()
},
methods: {
toggleFaq(index) {
this.faqList[index].isOpen = !this.faqList[index].isOpen
},
makePhoneCall() {
uni.makePhoneCall({
phoneNumber: HELP_CONTENT.CONTACT.PHONE.VALUE
phoneNumber: this.customerPhone
})
}
}
+205 -62
View File
@@ -1,19 +1,36 @@
<template>
<view class="container fullscreen">
<view class="" style="font-size: 32rpx;font-weight: 600;margin: 15rpx 20rpx;">风电者共享风扇&暖手充电宝</view>
<view class="map-notice" v-if="noticeText">
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="navbar-content" :style="{ height: navBarHeight + 'px' }">
<text class="navbar-title">风电者共享风扇&暖手充电宝</text>
</view>
</view>
<view class="map-notice" v-if="noticeText" @click="openNoticePopup">
<uv-notice-bar :text="noticeText" :speed="50" :show-icon="true" color="#07c160" bg-color="#E8F8EF"
icon="volume"></uv-notice-bar>
</view>
<!-- 全屏地图组件 -->
<MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation"
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword"
:enableMarkers="true"
@relocate="handleRelocate" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
@mapCenterChange="onMapCenterChange" />
<!-- 内容区域 -->
<view class="main-content" :style="{ paddingTop: (statusBarHeight) + 'px' }">
<!-- 全屏地图组件 -->
<MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation"
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword"
:enableMarkers="true"
@relocate="handleRelocate" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
@mapCenterChange="onMapCenterChange" />
<!-- 地图加载状态 -->
<view v-if="isLoading || !userLocation" class="map-loading-placeholder">
<view class="loading-content">
<view class="loading-spinner"></view>
<text>正在获取位置信息...</text>
</view>
</view>
</view>
<!-- 底部操作栏附近设备 / 扫码使用 / 我的 -->
<!-- 底部操作栏附近设备 / 扫码使用 / 我的 -->
<view class="bottom-actions">
<!-- <view class="action-btn secondary small btn-nearby" @click="showLocationList">
<view class="icon-wrap">
@@ -44,16 +61,6 @@
</view>
</view>
<!-- 地图加载状态 -->
<view v-if="isLoading || !userLocation" class="map-loading-placeholder">
<view class="loading-content">
<view class="loading-spinner"></view>
<text>正在获取位置信息...</text>
</view>
</view>
<!-- 场地列表弹窗组件 -->
<LocationListSheet
:show="showLocationPopup"
@@ -95,33 +102,50 @@
</view>
</view>
<!-- 使用指南居中弹出ref 控制 open/close -->
<uv-popup ref="guidePopup" mode="center" round="24" :overlay="true" :closeOnClickOverlay="false" :safeAreaInsetBottom="false">
<view class="guide-popup">
<view class="guide-header">
<text class="guide-title">使用指南</text>
<!-- <view class="guide-close" @click="closeGuidePopup">
<uv-icon name="close" size="20"></uv-icon>
</view> -->
</view>
<view class="guide-content">
<view class="guide-step" v-for="(step, idx) in guideSteps" :key="idx">
<view class="step-index">{{ idx + 1 }}</view>
<view class="step-info">
<view class="step-title">{{ step.title }}</view>
<view class="step-desc">{{ step.desc }}</view>
</view>
</view>
</view>
<view class="guide-actions">
<!-- <view class="primary-btn" @click="closeGuidePopup"></view> -->
<view class="primary-btn" @click="closeGuidePopup">
<uv-icon name="close" size="20"></uv-icon>
<!-- 使用指南居中弹出ref 控制 open/close -->
<uv-popup ref="guidePopup" mode="center" round="24" :overlay="true" :closeOnClickOverlay="false" :safeAreaInsetBottom="false">
<view class="guide-popup">
<view class="guide-header">
<text class="guide-title">使用指南</text>
<!-- <view class="guide-close" @click="closeGuidePopup">
<uv-icon name="close" size="20"></uv-icon>
</view> -->
</view>
<view class="guide-content">
<view class="guide-step" v-for="(step, idx) in guideSteps" :key="idx">
<view class="step-index">{{ idx + 1 }}</view>
<view class="step-info">
<view class="step-title">{{ step.title }}</view>
<view class="step-desc">{{ step.desc }}</view>
</view>
</view>
</view>
</uv-popup>
</view>
<view class="guide-actions">
<!-- <view class="primary-btn" @click="closeGuidePopup"></view> -->
<view class="primary-btn" @click="closeGuidePopup">
<uv-icon name="close" size="20"></uv-icon>
</view>
</view>
</view>
</uv-popup>
<!-- 通知详情弹窗居中弹出 -->
<uv-popup ref="noticePopup" mode="center" round="24" :overlay="true" :closeOnClickOverlay="true" :safeAreaInsetBottom="false">
<view class="notice-popup">
<view class="notice-header">
<text class="notice-title">通知公告</text>
</view>
<view class="notice-content">
<text class="notice-text">{{ noticeText }}</text>
</view>
<view class="notice-actions">
<view class="primary-btn" @click="closeNoticePopup">
<uv-icon name="close" size="20"></uv-icon>
</view>
</view>
</view>
</uv-popup>
</view>
</template>
<script setup>
@@ -177,6 +201,10 @@
const isLocationInitialized = ref(false)
const showLocationPopup = ref(false)
const isRelocating = ref(false) // 防抖标志:是否正在重新定位
// 导航栏高度相关
const statusBarHeight = ref(0)
const navBarHeight = ref(44) // 默认导航栏内容高度
// 使用指南步骤
const guideSteps = ref([
@@ -231,9 +259,10 @@
}
// 组件引用
const mapRef = ref(null)
const guidePopup = ref(null)
// 组件引用
const mapRef = ref(null)
const guidePopup = ref(null)
const noticePopup = ref(null)
// 计算属性
const searchPlaceholder = computed(() => {
@@ -243,8 +272,36 @@
return '搜索附近场地'
})
// 计算导航栏高度
const initNavBarHeight = () => {
try {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight || 0
// #ifdef MP-WEIXIN
// 获取胶囊按钮位置信息
const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
// 计算导航栏内容高度:(胶囊底部坐标 - 状态栏高度) 确保与胶囊对齐
navBarHeight.value = (menuButtonInfo.top - systemInfo.statusBarHeight) * 2 + menuButtonInfo.height
// #endif
// #ifndef MP-WEIXIN
// 非微信小程序使用默认高度
navBarHeight.value = 44
// #endif
console.log('状态栏高度:', statusBarHeight.value)
console.log('导航栏内容高度:', navBarHeight.value)
} catch (error) {
console.error('获取导航栏高度失败:', error)
statusBarHeight.value = 20
navBarHeight.value = 44
}
}
// 生命周期
onMounted(() => {
initNavBarHeight()
init()
})
@@ -741,18 +798,31 @@
}
}
// 使用指南弹窗控制
const openPopup = () => {
try {
guidePopup.value && typeof guidePopup.value.open === 'function' && guidePopup.value.open()
} catch (e) {}
}
// 使用指南弹窗控制
const openPopup = () => {
try {
guidePopup.value && typeof guidePopup.value.open === 'function' && guidePopup.value.open()
} catch (e) {}
}
const closeGuidePopup = () => {
try {
guidePopup.value && typeof guidePopup.value.close === 'function' && guidePopup.value.close()
} catch (e) {}
}
const closeGuidePopup = () => {
try {
guidePopup.value && typeof guidePopup.value.close === 'function' && guidePopup.value.close()
} catch (e) {}
}
// 通知弹窗控制
const openNoticePopup = () => {
try {
noticePopup.value && typeof noticePopup.value.open === 'function' && noticePopup.value.open()
} catch (e) {}
}
const closeNoticePopup = () => {
try {
noticePopup.value && typeof noticePopup.value.close === 'function' && noticePopup.value.close()
} catch (e) {}
}
</script>
<script>
@@ -790,6 +860,37 @@
padding: 0;
}
/* 自定义导航栏 */
.custom-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #ffffff;
z-index: 1000;
}
.navbar-content {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 0 20rpx;
}
.navbar-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
/* 主内容区域 */
.main-content {
flex: 1;
position: relative;
width: 100%;
height: 100%;
}
/* 顶部Logo和通知栏 */
.header-section {
width: 92%;
@@ -1271,7 +1372,7 @@
/* 地图加载状态 */
.map-loading-placeholder {
position: absolute;
position: fixed;
top: 0;
left: 0;
right: 0;
@@ -1403,10 +1504,7 @@
.map-notice {
margin: 0 20rpx;
// position: absolute;
// left: 20rpx;
// right: 20rpx;
// top: 20rpx;
margin-top: 10rpx;
border-radius: 20rpx;
z-index: 15;
}
@@ -1517,4 +1615,49 @@
font-size: 32rpx;
font-weight: 600;
}
/* 通知详情弹窗样式 */
.notice-popup {
width: 640rpx;
max-width: 86vw;
background: #ffffff;
border-radius: 24rpx;
padding: 24rpx 24rpx 16rpx;
box-sizing: border-box;
}
.notice-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 16rpx;
}
.notice-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.notice-content {
max-height: 600rpx;
overflow-y: auto;
padding: 16rpx 12rpx;
box-sizing: border-box;
background: #F8F9FA;
border-radius: 12rpx;
}
.notice-text {
font-size: 28rpx;
color: #333;
line-height: 1.8;
word-break: break-all;
white-space: pre-wrap;
}
.notice-actions {
margin-top: 20rpx;
}
</style>
+51
View File
@@ -6,6 +6,8 @@ import {
URL,
appid
} from "@/config/url.js"
import { getCommonByBrand } from "@/config/api/system"
import { HELP_CONTENT } from "@/constants/help"
// import { GET_PHONE_NUMBER_URL } from "../config/url"
// 微信登录方法
@@ -30,6 +32,11 @@ export const wxLogin = () => {
uni.setStorageSync('token', result.data.LoginWxVo.access_token)
uni.setStorageSync('client_id', result.data.LoginWxVo.client_id)
// 4. 登录成功后获取并缓存客服电话
fetchAndCacheCustomerPhone().catch(err => {
console.error('获取客服电话失败,但不影响登录', err)
})
resolve(result.data)
} else {
throw new Error(result.message || '登录失败')
@@ -173,4 +180,48 @@ export const getQueryString = function(url, name) {
return r[2]
}
return null;
}
// 获取并缓存客服电话
export const fetchAndCacheCustomerPhone = async () => {
try {
// 获取当前语言
const locale = uni.getLocale ? uni.getLocale() : uni.getSystemInfoSync().language
const isEnglish = locale && (locale.toLowerCase().startsWith('en') || locale.toLowerCase().includes('en'))
// 根据语言设置品牌名称
const brandName = isEnglish ? 'fdzpower' : '风电者'
console.log('获取客服电话,当前语言:', locale, '品牌名称:', brandName)
// 调用接口获取客服电话
const res = await getCommonByBrand(brandName)
if (res.code === 200 && res.data && res.data.servicePhone) {
const phone = res.data.servicePhone
// 缓存客服电话
uni.setStorageSync('customerPhone', phone)
console.log('客服电话已缓存:', phone)
return phone
} else {
console.warn('获取客服电话失败,使用内置默认电话')
// 使用内置默认电话并缓存
const defaultPhone = HELP_CONTENT.CONTACT.PHONE.VALUE
uni.setStorageSync('customerPhone', defaultPhone)
return defaultPhone
}
} catch (error) {
console.error('获取客服电话出错,使用内置默认电话:', error)
// 使用内置默认电话并缓存
const defaultPhone = HELP_CONTENT.CONTACT.PHONE.VALUE
uni.setStorageSync('customerPhone', defaultPhone)
return defaultPhone
}
}
// 从缓存获取客服电话
export const getCustomerPhone = () => {
const phone = uni.getStorageSync('customerPhone')
// 如果没有缓存,返回内置默认值
return phone || HELP_CONTENT.CONTACT.PHONE.VALUE
}