style:根据UI设计图跳转页面样式
@@ -3,3 +3,4 @@
|
||||
package-lock.json
|
||||
/unpackage
|
||||
/unpackage/dist
|
||||
/doc
|
||||
@@ -0,0 +1,234 @@
|
||||
<template>
|
||||
<view class="location-popup" v-if="show">
|
||||
<view class="popup-mask" @click="$emit('close')"></view>
|
||||
<view class="location-sheet" :class="{ 'expanded': expanded }">
|
||||
<view class="sheet-header">
|
||||
<text class="sheet-title">{{ title }} ({{ positions.length }})</text>
|
||||
<view class="close-btn" @click="$emit('close')">
|
||||
<uv-icon name="close"></uv-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="sheet-content">
|
||||
<view class="position-item" v-for="(item, index) in positions" :key="item.positionId || index"
|
||||
@click="$emit('select', item)">
|
||||
<view class="position-info">
|
||||
<view class="position-name">{{ item.name }}</view>
|
||||
<view class="tag-row">
|
||||
<view class="status-tag rent" v-if="isRentable(item)"><text>可租借</text></view>
|
||||
<view class="status-tag return" v-if="isReturnable(item)"><text>可归还</text></view>
|
||||
</view>
|
||||
<view class="position-time" v-if="item.workTime && item.workTime !== '0'"><text>营业时间:{{ item.workTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="position-actions">
|
||||
<view class="distance-info" v-if="item.distance"><text>{{ item.distance }}</text></view>
|
||||
<view class="nav-btn" @click.stop="$emit('navigate', item)"><text>导航</text></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="empty-state" v-if="!isLoading && (!positions || positions.length === 0)">
|
||||
<image class="empty-icon" src="/static/scan-icon.png" mode="aspectFit" />
|
||||
<text class="empty-text">附近暂无设备</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
show: { type: Boolean, default: false },
|
||||
expanded: { type: Boolean, default: false },
|
||||
positions: { type: Array, default: () => [] },
|
||||
isLoading: { type: Boolean, default: false },
|
||||
title: { type: String, default: '附近设备场地' }
|
||||
})
|
||||
|
||||
const isRentable = (item) => {
|
||||
// 兼容多种标识字段
|
||||
if (typeof item?.canRent !== 'undefined') return !!item.canRent
|
||||
return String(item?.status || '').toLowerCase() === 'online'
|
||||
}
|
||||
|
||||
const isReturnable = (item) => {
|
||||
if (typeof item?.canReturn !== 'undefined') return !!item.canReturn
|
||||
return String(item?.status || '').toLowerCase() === 'online'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.location-popup {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
|
||||
.popup-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.location-sheet {
|
||||
background: #ffffff;
|
||||
border-radius: 32rpx 32rpx 0 0;
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.1);
|
||||
max-height: 78vh;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
animation: slideUp 0.3s ease-out;
|
||||
|
||||
&.expanded {
|
||||
max-height: 88vh;
|
||||
}
|
||||
|
||||
.sheet-header {
|
||||
padding: 20rpx 30rpx;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.sheet-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f0f0f0;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:active {
|
||||
background: #e0e0e0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sheet-content {
|
||||
padding: 12rpx 0;
|
||||
height: 64vh;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.position-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 30rpx;
|
||||
border-bottom: 1px solid #f8f9fa;
|
||||
|
||||
.position-info {
|
||||
flex: 1;
|
||||
|
||||
.position-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.tag-row {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.status-tag {
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 22rpx;
|
||||
|
||||
&.rent {
|
||||
background: #e8f5e8;
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
&.return {
|
||||
background: #e8f2ff;
|
||||
color: #3578e5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.position-time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.position-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 8rpx;
|
||||
|
||||
.distance-info {
|
||||
font-size: 24rpx;
|
||||
color: #2196F3;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 12rpx 20rpx;
|
||||
background: #2196F3;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60rpx 0;
|
||||
|
||||
.empty-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin-bottom: 24rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
<!-- 使用小程序原生地图组件 -->
|
||||
<map id="map" class="native-map" :longitude="mapCenter.longitude" :latitude="mapCenter.latitude"
|
||||
:markers="mapMarkers" :scale="mapZoom" :show-location="true" @regionchange="onMapRegionChange"
|
||||
@markertap="onMapMarkerTap" @callouttap="onCalloutTap" @updated="onMapUpdated"
|
||||
@error="onMapError">
|
||||
@markertap="onMapMarkerTap" @callouttap="onCalloutTap" @updated="onMapUpdated" @error="onMapError">
|
||||
<!-- 覆盖在地图上的可点击控件(使用 cover-view 以兼容小程序原生组件层级) -->
|
||||
<cover-view class="index-swiper">
|
||||
<image src="/static/index_swiper.png" class="index-swiper" mode="aspectFit"></image>
|
||||
</cover-view>
|
||||
<cover-view class="map-side-controls">
|
||||
<cover-view class="side-btn service" @tap="handleService">
|
||||
<cover-image class="side-icon" src="/static/customer-service.png"></cover-image>
|
||||
@@ -25,8 +27,6 @@
|
||||
<!-- 使用原生 marker 方案渲染中心指示,随 regionchange 同步到地图中心 -->
|
||||
</map>
|
||||
|
||||
|
||||
|
||||
<!-- 地图加载状态 -->
|
||||
<view class="map-loading" v-if="isLoading">
|
||||
<view class="loading-content">
|
||||
@@ -136,7 +136,10 @@ import {
|
||||
iconPath: '/static/location-icon.png',
|
||||
width: 30,
|
||||
height: 40,
|
||||
anchor: { x: 0.5, y: 1 } // 图钉尖端对准坐标
|
||||
anchor: {
|
||||
x: 0.5,
|
||||
y: 1
|
||||
} // 图钉尖端对准坐标
|
||||
})
|
||||
}
|
||||
|
||||
@@ -171,7 +174,10 @@ const moveToLocation = (location) => {
|
||||
|
||||
try {
|
||||
mapContext.value.includePoints({
|
||||
points: [{ longitude: Number(location.longitude), latitude: Number(location.latitude) }],
|
||||
points: [{
|
||||
longitude: Number(location.longitude),
|
||||
latitude: Number(location.latitude)
|
||||
}],
|
||||
padding: [60, 60, 60, 60],
|
||||
success: () => {
|
||||
console.log('地图已移动到指定位置(includePoints)')
|
||||
@@ -284,47 +290,15 @@ const handleRelocate = () => {
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
// 选择位置后移动地图并通知父组件刷新附近列表(增强容错与平台兼容)
|
||||
const onSuccess = (res) => {
|
||||
try {
|
||||
const lng = Number(res.longitude)
|
||||
const lat = Number(res.latitude)
|
||||
if (!isNaN(lng) && !isNaN(lat)) {
|
||||
const center = { longitude: lng, latitude: lat }
|
||||
moveToLocation(center)
|
||||
emit('mapCenterChange', center)
|
||||
} else {
|
||||
uni.showToast({ title: '无效的坐标', icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('处理选择位置结果异常:', e)
|
||||
}
|
||||
}
|
||||
const onFail = (err) => {
|
||||
console.error('chooseLocation 失败:', err)
|
||||
// 授权被拒时引导开启
|
||||
if (err && (String(err.errMsg || '').includes('auth') || String(err.errMsg || '').includes('deny'))) {
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.openSetting && wx.openSetting({})
|
||||
// #endif
|
||||
}
|
||||
// uni.showToast({ title: '无法打开选择位置', icon: 'none' })
|
||||
}
|
||||
// #ifdef MP-WEIXIN
|
||||
if (typeof wx !== 'undefined' && wx.chooseLocation) {
|
||||
wx.chooseLocation({ success: onSuccess, fail: onFail })
|
||||
return
|
||||
}
|
||||
// #endif
|
||||
if (uni && typeof uni.chooseLocation === 'function') {
|
||||
uni.chooseLocation({ success: onSuccess, fail: onFail })
|
||||
} else {
|
||||
uni.showToast({ title: '当前环境不支持选择位置', icon: 'none' })
|
||||
}
|
||||
uni.navigateTo({ url: '/pages/search/index' })
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
const handleService = () => {
|
||||
uni.navigateTo({ url: '/pages/help/index' })
|
||||
uni.navigateTo({
|
||||
url: '/pages/help/index'
|
||||
})
|
||||
}
|
||||
|
||||
const handleScan = () => {
|
||||
@@ -479,7 +453,7 @@ onMounted(() => {
|
||||
|
||||
.map-side-controls {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
right: 20rpx;
|
||||
bottom: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -492,7 +466,7 @@ onMounted(() => {
|
||||
.side-btn {
|
||||
// min-width: 160rpx;
|
||||
margin: auto;
|
||||
height: 72rpx;
|
||||
// height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border-radius: 36rpx;
|
||||
display: inline-flex;
|
||||
@@ -500,7 +474,7 @@ onMounted(() => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12);
|
||||
padding: 0 18rpx;
|
||||
padding: 20rpx;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
|
||||
&:active {
|
||||
@@ -514,40 +488,9 @@ onMounted(() => {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.search {
|
||||
border-color: #07c160;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 展示用图标列(与 cover-view 对齐) */
|
||||
.map-side-icons {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
bottom: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
|
||||
.side-btn {
|
||||
min-width: 160rpx;
|
||||
height: 72rpx;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border-radius: 36rpx;
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12);
|
||||
padding: 0 18rpx;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
|
||||
.label {
|
||||
margin-left: 8rpx;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
// &.search {
|
||||
// border-color: #07c160;
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -563,8 +506,19 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.side-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
|
||||
.index-swiper {
|
||||
width: 92vw;
|
||||
height: 180rpx;
|
||||
border-radius: 20rpx;
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,364 @@
|
||||
<template>
|
||||
<view class="order-item" >
|
||||
<!-- 订单头部信息 -->
|
||||
<view class="order-header">
|
||||
<view class="header-left">
|
||||
<view class="status-chip" :class="statusChipClass"><text class="chip-text">{{ statusText }}</text></view>
|
||||
<view class="title">{{ titleText }}</view>
|
||||
</view>
|
||||
<view class="header-right">
|
||||
<!-- 支付方式标识(移到头部右侧) -->
|
||||
<view class="payment-badge wx-score" v-if="order.payWay == 'wx_score_pay'">
|
||||
<image src="/static/images/wxpayflag.png" mode="aspectFit" class="badge-icon"></image>
|
||||
<view class="badge-text">
|
||||
<text>微信支付分</text>
|
||||
<text class="divider">|</text>
|
||||
<text class="highlight">免押租借</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="payment-badge member" v-else-if="order.payWay == 'wx_member_pay'">
|
||||
<text class="badge-text">会员订单</text>
|
||||
</view>
|
||||
<view class="payment-badge deposit" v-else>
|
||||
<text class="badge-text">微信支付</text>
|
||||
<text class="divider">|</text>
|
||||
<text class="badge-text">押金租借</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单内容 -->
|
||||
<view class="order-body" @click="onDetails">
|
||||
<!-- <view class="device-info">
|
||||
<view class="device-left">
|
||||
<view class="device-name">{{ titleText }}</view>
|
||||
<view class="device-id">设备号:{{ order.deviceId }}</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 订单时间信息 -->
|
||||
<view class="order-times">
|
||||
<view class="time-row">
|
||||
<text class="time-label">租借地点:</text>
|
||||
<text class="time-value">{{ order.deviceName || order.positionName }}</text>
|
||||
</view>
|
||||
<view class="time-row">
|
||||
<text class="time-label">租借时间:</text>
|
||||
<text class="time-value">{{ order.startTime }}</text>
|
||||
</view>
|
||||
<view class="arrow" @click="onDetails">
|
||||
<uv-icon name="arrow-right" size="24rpx" color="#999"></uv-icon>
|
||||
</view>
|
||||
<!-- <view class="time-row">
|
||||
<text class="time-label">结束时间:</text>
|
||||
<text class="time-value">{{ order.endTime || '-' }}</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单底部 -->
|
||||
<view class="order-footer">
|
||||
<view class="footer-left">
|
||||
<view v-if="isInUse" class="renting"><text class="dot"></text>租借中</view>
|
||||
<view v-else-if="isFinished" class="meta">
|
||||
<view class="meta-item"><text class="dot"></text>{{ usedDurationText }}</view>
|
||||
<view class="meta-item"><text class="currency">¥</text>{{ displayAmount }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="actions">
|
||||
<!-- 待支付状态显示支付和取消按钮 -->
|
||||
<view v-if="isWaitingForPayment" class="action-item primary" @click="onPay">立即支付</view>
|
||||
<view v-if="isWaitingForPayment" class="action-item secondary" @click="onCancel">取消订单</view>
|
||||
<!-- 使用中状态显示归还设备按钮 -->
|
||||
<view v-if="isInUse" class="action-item primary" @click="onReturn">快速归还</view>
|
||||
<!-- 查看详情按钮对所有订单都显示 -->
|
||||
<!-- <view class="action-item secondary" >查看详情</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
order: { type: Object, required: true },
|
||||
orderStatusMap: { type: Object, required: true }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['pay', 'cancel', 'return-device', 'details']);
|
||||
|
||||
const rawStatus = computed(() => props.order.orderStatus ?? props.order.status);
|
||||
const normalizedStatus = computed(() => {
|
||||
const s = rawStatus.value;
|
||||
switch (s) {
|
||||
case 0:
|
||||
case '0':
|
||||
return 'waiting_for_payment';
|
||||
case 1:
|
||||
case '1':
|
||||
return 'in_used';
|
||||
case 2:
|
||||
case '2':
|
||||
return 'used_done';
|
||||
case 3:
|
||||
case '3':
|
||||
return 'order_cancelled';
|
||||
default:
|
||||
return s || '';
|
||||
}
|
||||
});
|
||||
|
||||
const statusDef = computed(() => props.orderStatusMap?.[rawStatus.value] || props.orderStatusMap?.[normalizedStatus.value] || {});
|
||||
const statusText = computed(() => statusDef.value.text || '');
|
||||
const statusChipClass = computed(() => {
|
||||
const cls = statusDef.value.class || '';
|
||||
if (cls.includes('status-using')) return 'chip-using';
|
||||
if (cls.includes('status-waiting')) return 'chip-waiting';
|
||||
if (cls.includes('status-finished')) return 'chip-finished';
|
||||
if (cls.includes('status-cancelled')) return 'chip-cancelled';
|
||||
return 'chip-default';
|
||||
});
|
||||
|
||||
const isWaitingForPayment = computed(() => normalizedStatus.value === 'waiting_for_payment');
|
||||
const isInUse = computed(() => normalizedStatus.value === 'in_used');
|
||||
const isFinished = computed(() => normalizedStatus.value === 'used_done');
|
||||
|
||||
const titleText = computed(() => props.order.deviceName ? '租借风扇' : '租借风扇');
|
||||
|
||||
// 显示金额(优先后端给定字段)
|
||||
const displayAmount = computed(() => props.order.amount || props.order.payAmount || props.order.actualDeviceAmount || props.order.currentFee || '0.00');
|
||||
|
||||
// 使用时长
|
||||
const usedDurationText = computed(() => {
|
||||
const start = parseDate(props.order.startTime);
|
||||
const end = parseDate(props.order.endTime) || new Date();
|
||||
if (!start) return '';
|
||||
const diffMs = Math.max(0, end.getTime() - start.getTime());
|
||||
const minutes = Math.floor(diffMs / 60000);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const mins = minutes % 60;
|
||||
if (hours > 0) return `${hours}小时${mins}分钟`;
|
||||
return `${mins}分钟`;
|
||||
});
|
||||
|
||||
function parseDate(str) {
|
||||
if (!str) return null;
|
||||
try {
|
||||
return new Date(String(str).replace(/-/g, '/'));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const onPay = () => emit('pay', props.order);
|
||||
const onCancel = () => emit('cancel', props.order);
|
||||
const onReturn = () => emit('return-device', props.order);
|
||||
const onDetails = () => emit('details', props.order);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-item {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
// 订单头部
|
||||
.order-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx;
|
||||
// border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
.header-left { display: flex; align-items: center; }
|
||||
.title { font-size: 30rpx; color: #222; margin-left: 16rpx; font-weight: 600; }
|
||||
.status-chip {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 24rpx;
|
||||
background: #f5f5f5;
|
||||
transform: skewX(-15deg);
|
||||
overflow: hidden;
|
||||
.chip-text { display: inline-block; transform: skewX(15deg); }
|
||||
&.chip-using { background: rgba(7,193,96,0.12); color: #07c160; }
|
||||
&.chip-waiting { background: rgba(255,152,0,0.12); color: #FF9800; }
|
||||
&.chip-finished { background: rgba(76,175,80,0.12); color: #4CAF50; }
|
||||
&.chip-cancelled { background: rgba(158,158,158,0.12); color: #9E9E9E; }
|
||||
}
|
||||
|
||||
.header-right { display: flex; align-items: center; }
|
||||
// 支付标识:仅文字与图标,无底色
|
||||
.payment-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
white-space: nowrap;
|
||||
|
||||
&.wx-score { }
|
||||
&.member { }
|
||||
&.deposit { }
|
||||
|
||||
.badge-icon { width: 32rpx; height: 26rpx; margin-right: 8rpx; }
|
||||
.badge-text { font-size: 22rpx; color: #07c160; font-weight: 500; background: transparent; }
|
||||
.divider { margin: 0 6rpx; color: #07c160; }
|
||||
}
|
||||
|
||||
// 不同支付方式的文字颜色
|
||||
.payment-badge.member {
|
||||
.badge-text, .divider { color: #1976D2; }
|
||||
}
|
||||
.payment-badge.deposit {
|
||||
.badge-text, .divider { color: #666; }
|
||||
}
|
||||
}
|
||||
|
||||
// 订单内容
|
||||
.order-body {
|
||||
padding: 24rpx;
|
||||
|
||||
.device-info {
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
|
||||
.device-left {
|
||||
flex: 1;
|
||||
margin-right: 20rpx;
|
||||
|
||||
.device-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.device-id {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.device-right {
|
||||
// 支付分标识
|
||||
.payment-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
white-space: nowrap;
|
||||
|
||||
&.wx-score {
|
||||
background: rgba(7, 193, 96, 0.08);
|
||||
|
||||
.badge-icon {
|
||||
width: 32rpx;
|
||||
height: 26rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.badge-text {
|
||||
font-size: 22rpx;
|
||||
color: #07c160;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.divider { margin: 0 6rpx; }
|
||||
.highlight { font-weight: 500; }
|
||||
}
|
||||
}
|
||||
|
||||
&.member {
|
||||
background: rgba(25, 118, 210, 0.08);
|
||||
|
||||
.badge-text {
|
||||
font-size: 22rpx;
|
||||
color: #1976D2;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&.deposit {
|
||||
background: #f5f5f5;
|
||||
|
||||
.badge-text {
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.order-times {
|
||||
position: relative;
|
||||
.time-row {
|
||||
display: flex;
|
||||
font-size: 26rpx;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.time-label { color: #999; width: 140rpx; }
|
||||
.time-value { color: #333; flex: 1; }
|
||||
}
|
||||
.arrow {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 订单底部
|
||||
.order-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 24rpx;
|
||||
background: #fafafa;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
|
||||
.footer-left { display: flex; align-items: center; }
|
||||
.renting { font-size: 26rpx; color: #333; display: flex; align-items: center; }
|
||||
.meta { display: flex; align-items: center; }
|
||||
.meta-item { font-size: 26rpx; color: #333; display: flex; align-items: center; margin-right: 28rpx; }
|
||||
.dot { width: 16rpx; height: 16rpx; background: #000; border-radius: 50%; margin-right: 12rpx; display: inline-block; }
|
||||
.currency { margin-right: 4rpx; color: #333; }
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
|
||||
.action-item {
|
||||
font-size: 26rpx;
|
||||
padding: 10rpx 30rpx;
|
||||
border-radius: 30rpx;
|
||||
margin-left: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
&.primary { background: #07c160; color: #fff; }
|
||||
&.secondary { background: #f5f5f5; color: #666; border: 1rpx solid #e0e0e0; }
|
||||
&:active { opacity: 0.8; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -249,6 +249,33 @@ export const uploadOssResource = (filePath) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 上传并更新用户头像(后端接口)
|
||||
export const uploadUserAvatar = (filePath) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
url: URL + '/app/user/uploadAvatar',
|
||||
filePath,
|
||||
name: 'file',
|
||||
header: {
|
||||
'appid': appid,
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
},
|
||||
success: (res) => {
|
||||
try {
|
||||
const parsed = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
|
||||
resolve(parsed)
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 获取系统配置(预留接口)
|
||||
// 期望后端返回形如:{ code: 200, data: { expressReturnCountdownSeconds: number } }
|
||||
export const getSystemConfig = () => {
|
||||
|
||||
@@ -43,7 +43,17 @@
|
||||
{
|
||||
"path": "pages/my/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "个人中心"
|
||||
"navigationBarTitleText": "个人中心",
|
||||
"navigationBarBackgroundColor": "#D1FFE1",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/setting/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "设置",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -125,6 +135,14 @@
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/order/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "订单详情",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/expressReturn/index",
|
||||
"style": {
|
||||
@@ -140,6 +158,14 @@
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/search/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "查找设备",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/waiting/index",
|
||||
"style": {
|
||||
|
||||
@@ -1,44 +1,19 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 顶部设备信息 -->
|
||||
<view class="device-header">
|
||||
<view class="device-status-card" :class="deviceStatus.class">
|
||||
<view class="status-indicator"></view>
|
||||
<text class="status-text">{{ deviceStatus.text }}</text>
|
||||
</view>
|
||||
<view class="device-title">
|
||||
<text class="name">共享风扇</text>
|
||||
<view class="device-meta">
|
||||
<text class="id-label">设备号:</text>
|
||||
<text class="id-value">{{ deviceId }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 设备信息卡片 -->
|
||||
<view class="card device-info-card">
|
||||
<view class="card-row">
|
||||
<view class="card-item">
|
||||
<view class="item-icon location-icon">
|
||||
<!-- <uni-icons type="location" size="24" color="#fff"></uni-icons> -->
|
||||
<image src="/static/images/location-map.svg" mode="aspectFill"
|
||||
style="width: 45rpx;height: 45rpx;"></image>
|
||||
<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="item-content">
|
||||
<text class="item-label">当前位置</text>
|
||||
<text class="item-value">{{ deviceLocation }}</text>
|
||||
<view class="device-status" :class="deviceStatus.class">
|
||||
<text class="status-text">{{ deviceStatus.text }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="card-item">
|
||||
<view class="item-icon battery-icon" :class="{ 'battery-low': batteryLevel < 20 }">
|
||||
<image src="/static/images/Electricity.svg" mode="aspectFill"
|
||||
style="width: 45rpx;height: 45rpx;"></image>
|
||||
</view>
|
||||
<view class="item-content">
|
||||
<text class="item-label">电池电量</text>
|
||||
<text class="item-value">{{ batteryLevel }}%</text>
|
||||
</view>
|
||||
</view> -->
|
||||
<view class="device-id">
|
||||
<text class="id-label">设备号:</text>
|
||||
<text class="id-value">{{ deviceId }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -50,46 +25,33 @@
|
||||
|
||||
<view class="pricing-banner">
|
||||
<view class="pricing-main">
|
||||
<text class="price">¥{{deviceFeeConfig.maxHourPrice}}</text>
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price">{{ deviceFeeConfig.maxHourPrice || '5.00' }}</text>
|
||||
<text class="unit">/小时</text>
|
||||
</view>
|
||||
<text class="cap-price">封顶 ¥{{deviceInfo.depositAmount}}</text>
|
||||
</view>
|
||||
|
||||
<view class="pricing-rules">
|
||||
<!-- <view class="rule-item">
|
||||
<view class="rule-dot"></view>
|
||||
<text class="rule-text">前15分钟内归还<text class="highlight">免费</text></text>
|
||||
</view> -->
|
||||
<view class="rule-item">
|
||||
<view class="rule-dot"></view>
|
||||
<text class="rule-text">不足60分钟按60分钟计费</text>
|
||||
</view>
|
||||
<view class="rule-item">
|
||||
<view class="rule-dot"></view>
|
||||
<text class="rule-text">持续计费至99元视为买断</text>
|
||||
</view>
|
||||
<view class="cap-badge">
|
||||
<text class="cap-text">{{ deviceInfo.depositAmount || '99' }}元封顶</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 手机号输入 -->
|
||||
<!-- <view class="card phone-card" v-if="!hasActiveOrder">
|
||||
<view class="card-header">
|
||||
<text class="card-title">联系方式</text>
|
||||
<view class="pricing-info">
|
||||
<view class="info-icon">
|
||||
<text class="icon-text">ⓘ</text>
|
||||
</view>
|
||||
<view class="phone-input-container">
|
||||
<view class="input-wrapper">
|
||||
<text class="prefix">+86</text>
|
||||
<input type="number" class="phone-input" maxlength="11" placeholder="请输入手机号码"
|
||||
v-model="phoneNumber" />
|
||||
<text class="info-text">5元/小时,45元/24小时</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>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 使用须知 -->
|
||||
<!-- 使用说明 -->
|
||||
<view class="card notice-card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">使用须知</text>
|
||||
<text class="card-title">使用说明</text>
|
||||
</view>
|
||||
<view class="notice-items">
|
||||
<view class="notice-item">
|
||||
@@ -98,35 +60,26 @@
|
||||
</view>
|
||||
<view class="notice-item">
|
||||
<view class="notice-dot"></view>
|
||||
<text class="notice-text">请在指定区域内使用设备</text>
|
||||
<text class="notice-text">超出使用时间将自动按小时计费</text>
|
||||
</view>
|
||||
<view class="notice-item">
|
||||
<view class="notice-dot"></view>
|
||||
<text class="notice-text">归还时请确保设备完好,避免损坏</text>
|
||||
<text class="notice-text">请在指定区域内使用设备</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作区 -->
|
||||
<view class="footer">
|
||||
|
||||
|
||||
<button class="rent-button" :class="{ 'return-button': hasActiveOrder }"
|
||||
@click="handleRent('wx-score-pay')">
|
||||
<text>{{ hasActiveOrder ? '归还设备' : '免押租借' }}</text>
|
||||
<text>{{ hasActiveOrder ? '归还设备' : '免押金租借' }}</text>
|
||||
</button>
|
||||
<view class="wechat-credit">
|
||||
<image src="/static/images/wxpayflag.png" mode="aspectFit" class="wx-icon"></image>
|
||||
<view class="credit-text">
|
||||
<text>微信支付分</text>
|
||||
<text class="credit-divider">|</text>
|
||||
<text class="credit-score">550分及以上优享</text>
|
||||
<text class="credit-text">微信支付分 <text class="divider">|</text> 550分以上优享</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="" style="align-items: center;align-content: center;text-align: center;line-height: 50rpx;"
|
||||
@click="handleRent('wx-pay')">
|
||||
无法免押点这里</view> -->
|
||||
</view>
|
||||
|
||||
<!-- 手机号授权弹窗 -->
|
||||
<view class="phone-auth-popup" v-if="showPhoneAuthPopup">
|
||||
@@ -539,81 +492,11 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// 顶部设备信息
|
||||
.device-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.device-status-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.status-indicator {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
&.available {
|
||||
.status-indicator {
|
||||
background-color: #10c469;
|
||||
box-shadow: 0 0 10rpx rgba(16, 196, 105, 0.5);
|
||||
}
|
||||
|
||||
.status-text {
|
||||
color: #10c469;
|
||||
}
|
||||
}
|
||||
|
||||
&.offline {
|
||||
.status-indicator {
|
||||
background-color: #9a9a9a;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
color: #9a9a9a;
|
||||
}
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.device-title {
|
||||
.name {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.device-meta {
|
||||
margin-top: 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.id-label {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.id-value {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 卡片通用样式
|
||||
.card {
|
||||
background-color: #fff;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||
box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
padding: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
@@ -631,177 +514,161 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 设备信息卡
|
||||
// 设备信息卡片
|
||||
.device-info-card {
|
||||
.card-row {
|
||||
|
||||
.device-location {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
.card-item {
|
||||
.location-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
.item-icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-right: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 30rpx;
|
||||
color: #fff;
|
||||
|
||||
&.location-icon {
|
||||
background: linear-gradient(135deg, #40c9ff, #32a5ff);
|
||||
|
||||
// &::before {
|
||||
// content: "\e900"; // 使用字体图标,需要自行替换
|
||||
// }
|
||||
.location-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 12rpx;
|
||||
background-color: #10d673;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
&.battery-icon {
|
||||
background: linear-gradient(135deg, #33db92, #10c469);
|
||||
|
||||
// &::before {
|
||||
// content: "\e901"; // 使用字体图标,需要自行替换
|
||||
// }
|
||||
|
||||
&.battery-low {
|
||||
background: linear-gradient(135deg, #ff7676, #f54f4f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.item-label {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 30rpx;
|
||||
.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(to right, #f8f9ff, #e8f0ff);
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
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: flex-end;
|
||||
align-items: baseline;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.price-symbol {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #07c160;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.price {
|
||||
font-size: 60rpx;
|
||||
font-size: 80rpx;
|
||||
font-weight: bold;
|
||||
color: #ff6b6b;
|
||||
color: #07c160;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-left: 4rpx;
|
||||
margin-bottom: 10rpx;
|
||||
font-size: 32rpx;
|
||||
color: #07c160;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cap-price {
|
||||
margin-top: 10rpx;
|
||||
.cap-badge {
|
||||
background-color: #07c160;
|
||||
padding: 10rpx 32rpx;
|
||||
border-radius: 30rpx;
|
||||
|
||||
.cap-text {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
background-color: rgba(255, 107, 107, 0.1);
|
||||
padding: 6rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pricing-rules {
|
||||
.rule-item {
|
||||
.pricing-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.rule-dot {
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #ff6b6b;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.rule-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
|
||||
.highlight {
|
||||
color: #ff6b6b;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 手机号输入
|
||||
.phone-card {
|
||||
.phone-input-container {
|
||||
.input-wrapper {
|
||||
.info-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 88rpx;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 16rpx;
|
||||
padding: 0 24rpx;
|
||||
justify-content: center;
|
||||
margin-right: 12rpx;
|
||||
margin-top: 2rpx;
|
||||
|
||||
.prefix {
|
||||
.icon-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-right: 16rpx;
|
||||
padding-right: 16rpx;
|
||||
border-right: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.phone-input {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
padding-left: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.phone-tip {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 16rpx;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.info-text {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用须知
|
||||
// 使用说明卡片
|
||||
.notice-card {
|
||||
.notice-items {
|
||||
.notice-item {
|
||||
@@ -814,15 +681,17 @@
|
||||
}
|
||||
|
||||
.notice-dot {
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #32a5ff;
|
||||
background-color: #07c160;
|
||||
margin-right: 16rpx;
|
||||
margin-top: 12rpx;
|
||||
margin-top: 10rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.notice-text {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
@@ -838,58 +707,31 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #fff;
|
||||
padding: 20rpx 30rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||
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;
|
||||
// 添加一个变量来保存footer高度,方便管理和确保一致性
|
||||
--footer-height: 180rpx;
|
||||
|
||||
.wechat-credit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 10rpx;
|
||||
|
||||
.wx-icon {
|
||||
width: 50rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.credit-text {
|
||||
font-size: 24rpx;
|
||||
color: #07c160;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.credit-divider {
|
||||
margin: 0 10rpx;
|
||||
}
|
||||
|
||||
.credit-score {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rent-button {
|
||||
height: 92rpx;
|
||||
border-radius: 46rpx;
|
||||
width: 100%;
|
||||
height: 96rpx;
|
||||
border-radius: 48rpx;
|
||||
background: linear-gradient(135deg, #07c160, #10d673);
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
width: 90%;
|
||||
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 {
|
||||
@@ -897,6 +739,29 @@
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.wechat-credit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 16rpx;
|
||||
|
||||
.wx-icon {
|
||||
width: 48rpx;
|
||||
height: 38rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.credit-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
|
||||
.divider {
|
||||
margin: 0 8rpx;
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机号授权弹窗样式 */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view class="express-form-container">
|
||||
<!-- 页头:订单摘要(商务风卡片) -->
|
||||
<view class="card order-summary" v-if="orderInfo.orderNo">
|
||||
<!-- 订单摘要卡片 -->
|
||||
<view class="order-summary-card" v-if="orderInfo.orderNo">
|
||||
<view class="summary-row">
|
||||
<view class="label">订单号</view>
|
||||
<view class="value">{{ orderInfo.orderNo }}</view>
|
||||
@@ -11,32 +11,32 @@
|
||||
<view class="value">{{ orderInfo.deviceNo }}</view>
|
||||
</view>
|
||||
<view class="summary-row" v-if="orderInfo.startTime">
|
||||
<view class="label">开始时间</view>
|
||||
<view class="label">开放时间</view>
|
||||
<view class="value">{{ orderInfo.startTime }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 表单卡片 -->
|
||||
<view class="card form-card">
|
||||
<!-- 表单区域 -->
|
||||
<view class="form-section">
|
||||
<view class="form-title">填写快递归还信息</view>
|
||||
<view class="form-item">
|
||||
<view class="item-label">联系电话</view>
|
||||
<input class="item-input" type="number" v-model="phone" placeholder="请输入联系电话" maxlength="20"
|
||||
:focus-class="'item-input-focus'" />
|
||||
|
||||
<view class="input-wrapper">
|
||||
<view class="input-label">联系电话</view>
|
||||
<input class="input-field" type="number" v-model="phone" maxlength="20" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<view class="item-label">快递单号</view>
|
||||
<input class="item-input" type="text" v-model="trackingNumber"
|
||||
:placeholder="isFillMode ? '请输入需要补填的快递单号' : '请输入快递单号(可先留空)'" maxlength="40"
|
||||
:focus-class="'item-input-focus'" />
|
||||
|
||||
<view class="input-wrapper">
|
||||
<view class="input-label">快递单号</view>
|
||||
<input class="input-field" type="text" v-model="trackingNumber"
|
||||
:placeholder="isFillMode ? '请输入需要补填的快递单号' : '请输入快递单号(可先留空)'" maxlength="40" />
|
||||
</view>
|
||||
|
||||
<view class="tips" v-if="tipsText">{{ tipsText }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交操作条 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="action-item primary" :class="{ disabled: submitting }" hover-class="primary-hover"
|
||||
@click="!submitting && handleSubmit()">{{ isFillMode ? '确认补填' : '提交信息' }}</view>
|
||||
<!-- 提交按钮 -->
|
||||
<view class="submit-btn" :class="{ disabled: submitting }" @click="!submitting && handleSubmit()">
|
||||
{{ isFillMode ? '确认补填' : '提交信息' }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -249,130 +249,117 @@
|
||||
<style lang="scss" scoped>
|
||||
.express-form-container {
|
||||
min-height: 100vh;
|
||||
background: #f7f8fa;
|
||||
padding: 30rpx 30rpx 200rpx;
|
||||
background: #f5f5f5;
|
||||
padding: 40rpx 32rpx 180rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
/* 订单摘要卡片 - 淡绿色背景 */
|
||||
.order-summary-card {
|
||||
background: linear-gradient(135deg, #d4f4dd 0%, #e8f8ed 100%);
|
||||
border-radius: 24rpx;
|
||||
padding: 40rpx 32rpx;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.order-summary {
|
||||
.summary-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14rpx 0;
|
||||
border-bottom: 1rpx solid #f0f2f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
padding: 20rpx 0;
|
||||
|
||||
.label {
|
||||
font-size: 26rpx;
|
||||
color: #6b7280;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 28rpx;
|
||||
color: #111827;
|
||||
color: #333333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-card {
|
||||
/* 表单区域 */
|
||||
.form-section {
|
||||
background: transparent;
|
||||
margin-bottom: 40rpx;
|
||||
|
||||
.form-title {
|
||||
font-size: 30rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
margin-bottom: 16rpx;
|
||||
color: #333333;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
padding: 22rpx 0;
|
||||
border-bottom: 1rpx solid #f0f2f5;
|
||||
.input-wrapper {
|
||||
position: relative;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.item-label {
|
||||
font-size: 28rpx;
|
||||
color: #374151;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.item-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 50rpx;
|
||||
box-sizing: border-box;
|
||||
.input-label {
|
||||
position: absolute;
|
||||
top: -14rpx;
|
||||
left: 32rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
background: #ffffff;
|
||||
border: 1rpx solid #e5e7eb;
|
||||
border-radius: 12rpx;
|
||||
padding: 0rpx 14rpx;
|
||||
padding: 0 12rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
width: 100%;
|
||||
height: 100rpx;
|
||||
background: #ffffff;
|
||||
border: 2rpx solid #333333;
|
||||
border-radius: 48rpx;
|
||||
padding: 0 32rpx;
|
||||
font-size: 28rpx;
|
||||
color: #111827;
|
||||
line-height: 1.2;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
color: #333333;
|
||||
line-height: 100rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.item-input::placeholder {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.item-input-focus {
|
||||
border-color: #2563eb;
|
||||
box-shadow: 0 0 0 4rpx rgba(37, 99, 235, 0.15);
|
||||
.input-field::placeholder {
|
||||
color: #cccccc;
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin-top: 12rpx;
|
||||
margin-top: 16rpx;
|
||||
padding: 0 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
/* 提交按钮 */
|
||||
.submit-btn {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 24rpx 30rpx;
|
||||
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
|
||||
background: #ffffff;
|
||||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
display: block;
|
||||
|
||||
.action-item {
|
||||
width: 100%;
|
||||
height: 92rpx;
|
||||
left: 32rpx;
|
||||
right: 32rpx;
|
||||
bottom: 60rpx;
|
||||
bottom: calc(60rpx + env(safe-area-inset-bottom));
|
||||
height: 96rpx;
|
||||
background: linear-gradient(135deg, #7cd89f 0%, #6bc98c 100%);
|
||||
border-radius: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 44rpx;
|
||||
font-size: 30rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 8rpx 24rpx rgba(124, 216, 159, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.primary {
|
||||
background: #1f2937;
|
||||
color: #fff;
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 4rpx 16rpx rgba(124, 216, 159, 0.2);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.primary-hover {
|
||||
background: #111827;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,42 +1,42 @@
|
||||
<template>
|
||||
<view class="express-return-container">
|
||||
<!-- 页面标题 -->
|
||||
<!-- <view class="page-header">
|
||||
<text class="page-title">快递归还</text>
|
||||
<text class="page-subtitle">查看您的快递归还记录</text>
|
||||
</view> -->
|
||||
<!-- 收件信息卡片 -->
|
||||
<view class="recipient-info-card">
|
||||
<view class="info-header">收件信息</view>
|
||||
<view class="info-content">
|
||||
<text class="recipient-name">风电者 18163601305</text>
|
||||
<text class="recipient-address">湖南省长沙市岳麓区麓谷街道新长海尖科技园A2栋623</text>
|
||||
</view>
|
||||
<view class="copy-all-btn" @click="copyAllInfo">
|
||||
<text class="btn-text">一键复制全部信息</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 列表容器 -->
|
||||
<view class="list-container">
|
||||
<view class="return-item" v-for="(item, index) in returnList" :key="index" @click="handleItemClick(item)">
|
||||
<!-- 左侧图标 -->
|
||||
<view class="item-icon">
|
||||
<view class="icon-wrapper" :class="getStatusClass(item.status)">
|
||||
<text class="icon-text">{{ getStatusIcon(item.status) }}</text>
|
||||
</view>
|
||||
<!-- 左侧图标区域 -->
|
||||
<view class="item-left">
|
||||
<view class="avatar-placeholder"></view>
|
||||
</view>
|
||||
|
||||
<!-- 中间内容 -->
|
||||
<!-- 中间内容区域 -->
|
||||
<view class="item-content">
|
||||
<view class="item-header">
|
||||
<text class="tracking-number">运单号:{{ item.logisticsTrackingNumber }}</text>
|
||||
<!-- <text class="express-company">{{ item.expressCompany }}</text> -->
|
||||
<view class="content-header">
|
||||
<text class="status-label">{{ getStatusText(item.status) }}</text>
|
||||
</view>
|
||||
<view class="item-details">
|
||||
<text class="return-address">归还地址:{{ item.returnAddress }}</text>
|
||||
<text class="return-time">填写时间:{{ item.returnTime }}</text>
|
||||
</view>
|
||||
<view class="item-footer">
|
||||
<text class="package-info">订单号:{{ item.orderId }}</text>
|
||||
<text class="user-phone" v-if="item.userPhone">用户电话:{{ item.userPhone }}</text>
|
||||
<view class="content-body">
|
||||
<text class="info-text">订单号:{{ item.orderId || '' }}</text>
|
||||
<text class="info-text">快递单号:{{ item.trackingNumber || '待填写' }}</text>
|
||||
<text class="info-text">用户电话:{{ item.userPhone || '' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧状态 -->
|
||||
<view class="item-status">
|
||||
<text class="status-text" :class="getStatusClass(item.status)">
|
||||
{{ getStatusText(item.status) }}
|
||||
</text>
|
||||
<!-- 右侧状态标签 -->
|
||||
<view class="item-right">
|
||||
<view class="status-badge" :class="getStatusClass(item.status)">
|
||||
{{ getStatusBadge(item.status) }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -57,6 +57,10 @@ const returnList = ref([])
|
||||
const loading = ref(false)
|
||||
const query = ref({ pageNum: 1, pageSize: 20 })
|
||||
|
||||
// 收件信息
|
||||
const recipientName = '风电者 18163601305'
|
||||
const recipientAddress = '湖南省长沙市岳麓区麓谷街道新长海尖科技园A2栋623'
|
||||
|
||||
const loadList = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
@@ -103,18 +107,32 @@ const getStatusClass = (status) => ({
|
||||
'pending': 'status-pending'
|
||||
}[status] || 'status-pending')
|
||||
|
||||
const getStatusIcon = (status) => ({
|
||||
'completed': '✓',
|
||||
'processing': '⏳',
|
||||
'pending': '⏸'
|
||||
}[status] || '⏸')
|
||||
|
||||
const getStatusText = (status) => ({
|
||||
'completed': '暂停计费中',
|
||||
'processing': '暂停计费中',
|
||||
'pending': '暂停计费中'
|
||||
}[status] || '暂停计费中')
|
||||
|
||||
const getStatusBadge = (status) => ({
|
||||
'completed': '已完成',
|
||||
'processing': '处理中',
|
||||
'pending': '待处理'
|
||||
}[status] || '待处理')
|
||||
|
||||
// 一键复制全部信息
|
||||
const copyAllInfo = () => {
|
||||
const allInfo = `收件人:${recipientName}\n收件地址:${recipientAddress}`
|
||||
uni.setClipboardData({
|
||||
data: allInfo,
|
||||
success: () => {
|
||||
uni.showToast({ title: '全部信息已复制', icon: 'success' })
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({ title: '复制失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 点击列表项
|
||||
const handleItemClick = (item) => {
|
||||
console.log('点击了归还记录:', item)
|
||||
@@ -132,145 +150,135 @@ onMounted(loadList)
|
||||
<style lang="scss">
|
||||
.express-return-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f8f9fa;
|
||||
background-color: #f5f5f5;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 40rpx;
|
||||
padding: 20rpx 0;
|
||||
// 收件信息卡片
|
||||
.recipient-info-card {
|
||||
background: linear-gradient(135deg, #D1FFE1 0%, #B8F5D0 100%);
|
||||
border-radius: 16rpx;
|
||||
padding: 24rpx 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
display: block;
|
||||
font-size: 36rpx;
|
||||
.info-header {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 10rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
display: block;
|
||||
.info-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.recipient-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #1a1a1a;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.recipient-address {
|
||||
font-size: 26rpx;
|
||||
color: #333333;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.copy-all-btn {
|
||||
padding: 16rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 12rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 28rpx;
|
||||
color: #7f8c8d;
|
||||
color: #27ae60;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
// 列表容器
|
||||
.list-container {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.return-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 30rpx 24rpx;
|
||||
align-items: flex-start;
|
||||
padding: 28rpx 24rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
transition: background-color 0.2s ease;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||||
position: relative;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #f8f9fa;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
|
||||
&.status-completed {
|
||||
background-color: #e8f5e8;
|
||||
color: #27ae60;
|
||||
}
|
||||
|
||||
&.status-processing {
|
||||
background-color: #fff3cd;
|
||||
color: #f39c12;
|
||||
}
|
||||
|
||||
&.status-pending {
|
||||
background-color: #f8f9fa;
|
||||
color: #6c757d;
|
||||
}
|
||||
}
|
||||
|
||||
.item-content {
|
||||
flex: 1;
|
||||
// 左侧头像区域
|
||||
.item-left {
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
margin-bottom: 12rpx;
|
||||
.avatar-placeholder {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.express-company {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.tracking-number {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #7f8c8d;
|
||||
}
|
||||
|
||||
.item-details {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.return-address,
|
||||
.return-time {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
color: #34495e;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.item-footer {
|
||||
margin-top: 8rpx;
|
||||
// 中间内容区域
|
||||
.item-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.package-info {
|
||||
font-size: 24rpx;
|
||||
color: #95a5a6;
|
||||
margin-bottom: 4rpx;
|
||||
.content-header {
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.user-phone {
|
||||
font-size: 24rpx;
|
||||
color: #95a5a6;
|
||||
.status-label {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.item-status {
|
||||
text-align: right;
|
||||
.content-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
.info-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
padding: 8rpx 16rpx;
|
||||
color: #666666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
// 右侧状态标签
|
||||
.item-right {
|
||||
position: absolute;
|
||||
top: 28rpx;
|
||||
right: 24rpx;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
|
||||
&.status-completed {
|
||||
background-color: #e8f5e8;
|
||||
@@ -283,20 +291,18 @@ onMounted(loadList)
|
||||
}
|
||||
|
||||
&.status-pending {
|
||||
background-color: #f8f9fa;
|
||||
color: #6c757d;
|
||||
background-color: #f0f0f0;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
// 空状态
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 40rpx;
|
||||
// background-color: #ffffff;
|
||||
// border-radius: 16rpx;
|
||||
// box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
@@ -306,6 +312,6 @@ onMounted(loadList)
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #7f8c8d;
|
||||
color: #999999;
|
||||
}
|
||||
</style>
|
||||
@@ -127,12 +127,28 @@ export default {
|
||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.04);
|
||||
|
||||
.contact-title {
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
display: inline-block;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20rpx;
|
||||
border-left: 8rpx solid #1976D2;
|
||||
padding-left: 20rpx;
|
||||
padding-bottom: 8rpx;
|
||||
width:fit-content;
|
||||
|
||||
&::after {
|
||||
z-index: -1;
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 8rpx;
|
||||
width: 88%;
|
||||
height: 16rpx;
|
||||
border-radius: 20rpx;
|
||||
// background: transparent;
|
||||
background: #07C160; // 底部高亮(微信绿)
|
||||
}
|
||||
}
|
||||
|
||||
.contact-content {
|
||||
|
||||
@@ -14,23 +14,30 @@
|
||||
|
||||
<!-- 底部操作栏:附近设备 / 扫码使用 / 我的 -->
|
||||
<view class="bottom-actions">
|
||||
<view class="action-btn secondary small btn-nearby" @click="showLocationList">
|
||||
<!-- <view class="action-btn secondary small btn-nearby" @click="showLocationList">
|
||||
<view class="icon-wrap">
|
||||
<image class="action-icon" src="/static/map.png" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="action-label">附近设备</text>
|
||||
</view> -->
|
||||
|
||||
<view class="action-btn secondary small btn-nearby" @click="openPopup">
|
||||
<view class="icon-wrap">
|
||||
<image src="/static/use_help.png" class="action-icon" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="action-label">使用指南</text>
|
||||
</view>
|
||||
|
||||
<view class="action-btn primary btn-scan" @click="handleScan">
|
||||
<view class="icon-wrap">
|
||||
<image class="action-icon" src="/static/scan-icon.png" mode="aspectFit" />
|
||||
<image class="action-icon" src="/static/scan-icon.png" mode="aspectFill" />
|
||||
</view>
|
||||
<text class="action-label">扫码使用</text>
|
||||
<text class="primary-label">扫码使用</text>
|
||||
</view>
|
||||
|
||||
<view class="action-btn secondary small btn-my" @click="goMy">
|
||||
<view class="icon-wrap">
|
||||
<image class="action-icon" src="/static/user-active.png" mode="aspectFit" />
|
||||
<image class="action-icon" src="/static/user.png" mode="aspectFit" />
|
||||
</view>
|
||||
<text class="action-label">个人中心</text>
|
||||
</view>
|
||||
@@ -46,55 +53,17 @@
|
||||
|
||||
|
||||
|
||||
<!-- 场地列表弹窗 -->
|
||||
<view class="location-popup" v-if="showLocationPopup">
|
||||
<view class="popup-mask" @click="hideLocationList"></view>
|
||||
<view class="location-sheet" :class="{ 'expanded': isExpanded }">
|
||||
<!-- <view class="sheet-handle" @click="toggleSheet">
|
||||
<view class="handle-bar"></view>
|
||||
</view> -->
|
||||
|
||||
<view class="sheet-header">
|
||||
<text class="sheet-title">附近设备场地 ({{ filteredPositions.length }})</text>
|
||||
<view class="close-btn" @click="hideLocationList">
|
||||
<!-- <image class="close-icon" src="/static/scan-icon.png" mode="aspectFit" /> -->
|
||||
<uv-icon name='close'></uv-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="sheet-content" scroll-y="true">
|
||||
<view class="position-item" v-for="(item, index) in filteredPositions" :key="item.positionId"
|
||||
@click="selectPositionFromPopup(item)">
|
||||
<view class="position-info">
|
||||
<view class="position-name">{{ item.name }}</view>
|
||||
<view class="status-tag" :class="item.status">
|
||||
<text>{{ item.status === 'online' ? '可租借/归还' : '不可租借/归还' }}</text>
|
||||
</view>
|
||||
<view class="position-time" v-if="item.workTime && item.workTime !== '0'">
|
||||
<text>营业时间:{{ item.workTime }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="position-actions">
|
||||
<view class="distance-info" v-if="item.distance">
|
||||
<text>{{ item.distance }}</text>
|
||||
</view>
|
||||
<view class="status-tag" :class="item.status">
|
||||
<text>{{ item.status === 'online' ? '营业中' : '暂停服务' }}</text>
|
||||
</view>
|
||||
|
||||
<view class="nav-btn" @click.stop="navigateToPosition(item)">
|
||||
<text>导航</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="empty-state" v-if="filteredPositions.length === 0 && !isLoading">
|
||||
<image class="empty-icon" src="/static/scan-icon.png" mode="aspectFit" />
|
||||
<text class="empty-text">附近5公里内暂无设备</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 场地列表弹窗组件 -->
|
||||
<LocationListSheet
|
||||
:show="showLocationPopup"
|
||||
:expanded="isExpanded"
|
||||
:positions="filteredPositions"
|
||||
:isLoading="isLoading"
|
||||
title="附近设备场地"
|
||||
@close="hideLocationList"
|
||||
@select="selectPositionFromPopup"
|
||||
@navigate="navigateToPosition"
|
||||
/>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-overlay" v-if="isLoading">
|
||||
@@ -124,27 +93,37 @@
|
||||
</view>
|
||||
</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>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uv-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const redirectToLogin = () => {
|
||||
try {
|
||||
const pages = getCurrentPages()
|
||||
const current = pages && pages.length ? pages[pages.length - 1] : null
|
||||
const route = current && current.route ? ('/' + current.route) : '/pages/index/index'
|
||||
const query = current && current.options ? Object.keys(current.options).map(k =>
|
||||
`${k}=${encodeURIComponent(current.options[k])}`).join('&') : ''
|
||||
const redirect = encodeURIComponent(query ? `${route}?${query}` : route)
|
||||
uni.reLaunch({
|
||||
url: `/pages/login/index?redirect=${redirect}`
|
||||
})
|
||||
} catch (e) {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
})
|
||||
}
|
||||
}
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
@@ -173,6 +152,7 @@
|
||||
// 同样需要使用相对路径引入组件
|
||||
// 注意:从 pages/index/ 目录访问 components/ 需要使用 ../../components/ 路径
|
||||
import MapComponent from '../../components/MapComponent.vue'
|
||||
import LocationListSheet from '../../components/LocationListSheet.vue'
|
||||
// 开启右上角分享菜单(仅 mp-weixin 有效)
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.showShareMenu({
|
||||
@@ -192,12 +172,39 @@
|
||||
const isLocationInitialized = ref(false)
|
||||
const showLocationPopup = ref(false)
|
||||
|
||||
// 使用指南步骤
|
||||
const guideSteps = ref([
|
||||
{ title: '扫码使用', desc: '找到附近设备,扫描设备上的二维码' },
|
||||
{ title: '免押金支付', desc: '无需支付押金,使用支付分免押即可完成租借' },
|
||||
{ title: '开始使用', desc: '设备自动解锁,风扇弹出后取出即可开始使用' },
|
||||
{ title: '归还设备', desc: '使用完毕后,按照设备规格要求将风扇还入即可结束订单' }
|
||||
])
|
||||
|
||||
// 使用指南步骤
|
||||
// 使用指南已取消
|
||||
|
||||
// 滚动通知内容
|
||||
// const noticeText = ref('消费规则:每小时5元,不足1小时按1小时计费,最高24小时封顶,请爱护设备,使用后请及时归还')
|
||||
|
||||
|
||||
const redirectToLogin = () => {
|
||||
try {
|
||||
const pages = getCurrentPages()
|
||||
const current = pages && pages.length ? pages[pages.length - 1] : null
|
||||
const route = current && current.route ? ('/' + current.route) : '/pages/index/index'
|
||||
const query = current && current.options ? Object.keys(current.options).map(k =>
|
||||
`${k}=${encodeURIComponent(current.options[k])}`).join('&') : ''
|
||||
const redirect = encodeURIComponent(query ? `${route}?${query}` : route)
|
||||
uni.reLaunch({
|
||||
url: `/pages/login/index?redirect=${redirect}`
|
||||
})
|
||||
} catch (e) {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const noticeText = ref('')
|
||||
const getNoticeText = async () => {
|
||||
const parasm = {
|
||||
@@ -219,6 +226,7 @@
|
||||
|
||||
// 组件引用
|
||||
const mapRef = ref(null)
|
||||
const guidePopup = ref(null)
|
||||
|
||||
// 计算属性
|
||||
const searchPlaceholder = computed(() => {
|
||||
@@ -575,7 +583,7 @@
|
||||
} else if (inUseRes.statusCode == 200 && inUseRes.data.code == 200 && inUseRes.data.data) {
|
||||
const inUseOrder = inUseRes.data.data
|
||||
uni.reLaunch({
|
||||
url: `/pages/return/index?orderId=${inUseOrder.orderId}&deviceId=${deviceNo || inUseOrder.deviceNo}`
|
||||
url: `/pages/order/detail?orderId=${inUseOrder.orderId}&deviceId=${deviceNo || inUseOrder.deviceNo}`
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -659,6 +667,19 @@
|
||||
showPhoneAuthPopup.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 使用指南弹窗控制
|
||||
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) {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
@@ -1038,7 +1059,7 @@
|
||||
font-weight: 600;
|
||||
|
||||
&.primary {
|
||||
background: #07c160;
|
||||
background: #3EAB64;
|
||||
color: #fff;
|
||||
border-radius: 64rpx;
|
||||
height: 100rpx;
|
||||
@@ -1053,8 +1074,9 @@
|
||||
// background: rgba(255, 255, 255, 0.25);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10rpx;
|
||||
// margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1071,8 +1093,8 @@
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
background: #f7f8fa;
|
||||
border: 2rpx solid #eaeaea;
|
||||
// background: #f7f8fa;
|
||||
// border: 2rpx solid #eaeaea;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -1093,15 +1115,36 @@
|
||||
}
|
||||
|
||||
.btn-scan {
|
||||
/* 尺寸与布局(覆盖 primary 的默认尺寸) */
|
||||
height: 112rpx;
|
||||
min-width: 360rpx;
|
||||
padding: 0 32rpx;
|
||||
border-radius: 56rpx;
|
||||
flex-direction: row;
|
||||
gap: 14rpx;
|
||||
// gap: 16rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
/* 左侧图标容器 */
|
||||
.icon-wrap {
|
||||
margin-bottom: 0;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 50%;
|
||||
margin: 0;
|
||||
margin-right: 12rpx;
|
||||
// background: rgba(255, 255, 255, 0.25);
|
||||
// background: rgba(255, 255, 255, 0.18);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 图标大小与反色 */
|
||||
.action-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1285,7 +1328,6 @@
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
|
||||
.map-notice {
|
||||
margin: 0 20rpx;
|
||||
// position: absolute;
|
||||
@@ -1295,4 +1337,111 @@
|
||||
border-radius: 20rpx;
|
||||
z-index: 15;
|
||||
}
|
||||
|
||||
/* 使用指南弹窗样式 */
|
||||
.guide-popup {
|
||||
width: 640rpx;
|
||||
max-width: 86vw;
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx 24rpx 16rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.guide-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.guide-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.guide-close {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f5f5f5;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.guide-content {
|
||||
max-height: 600rpx;
|
||||
padding: 8rpx 4rpx 4rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.guide-step {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16rpx;
|
||||
padding: 16rpx 0;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.step-index {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
background: #07c160;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.step-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.guide-actions {
|
||||
margin-top:20rpx;
|
||||
}
|
||||
|
||||
.primary-btn {
|
||||
// width: 100%;
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
padding: 20rpx;
|
||||
border-radius: 40rpx;
|
||||
background: #DDEFE3;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
.primary-label{
|
||||
color:#ffffff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -1,124 +1,109 @@
|
||||
<template>
|
||||
<view class="profile-container">
|
||||
<!-- 用户信息区域 -->
|
||||
<view class="header-section">
|
||||
<view class="user-profile" @click="handleUserProfileClick">
|
||||
<view class="avatar-container">
|
||||
<image class="avatar" v-if="userInfo.avatar" :src="userInfo.avatar" mode="aspectFill">
|
||||
</image>
|
||||
<image v-else class="avatar" src="@/static/user-active.png" mode="aspectFill"></image>
|
||||
<view class="avatar-badge" v-if="userInfo.isAdmin"></view>
|
||||
<view class="my-page">
|
||||
<view class="user-card">
|
||||
<view class="avatar-box">
|
||||
<image class="avatar" v-if="userInfo.avatar" :src="userInfo.avatar" mode="aspectFill"></image>
|
||||
<image v-else class="avatar" src="@/static/head.png" mode="aspectFill"></image>
|
||||
<!-- 覆盖在头像上的微信选择头像授权按钮,仅小程序生效 -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<button class="avatar-choose-btn" open-type="chooseAvatar" @chooseavatar="onChooseAvatar"></button>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="user-text">
|
||||
<view class="nickname">{{ userInfo.nickName || '点击登录' }}</view>
|
||||
<view class="subtext">{{ userInfo.phone ? maskPhone(userInfo.phone) : '授权登录后可查看订单与资产' }}</view>
|
||||
</view>
|
||||
<uv-icon type="right" size="16" color="#999"></uv-icon>
|
||||
</view>
|
||||
|
||||
<view class="user-details" v-if="userInfo">
|
||||
<view class="username">{{ userInfo.nickName }}</view>
|
||||
<view class="user-id">{{ userInfo.phone ? maskPhone(userInfo.phone) : '未绑定手机号' }}</view>
|
||||
</view>
|
||||
|
||||
<view class="user-details" v-else>
|
||||
<view class="username">点击登录</view>
|
||||
<view class="user-id">授权登录后享受更多服务</view>
|
||||
<!-- <view class="assets-card">
|
||||
<view class="assets-left">
|
||||
<view class="label">押金余额</view>
|
||||
<view class="amount">¥{{ deposit }}</view>
|
||||
</view>
|
||||
|
||||
<view class="edit-profile">
|
||||
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 余额卡片 -->
|
||||
<view class="balance-card">
|
||||
<view class="balance-info">
|
||||
<view class="balance-label">押金余额</view>
|
||||
<view class="balance-amount">¥{{ deposit }}</view>
|
||||
</view>
|
||||
<view class="action-button" @click="handleWithdraw">
|
||||
<text>提现</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能区域列表 -->
|
||||
<view class="function-list">
|
||||
<!-- 订单中心 -->
|
||||
<view class="function-item" @click="navigateTo('/pages/order/index')">
|
||||
<view class="item-left">
|
||||
<view class="item-icon">
|
||||
<image src="/static/jl.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="item-title">订单中心</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 投诉建议 -->
|
||||
<view class="function-item" @click="navigateTo('/pages/feedback/index')">
|
||||
<view class="item-left">
|
||||
<view class="item-icon">
|
||||
<image src="/static/complaint.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="item-title">投诉建议</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 帮助中心 -->
|
||||
<view class="function-item" @click="navigateTo('/pages/help/index')">
|
||||
<view class="item-left">
|
||||
<view class="item-icon">
|
||||
<image src="/static/hlep.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="item-title">帮助中心</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="function-item" @click="navigateTo('/pages/expressReturn/index')">
|
||||
<view class="item-left">
|
||||
<view class="item-icon">
|
||||
<image src="/static/express.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="item-title">快递归还</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
<view class="function-item" @click="doLogout()">
|
||||
<view class="item-left">
|
||||
<view class="item-icon">
|
||||
<image src="/static/logout.png" mode="aspectFit"></image>
|
||||
</view>
|
||||
<text class="item-title">退出登录</text>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!--
|
||||
<view class="section-title">设置</view>
|
||||
|
||||
<view class="settings-list">
|
||||
<view
|
||||
v-for="(item, index) in settingItems"
|
||||
:key="index"
|
||||
class="settings-item"
|
||||
@click="item.action">
|
||||
<view class="settings-item-left">
|
||||
<uni-icons :type="item.icon" size="20" color="#4facfe"></uni-icons>
|
||||
<text>{{ item.name }}</text>
|
||||
</view>
|
||||
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||
<view class="assets-right" @click="handleWithdraw">
|
||||
<text class="withdraw-btn">提现</text>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- <u-popup ref="authPopup" mode="center" border-radius="15" width="600rpx" @open="onPopupOpen" @close="onPopupClose">
|
||||
<view class="section">
|
||||
<view class="banner-card">
|
||||
<image class="banner-image" src="/static/userCenter_swiper.png" mode="aspectFill"></image>
|
||||
</view>
|
||||
<!-- <view class="section-title">常用服务</view> -->
|
||||
<view class="list">
|
||||
<view class="list-item" @click="handleQuickReturn">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/express_return.png" mode="aspectFit"></image>
|
||||
<text class="title">快速归还</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('/pages/expressReturn/index')">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/express.png" mode="aspectFit"></image>
|
||||
<text class="title">快递归还记录</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('/pages/order/index')">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/orderList.png" mode="aspectFit"></image>
|
||||
<text class="title">我的订单</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('/pages/help/index')">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/customer-service.png" mode="aspectFit"></image>
|
||||
<text class="title">客服中心</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('/pages/feedback/index')">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/complaint.png" mode="aspectFit"></image>
|
||||
<text class="title">投诉与建议</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('/pages/legal/agreement')">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/peopleInWork.png" mode="aspectFit"></image>
|
||||
<text class="title">营业资质</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('/pages/serve/bagCheck/index')">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/other_device.png" mode="aspectFit"></image>
|
||||
<text class="title">合作加盟</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
<view class="list-item" @click="navigateTo('/pages/setting/index')">
|
||||
<view class="left">
|
||||
<image class="icon" src="/static/setting.png" mode="aspectFit"></image>
|
||||
<text class="title">设置</text>
|
||||
</view>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer-agreements">
|
||||
<view class="link-box">
|
||||
<text class="link" @click="navigateTo('/pages/legal/agreement')">《服务条款》</text>
|
||||
<text class="sep">|</text>
|
||||
<text class="link" @click="navigateTo('/pages/legal/privacy')">《隐私政策》</text>
|
||||
</view>
|
||||
<view class="version">v{{ appVersion }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 保留授权弹窗,暂不启用 -->
|
||||
<!--
|
||||
<u-popup ref="authPopup" mode="center" border-radius="15" width="600rpx" @open="onPopupOpen" @close="onPopupClose">
|
||||
<view class="auth-popup">
|
||||
<view class="auth-title">授权登录</view>
|
||||
<view class="auth-desc">获取您的微信头像、昵称等公开信息</view>
|
||||
@@ -127,7 +112,8 @@
|
||||
<button class="confirm-btn" @click="getUserProfile">确定</button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup> -->
|
||||
</u-popup>
|
||||
-->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -142,9 +128,9 @@
|
||||
wxLogin,
|
||||
getUserInfo
|
||||
} from '../../util/index.js';
|
||||
import {
|
||||
userLogout
|
||||
} from '@/config/user.js'
|
||||
import { uploadUserAvatar } from '../../config/user.js'
|
||||
import { URL } from '../../config/url.js'
|
||||
// 设置页执行退出登录,此页不再直接调用
|
||||
|
||||
// 响应式状态
|
||||
const userInfo = ref({});
|
||||
@@ -152,10 +138,12 @@
|
||||
const openId = ref('');
|
||||
const authPopup = ref(null); // u-popup 的引用
|
||||
const isPopupVisible = ref(false);
|
||||
const appVersion = ref('1.0.0');
|
||||
|
||||
// 页面加载时初始化
|
||||
onMounted(() => {
|
||||
getInfo();
|
||||
initVersion();
|
||||
});
|
||||
|
||||
// 获取用户信息
|
||||
@@ -194,6 +182,26 @@
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化应用版本号(多端兼容,取可用信息)
|
||||
const initVersion = () => {
|
||||
// #ifdef MP-WEIXIN
|
||||
try {
|
||||
const info = wx.getAccountInfoSync && wx.getAccountInfoSync();
|
||||
if (info && info.miniProgram && info.miniProgram.version) {
|
||||
appVersion.value = info.miniProgram.version;
|
||||
}
|
||||
} catch (e) {}
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
try {
|
||||
if (typeof plus !== 'undefined' && plus.runtime && plus.runtime.version) {
|
||||
appVersion.value = plus.runtime.version;
|
||||
}
|
||||
} catch (e) {}
|
||||
// #endif
|
||||
};
|
||||
|
||||
const redirectToLogin = () => {
|
||||
try {
|
||||
const pages = getCurrentPages()
|
||||
@@ -219,6 +227,52 @@
|
||||
});
|
||||
};
|
||||
|
||||
// 处理快速归还
|
||||
const handleQuickReturn = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '加载中'
|
||||
});
|
||||
|
||||
// 获取使用中的订单
|
||||
const res = await uni.request({
|
||||
url: `${URL}/app/order/inUse`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
});
|
||||
|
||||
uni.hideLoading();
|
||||
|
||||
if (res.statusCode === 401 || res.data?.code === 401 || res.data?.code === 40101) {
|
||||
redirectToLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200 && res.data.data) {
|
||||
const inUseOrder = res.data.data;
|
||||
// 跳转到统一订单详情页面
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/detail?orderId=${inUseOrder.orderId}&deviceId=${inUseOrder.deviceNo}`
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '暂无使用中的订单',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('获取使用中订单失败:', error);
|
||||
uni.showToast({
|
||||
title: '获取订单失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 处理提现按钮点击
|
||||
const handleWithdraw = () => {
|
||||
navigateTo('/pages/deposit/index');
|
||||
@@ -226,15 +280,49 @@
|
||||
|
||||
// 处理用户资料点击
|
||||
const handleUserProfileClick = () => {
|
||||
if (!userInfo.value) {
|
||||
// 打开授权弹窗
|
||||
openAuthPopup();
|
||||
} else {
|
||||
// 已登录用户点击个人资料,可以导航到编辑页面
|
||||
// navigateTo('/pages/profile/edit');
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
redirectToLogin()
|
||||
return
|
||||
}
|
||||
// #ifdef MP-WEIXIN
|
||||
getUserProfile()
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.showToast({ title: '请在微信小程序中使用此功能', icon: 'none' })
|
||||
// #endif
|
||||
};
|
||||
|
||||
// 小程序原生选择头像回调(需基础库>=2.21.2)
|
||||
const onChooseAvatar = async (e) => {
|
||||
try {
|
||||
const token = uni.getStorageSync('token')
|
||||
if (!token) {
|
||||
redirectToLogin()
|
||||
return
|
||||
}
|
||||
const avatarLocalPath = e?.detail?.avatarUrl
|
||||
if (!avatarLocalPath) {
|
||||
uni.showToast({ title: '未选择头像', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.showLoading({ title: '上传中...', mask: true })
|
||||
const uploadRes = await uploadUserAvatar(avatarLocalPath)
|
||||
const serverAvatar = uploadRes?.data?.url || uploadRes?.url || uploadRes?.data || ''
|
||||
if (serverAvatar) {
|
||||
userInfo.value = { ...userInfo.value, avatar: serverAvatar }
|
||||
uni.setStorageSync('userInfo', userInfo.value)
|
||||
}
|
||||
uni.showToast({ title: '头像已更新', icon: 'success' })
|
||||
await getInfo()
|
||||
} catch (err) {
|
||||
console.error('选择/上传头像失败:', err)
|
||||
uni.showToast({ title: '头像更新失败', icon: 'none' })
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
// 打开授权弹窗
|
||||
const openAuthPopup = () => {
|
||||
if (authPopup.value) {
|
||||
@@ -270,6 +358,7 @@
|
||||
success: (res) => {
|
||||
console.log('获取用户信息成功:', res);
|
||||
updateUserInfo(res.userInfo);
|
||||
uploadAvatarAndRefresh(res.userInfo);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取用户信息失败:', err);
|
||||
@@ -331,6 +420,46 @@
|
||||
}
|
||||
};
|
||||
|
||||
// 下载并上传头像,更新用户信息
|
||||
const uploadAvatarAndRefresh = async (wxUserInfo) => {
|
||||
try {
|
||||
const avatarUrl = wxUserInfo?.avatarUrl
|
||||
if (!avatarUrl) {
|
||||
uni.showToast({ title: '未获取到头像地址', icon: 'none' })
|
||||
return
|
||||
}
|
||||
// 下载微信头像为本地临时文件
|
||||
const tempFilePath = await new Promise((resolve, reject) => {
|
||||
uni.downloadFile({
|
||||
url: avatarUrl,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.tempFilePath) {
|
||||
resolve(res.tempFilePath)
|
||||
return
|
||||
}
|
||||
reject(new Error('头像下载失败'))
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
// 上传到后端
|
||||
const uploadRes = await uploadUserAvatar(tempFilePath)
|
||||
// 直接使用返回的头像地址(如果有),并刷新用户信息
|
||||
const serverAvatar = uploadRes?.data?.url || uploadRes?.url || uploadRes?.data || ''
|
||||
if (serverAvatar) {
|
||||
userInfo.value = { ...userInfo.value, avatar: serverAvatar }
|
||||
uni.setStorageSync('userInfo', userInfo.value)
|
||||
}
|
||||
uni.showToast({ title: '头像已更新', icon: 'success' })
|
||||
await getInfo()
|
||||
} catch (error) {
|
||||
console.error('头像上传失败:', error)
|
||||
uni.showToast({ title: '头像上传失败', icon: 'none' })
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭授权弹窗
|
||||
const closeAuthPopup = () => {
|
||||
if (authPopup.value) {
|
||||
@@ -362,222 +491,221 @@
|
||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||
}
|
||||
|
||||
const doLogout = async () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要退出登录吗?',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
const response = await userLogout();
|
||||
if (response.code == 200) {
|
||||
uni.showToast({
|
||||
title:'退出成功',
|
||||
icon:'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.removeStorageSync('token');
|
||||
uni.removeStorageSync('userInfo');
|
||||
uni.redirectTo({
|
||||
url: '/pages/login/index'
|
||||
})
|
||||
}, 1500)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// 退出登录移动至设置页
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.profile-container {
|
||||
.my-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
background-color: #ffffff;
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Header Section */
|
||||
.header-section {
|
||||
padding: 40rpx;
|
||||
background: linear-gradient(135deg, #07c160, #05a14e);
|
||||
position: relative;
|
||||
border-radius: 0 0 30rpx 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 10rpx 30rpx rgba(7, 193, 96, 0.25);
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
.user-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
padding: 24rpx 30rpx;
|
||||
|
||||
// background-color: #D1FFE1;
|
||||
background: linear-gradient(180deg, #D1FFE1 0%, #ffffff 100%);
|
||||
// border-bottom: 1rpx solid #f0f0f0;
|
||||
// margin: 0 20rpx;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
.banner-card {
|
||||
margin: 20rpx 30rpx 0 30rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 50rpx;
|
||||
overflow: hidden;
|
||||
border: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.banner-image {
|
||||
width: 100%;
|
||||
height: 260rpx;
|
||||
border-radius: 50rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.avatar-box {
|
||||
margin-right: 20rpx;
|
||||
position: relative;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 60rpx;
|
||||
border: 4rpx solid rgba(255, 255, 255, 0.6);
|
||||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1);
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50rpx;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.avatar-badge {
|
||||
/* 仅小程序端存在,此按钮覆盖在头像上捕获点击以触发选择头像 */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
.avatar-choose-btn {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 18rpx;
|
||||
background-color: #ff9500;
|
||||
border: 4rpx solid white;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
opacity: 0; /* 保持可点击但不可见 */
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.user-details {
|
||||
.user-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 36rpx;
|
||||
.nickname {
|
||||
font-size: 32rpx;
|
||||
color: #222222;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.user-id {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
.subtext {
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.edit-profile {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
padding: 10rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Balance Card */
|
||||
.balance-card {
|
||||
margin: 30rpx;
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
.assets-card {
|
||||
margin: 20rpx 0;
|
||||
padding: 24rpx 30rpx;
|
||||
background-color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.balance-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.balance-amount {
|
||||
font-size: 48rpx;
|
||||
font-weight: 600;
|
||||
color: #07c160;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
background: linear-gradient(135deg, #07c160, #05a14e);
|
||||
border-radius: 40rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 40rpx;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
font-size: 30rpx;
|
||||
box-shadow: 0 8rpx 16rpx rgba(7, 193, 96, 0.25);
|
||||
|
||||
&:active {
|
||||
opacity: 0.9;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
text {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* Function List */
|
||||
.function-list {
|
||||
margin: 30rpx;
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.function-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 32rpx 30rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.assets-left .label {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.item-left {
|
||||
.assets-left .amount {
|
||||
margin-top: 8rpx;
|
||||
font-size: 40rpx;
|
||||
color: #e2231a;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.withdraw-btn {
|
||||
display: inline-block;
|
||||
padding: 14rpx 28rpx;
|
||||
color: #e2231a;
|
||||
border: 1rpx solid #e2231a;
|
||||
border-radius: 6rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-top: 20rpx;
|
||||
margin: 0 20rpx 20rpx 20rpx;
|
||||
background-color: #ffffff;
|
||||
flex:1;
|
||||
// border-top: 1rpx solid #f0f0f0;
|
||||
// border-bottom: 1rpx solid #f0f0f0;
|
||||
border-radius: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
padding: 20rpx 30rpx;
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 28rpx 30rpx;
|
||||
border-top: 1rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.list-item:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-right: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
image {
|
||||
.icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
.title {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
.footer-agreements {
|
||||
margin: 40rpx 0 20rpx 0;
|
||||
position: absolute;
|
||||
bottom: 10rpx;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999999;
|
||||
font-size: 22rpx;
|
||||
|
||||
.link-box{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* Auth Popup */
|
||||
.footer-agreements .link {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.footer-agreements .sep {
|
||||
margin: 0 10rpx;
|
||||
color: #c0c0c0;
|
||||
}
|
||||
|
||||
.footer-agreements .version {
|
||||
margin-top: 10rpx;
|
||||
color: #c0c0c0;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
|
||||
/* 保留弹窗样式(未启用) */
|
||||
.auth-popup {
|
||||
background-color: white;
|
||||
background-color: #ffffff;
|
||||
width: 100%;
|
||||
padding: 40rpx;
|
||||
border-radius: 15rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.auth-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.auth-desc {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
color: #666666;
|
||||
text-align: center;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
@@ -595,19 +723,5 @@
|
||||
text-align: center;
|
||||
border-radius: 40rpx;
|
||||
font-size: 28rpx;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background-color: #f0f0f0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
background: linear-gradient(135deg, #07c160, #05a14e);
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
@@ -11,96 +11,22 @@
|
||||
<!-- 订单列表 -->
|
||||
<view class="order-list">
|
||||
<view class="empty-state" v-if="orderList.length === 0">
|
||||
<view class="empty-icon"></view>
|
||||
<view class="empty-icon">
|
||||
<image src="/static/orderList.png" mode="aspectFill" class="empty-icon"></image>
|
||||
</view>
|
||||
<text class="empty-text">暂无订单记录</text>
|
||||
</view>
|
||||
|
||||
<view class="order-item" v-for="(order, index) in orderList" :key="index">
|
||||
<!-- 订单头部信息 -->
|
||||
<view class="order-header">
|
||||
<view class="order-id">
|
||||
<text>订单号:{{ order.orderNo }}</text>
|
||||
</view>
|
||||
<view class="order-status" :class="orderStatusMap[order.orderStatus]?.class">
|
||||
{{ orderStatusMap[order.orderStatus]?.text }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单内容 -->
|
||||
<view class="order-body">
|
||||
<view class="device-info">
|
||||
<view class="device-left">
|
||||
<view class="device-name">共享风扇</view>
|
||||
<view class="device-id">设备号:{{ order.deviceId }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付方式标识 -->
|
||||
<view class="device-right">
|
||||
<!-- 微信支付分标识 -->
|
||||
<view class="payment-badge wx-score" v-if="order.payWay == 'wx_score_pay'">
|
||||
<image src="/static/images/wxpayflag.png" mode="aspectFit" class="badge-icon"></image>
|
||||
<view class="badge-text">
|
||||
<text>微信支付分</text>
|
||||
<text class="divider">|</text>
|
||||
<text class="highlight">免押租借</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 会员订单标识 -->
|
||||
<view class="payment-badge member" v-else-if="order.payWay == 'wx_member_pay'">
|
||||
<text class="badge-text">会员订单</text>
|
||||
</view>
|
||||
<!-- 押金租借标识(微信支付订单) -->
|
||||
<view class="payment-badge deposit" v-else-if="order.payWay == 'wx_pay'">
|
||||
<text class="badge-text">押金租借</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单时间信息 -->
|
||||
<view class="order-times">
|
||||
<view class="time-row">
|
||||
<text class="time-label">开始时间:</text>
|
||||
<text class="time-value">{{ order.startTime }}</text>
|
||||
</view>
|
||||
<view class="time-row">
|
||||
<text class="time-label">结束时间:</text>
|
||||
<text class="time-value">{{ order.endTime || '-' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单底部 -->
|
||||
<view class="order-footer">
|
||||
<view class="price">¥{{ order.amount }}</view>
|
||||
|
||||
<view class="actions">
|
||||
<!-- 待支付状态显示支付和取消按钮 -->
|
||||
<view v-if="order.status === 'waiting_for_payment' || order.orderStatus === 'waiting_for_payment'" class="action-item primary" @click="handlePayment(order)">
|
||||
立即支付
|
||||
</view>
|
||||
<view v-if="order.status === 'waiting_for_payment' || order.orderStatus === 'waiting_for_payment'" class="action-item secondary" @click="handleCancelOrder(order)">
|
||||
取消订单
|
||||
</view>
|
||||
|
||||
<!-- 使用中状态显示归还设备按钮 -->
|
||||
<view v-if="order.status=='in_used' || order.orderStatus=='in_used'" class="action-item primary"
|
||||
@click="navigateToReturn(order.deviceId, order.orderId)">
|
||||
归还设备
|
||||
</view>
|
||||
|
||||
<!-- 查看详情按钮对所有订单都显示 -->
|
||||
<view class="action-item secondary" @click="navigateToDetails(order)">
|
||||
查看详情
|
||||
</view>
|
||||
|
||||
<!-- 同步订单状态按钮 -->
|
||||
<!-- <view v-if="order.status === 'waiting_for_payment' || order.orderStatus === 'waiting_for_payment'" class="action-item secondary"
|
||||
@click="getOrderStatus(order)">
|
||||
同步状态
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<OrderItemCard
|
||||
v-for="(order, index) in orderList"
|
||||
:key="index"
|
||||
:order="order"
|
||||
:orderStatusMap="orderStatusMap"
|
||||
@pay="handlePayment"
|
||||
@cancel="handleCancelOrder"
|
||||
@return-device="onReturnDevice"
|
||||
@details="navigateToDetails"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -111,6 +37,7 @@
|
||||
reactive,
|
||||
onMounted
|
||||
} from 'vue';
|
||||
import OrderItemCard from '../../components/OrderItemCard.vue';
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app';
|
||||
@@ -215,7 +142,9 @@
|
||||
payWay: orderData.payWay,
|
||||
startTime: orderStartTime,
|
||||
endTime: orderData.endTime || '',
|
||||
amount: orderData.payAmount || orderData.actualDeviceAmount || '0.00'
|
||||
positionName: orderData.positionName || orderData.positionLocation || '',
|
||||
deviceName: orderData.deviceName || '',
|
||||
amount: orderData.payAmount || orderData.actualDeviceAmount || orderData.currentFee || orderData.residueAmount || '0.00'
|
||||
};
|
||||
|
||||
// 将订单添加到列表开头
|
||||
@@ -270,7 +199,9 @@
|
||||
payWay: item.payWay,
|
||||
startTime: orderStartTime,
|
||||
endTime: item.endTime || '',
|
||||
amount: item.payAmount || item.actualDeviceAmount || '0.00'
|
||||
positionName: item.positionName || item.positionLocation || '',
|
||||
deviceName: item.deviceName || '',
|
||||
amount: item.payAmount || item.actualDeviceAmount || item.currentFee || item.residueAmount || '0.00'
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -302,19 +233,21 @@
|
||||
}
|
||||
};
|
||||
|
||||
// 跳转到归还设备页面
|
||||
const navigateToReturn = (deviceId, orderId) => {
|
||||
console.log(orderId);
|
||||
// 跳转到订单详情页面(统一入口)
|
||||
const navigateToOrderDetail = (order) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/return/index?deviceId=${deviceId}&orderId=${orderId}`
|
||||
url: `/pages/order/detail?orderId=${order.orderId || order.orderNo}&deviceId=${order.deviceId}`
|
||||
});
|
||||
};
|
||||
|
||||
// 组件事件:归还设备(实际跳转到订单详情页)
|
||||
const onReturnDevice = (order) => {
|
||||
navigateToOrderDetail(order);
|
||||
};
|
||||
|
||||
// 跳转到订单详情页
|
||||
const navigateToDetails = (order) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/return/index?orderId=${order.orderId || order.orderNo}&deviceId=${order.deviceId}`
|
||||
});
|
||||
navigateToOrderDetail(order);
|
||||
};
|
||||
|
||||
// 立即支付
|
||||
@@ -676,7 +609,7 @@
|
||||
height: 180rpx;
|
||||
margin: 0 auto 30rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 50%;
|
||||
// border-radius: 50%;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
<template>
|
||||
<view class="search-page">
|
||||
<!-- <view class="map-wrap">
|
||||
<MapComponent :userLocation="userLocation" :filteredPositions="filteredPositions"
|
||||
@mapCenterChange="onMapCenterChange" />
|
||||
</view> -->
|
||||
<view class="list-wrap">
|
||||
<view class="panel">
|
||||
<view class="filter-tabs">
|
||||
<view class="tab" :class="{ active: activeTab === 'rent' }" @click="setTab('rent')">可租借</view>
|
||||
<view class="tab" :class="{ active: activeTab === 'return' }" @click="setTab('return')">可归还</view>
|
||||
</view>
|
||||
<scroll-view class="list-scroll" scroll-y="true">
|
||||
<view class="card" :class="{ available: isRentable(item) }"
|
||||
v-for="(item, index) in filteredPositions" :key="item.positionId || index">
|
||||
<view class="thumb"></view>
|
||||
<view class="info">
|
||||
<view class="row top">
|
||||
<view class="name">{{ item.name }}</view>
|
||||
|
||||
</view>
|
||||
<view class="row sub" v-if="item.location">
|
||||
<text class="addr">{{ item.location }}</text>
|
||||
</view>
|
||||
<view class="row meta" v-if="item.workTime && item.workTime !== '0'">
|
||||
<text class="time">营业时间:{{ item.workTime }}</text>
|
||||
</view>
|
||||
<view class="tags">
|
||||
<view class="tag rent" v-if="isRentable(item)">可租借</view>
|
||||
<view class="tag return" v-if="isReturnable(item)">可归还</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="actions">
|
||||
|
||||
<view class="nav" @click.stop="navigateToPosition(item)">
|
||||
<image src="/static/location.png" class="action-icon" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="distance" v-if="item.distance">{{ item.distance }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="empty-state" v-if="!isLoading && (!positionList || positionList.length === 0)">
|
||||
<image class="empty-icon" src="/static/scan-icon.png" mode="aspectFit" />
|
||||
<text class="empty-text">附近暂无设备</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
onMounted
|
||||
} from 'vue'
|
||||
import MapComponent from '../../components/MapComponent.vue'
|
||||
import {
|
||||
URL
|
||||
} from '../../config/url.js'
|
||||
import {
|
||||
getUserLocation,
|
||||
getRegeo,
|
||||
calculateDistanceSync
|
||||
} from '../../utils/mapUtils.js'
|
||||
|
||||
const userLocation = ref(null)
|
||||
const positionList = ref([])
|
||||
const filteredPositions = ref([])
|
||||
const isLoading = ref(false)
|
||||
const activeTab = ref('rent')
|
||||
|
||||
const isRentable = (item) => {
|
||||
if (typeof item?.canRent !== 'undefined') return !!item.canRent
|
||||
return String(item?.status || '').toLowerCase() === 'online'
|
||||
}
|
||||
|
||||
const isReturnable = (item) => {
|
||||
if (typeof item?.canReturn !== 'undefined') return !!item.canReturn
|
||||
return String(item?.status || '').toLowerCase() === 'online'
|
||||
}
|
||||
|
||||
const formatDistance = (meters) => {
|
||||
if (meters < 1000) return `${Math.round(meters)}m`
|
||||
return `${(meters / 1000).toFixed(1)}km`
|
||||
}
|
||||
|
||||
const setTab = (name) => {
|
||||
activeTab.value = name
|
||||
applyFilter()
|
||||
}
|
||||
|
||||
const applyFilter = () => {
|
||||
if (activeTab.value === 'rent') {
|
||||
filteredPositions.value = positionList.value.filter(isRentable)
|
||||
} else if (activeTab.value === 'return') {
|
||||
filteredPositions.value = positionList.value.filter(isReturnable)
|
||||
} else {
|
||||
filteredPositions.value = [...positionList.value]
|
||||
}
|
||||
}
|
||||
|
||||
const calculateDistances = (center) => {
|
||||
positionList.value.forEach(item => {
|
||||
if (item.longitude && item.latitude) {
|
||||
try {
|
||||
const distanceInMeters = calculateDistanceSync(
|
||||
center.latitude,
|
||||
center.longitude,
|
||||
parseFloat(item.latitude),
|
||||
parseFloat(item.longitude)
|
||||
)
|
||||
item.distance = formatDistance(distanceInMeters)
|
||||
item.distanceInMeters = distanceInMeters
|
||||
} catch (_) {}
|
||||
}
|
||||
})
|
||||
positionList.value.sort((a, b) => (a.distanceInMeters || 999000) - (b.distanceInMeters || 999000))
|
||||
applyFilter()
|
||||
}
|
||||
|
||||
const loadPositions = async (center) => {
|
||||
try {
|
||||
isLoading.value = true
|
||||
const res = await uni.request({
|
||||
url: `${URL}/device/position/app/list`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
})
|
||||
if (res.statusCode === 200 && res.data?.code === 200) {
|
||||
positionList.value = res.data.rows || []
|
||||
calculateDistances(center)
|
||||
} else if (res.statusCode === 401 || res.data?.code === 401 || res.data?.code === 40101) {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
})
|
||||
} else {
|
||||
positionList.value = []
|
||||
filteredPositions.value = []
|
||||
}
|
||||
} catch (e) {
|
||||
positionList.value = []
|
||||
filteredPositions.value = []
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const init = async () => {
|
||||
isLoading.value = true
|
||||
try {
|
||||
const loc = await getUserLocation()
|
||||
userLocation.value = {
|
||||
longitude: loc.longitude,
|
||||
latitude: loc.latitude
|
||||
}
|
||||
await loadPositions(userLocation.value)
|
||||
} catch (e) {
|
||||
await loadPositions(userLocation.value || {
|
||||
longitude: 0,
|
||||
latitude: 0
|
||||
})
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const onMapCenterChange = (center) => {
|
||||
if (center && typeof center.longitude !== 'undefined' && typeof center.latitude !== 'undefined') {
|
||||
userLocation.value = {
|
||||
longitude: Number(center.longitude),
|
||||
latitude: Number(center.latitude)
|
||||
}
|
||||
loadPositions(userLocation.value)
|
||||
}
|
||||
}
|
||||
|
||||
const navigateToPosition = (position) => {
|
||||
const latitude = parseFloat(position.latitude)
|
||||
const longitude = parseFloat(position.longitude)
|
||||
uni.openLocation({
|
||||
latitude,
|
||||
longitude,
|
||||
name: position.name,
|
||||
address: position.location
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.map-wrap {
|
||||
flex: 0 0 48vh;
|
||||
padding: 20rpx 20rpx 0 20rpx;
|
||||
}
|
||||
|
||||
.list-wrap {
|
||||
flex: 1;
|
||||
background: transparent;
|
||||
margin-top: -20rpx;
|
||||
padding: 0 20rpx 20rpx;
|
||||
|
||||
.panel {
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||
padding: 16rpx 16rpx 8rpx;
|
||||
}
|
||||
|
||||
.filter-tabs {
|
||||
display: flex;
|
||||
background: #E8F8EF;
|
||||
padding: 8rpx;
|
||||
border-radius: 28rpx;
|
||||
gap: 8rpx;
|
||||
margin: 6rpx 6rpx 12rpx 6rpx;
|
||||
|
||||
.tab {
|
||||
flex: 1;
|
||||
height: 64rpx;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
color: #3EAB64;
|
||||
font-weight: 600;
|
||||
|
||||
&.active {
|
||||
background: #3EAB64;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-scroll {
|
||||
height: calc(52vh - 88rpx);
|
||||
padding: 6rpx 4rpx 12rpx 4rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
display: grid;
|
||||
grid-template-columns: 120rpx 1fr 72rpx;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
padding: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
border: 2rpx solid #E3EDE7;
|
||||
background: #ffffff;
|
||||
margin: 12rpx 8rpx;
|
||||
|
||||
&.available {
|
||||
border-color: #BFE2CF;
|
||||
background: #F6FBF8;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #F0F2F5;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
|
||||
.row.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #2A2A2A;
|
||||
max-width: 70%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.row.sub {
|
||||
color: #888;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.addr {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.row.meta {
|
||||
color: #999;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 6rpx 14rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tag.rent {
|
||||
background: #E8F5E8;
|
||||
color: #3EAB64;
|
||||
}
|
||||
|
||||
.tag.return {
|
||||
background: #E8F2FF;
|
||||
color: #3578e5;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.nav {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
border-radius: 25%;
|
||||
background: #E8F5EE;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #3EAB64;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin-bottom: 24rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
filter: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.distance {
|
||||
font-size: 24rpx;
|
||||
color: #2A8E5A;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<view class="setting-page">
|
||||
<view class="group">
|
||||
<view class="item" @click="navigateTo('/pages/legal/agreement')">
|
||||
<text class="label">用户协议</text>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
<view class="item" @click="navigateTo('/pages/legal/privacy')">
|
||||
<text class="label">隐私政策</text>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="group">
|
||||
<view class="item" @click="handleLogout">
|
||||
<text class="label">退出登录</text>
|
||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { userLogout } from '@/config/user.js'
|
||||
|
||||
const navigateTo = (url) => {
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
const handleLogout = async () => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要退出登录吗?',
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
const response = await userLogout();
|
||||
if (response.code == 200) {
|
||||
uni.showToast({ title: '退出成功', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.removeStorageSync('token')
|
||||
uni.removeStorageSync('userInfo')
|
||||
uni.reLaunch({ url: '/pages/login/index' })
|
||||
}, 1200)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.setting-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f6f6f6;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.group {
|
||||
margin-top: 20rpx;
|
||||
background-color: #ffffff;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 28rpx 30rpx;
|
||||
border-top: 1rpx solid #f5f5f5;
|
||||
}
|
||||
|
||||
.item:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 30rpx;
|
||||
color: #333333;
|
||||
}
|
||||
</style>
|
||||
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 112 KiB |
@@ -191,9 +191,9 @@ class OrderMonitor {
|
||||
confirmText: '查看详情',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 跳转到归还成功页面查看详情
|
||||
// 跳转到统一订单详情页面查看详情
|
||||
uni.redirectTo({
|
||||
url: `/pages/order/return-success?orderId=${orderId}`
|
||||
url: `/pages/order/detail?orderId=${orderId}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||