Files
medical-mall/pages/mall/delivery/profile.uvue
not-like-juvenile c803a77c8f 修改页面逻辑
2026-02-03 12:01:10 +08:00

827 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 配送端 - 个人中心 -->
<template>
<view class="delivery-profile">
<!-- 1. 蓝色头像条profile-header -->
<view class="profile-header">
<!-- 返回按钮:最左边垂直居中 -->
<view class="nav-left" @click="goBack">
<text class="nav-icon"></text>
</view>
<image :src="driverInfo.avatar_url || '/static/default-avatar.png'" class="driver-avatar" @click="editProfile" />
<view class="driver-info">
<text class="driver-name">{{ driverInfo.real_name }}</text>
<text class="driver-status">{{ getWorkStatus() }}</text>
<view class="driver-stats">
<text class="stat-item">评分: {{ driverInfo.rating }}/5.0</text>
<text class="stat-item">总单数: {{ driverInfo.total_orders }}</text>
</view>
</view>
<view class="settings-icon" @click="goToSettings">⚙️</view>
</view>
<!-- 2. 工作状态切换 -->
<view class="work-status">
<view class="section-title">工作状态</view>
<view class="status-controls">
<view class="status-toggle" @click="toggleWorkStatus">
<text class="toggle-label">{{ workStatus === 1 ? '工作中' : '休息中' }}</text>
<view class="toggle-switch" :class="{ active: workStatus === 1 }">
<view class="toggle-handle"></view>
</view>
</view>
<!-- 修改点:将定位信息和重新定位按钮合并到一个区域 -->
<view v-if="workStatus === 1" class="current-location-section">
<text class="location-text">📍 {{ currentLocation }}</text>
<view class="relocate-icon" @click.stop="showRelocateConfirm">
<text class="icon-text">↻</text>
</view>
</view>
</view>
</view>
<!-- 3. 配送任务快捷入口 -->
<view class="task-shortcuts">
<view class="section-title">配送任务</view>
<view class="task-tabs">
<view class="task-tab" @click="goToTasks('all')">
<text class="tab-icon">📋</text>
<text class="tab-text">全部任务</text>
<text v-if="taskCounts.total > 0" class="tab-badge">{{ taskCounts.total }}</text>
</view>
<view class="task-tab" @click="goToTasks('pending')">
<text class="tab-icon">⏳</text>
<text class="tab-text">待接单</text>
<text v-if="taskCounts.pending > 0" class="tab-badge alert">{{ taskCounts.pending }}</text>
</view>
<view class="task-tab" @click="goToTasks('ongoing')">
<text class="tab-icon">🚚</text>
<text class="tab-text">配送中</text>
<text v-if="taskCounts.ongoing > 0" class="tab-badge">{{ taskCounts.ongoing }}</text>
</view>
<view class="task-tab" @click="goToTasks('completed')">
<text class="tab-icon">✅</text>
<text class="tab-text">已完成</text>
<text v-if="taskCounts.completed > 0" class="tab-badge">{{ taskCounts.completed }}</text>
</view>
</view>
</view>
<!-- 4. 今日配送数据 -->
<view class="today-stats">
<view class="section-title">今日配送</view>
<view class="stats-grid">
<view class="stat-card">
<text class="stat-value">{{ todayStats.deliveries }}</text>
<text class="stat-label">完成单数</text>
</view>
<view class="stat-card">
<text class="stat-value">¥{{ todayStats.earnings }}</text>
<text class="stat-label">配送收入</text>
</view>
<view class="stat-card">
<text class="stat-value">{{ todayStats.distance }}km</text>
<text class="stat-label">配送里程</text>
</view>
<view class="stat-card">
<text class="stat-value">{{ todayStats.efficiency }}%</text>
<text class="stat-label">准时率</text>
</view>
</view>
</view>
<!-- 5. 当前任务 -->
<view v-if="currentTask" class="current-task">
<view class="section-title">当前任务</view>
<view class="task-card">
<view class="task-header">
<text class="task-id">任务 #{{ currentTask.id.slice(-6) }}</text>
<text class="task-status">{{ getTaskStatusText(currentTask.status) }}</text>
</view>
<view class="task-route">
<view class="route-point">
<text class="point-icon">📍</text>
<view class="point-info">
<text class="point-label">取货地址</text>
<text class="point-address">{{ getAddressText(currentTask.pickup_address) }}</text>
</view>
</view>
<view class="route-line"></view>
<view class="route-point">
<text class="point-icon">🏠</text>
<view class="point-info">
<text class="point-label">送达地址</text>
<text class="point-address">{{ getAddressText(currentTask.delivery_address) }}</text>
</view>
</view>
</view>
<view class="task-actions">
<button class="action-btn" @click="contactCustomer">联系客户</button>
<button class="action-btn primary" @click="viewTaskDetail">查看详情</button>
</view>
</view>
</view>
<!-- 6. 最近任务 -->
<view class="recent-tasks">
<view class="section-header">
<text class="section-title">最近任务</text>
<text class="view-all" @click="goToTasks('all')">查看全部 ></text>
</view>
<view v-if="recentTasks.length > 0" class="task-list">
<view v-for="task in recentTasks" :key="task.id" class="task-item" @click="viewTaskDetail(task.id)">
<view class="task-info">
<text class="task-order">订单: {{ task.order_id.slice(-6) }}</text>
<text class="task-fee">配送费: ¥{{ task.delivery_fee }}</text>
</view>
<view class="task-time">
<text class="time-text">{{ formatTime(task.created_at) }}</text>
<text class="status-text" :class="'status-' + task.status">{{ getTaskStatusText(task.status) }}</text>
</view>
</view>
</view>
<view v-else class="no-data">
<text class="no-data-text">暂无最近任务</text>
</view>
</view>
<!-- 7. 收入统计 -->
<view class="earnings-chart">
<view class="section-header">
<text class="section-title">收入统计</text>
<text class="view-more" @click="goToEarnings">详细报表 ></text>
</view>
<view class="chart-container">
<view class="chart-bar">
<view v-for="(day, index) in weeklyEarnings" :key="index"
class="bar-item"
:style="{ height: (day.amount / maxEarnings * 100) + '%' }">
<text class="bar-label">{{ day.day }}</text>
</view>
</view>
</view>
</view>
<!-- 8. 功能菜单 -->
<view class="function-menu">
<view class="menu-group">
<view class="menu-item" @click="goToEarnings">
<text class="menu-icon">💰</text>
<text class="menu-label">收入明细</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="goToVehicle">
<text class="menu-icon">🚗</text>
<text class="menu-label">车辆管理</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="goToRatings">
<text class="menu-icon">⭐</text>
<text class="menu-label">评价记录</text>
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-group">
<view class="menu-item" @click="goToHelp">
<text class="menu-icon">❓</text>
<text class="menu-label">帮助中心</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="goToFeedback">
<text class="menu-icon">💬</text>
<text class="menu-label">意见反馈</text>
<text class="menu-arrow">></text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, onMounted, computed } from 'vue'
import supa, { supaReady } from '@/components/supadb/aksupainstance.uts'
import { getCurrentUserId } from '@/utils/store.uts'
import type { DeliveryDriverType, DeliveryTaskType } from '@/types/mall-types'
/* ----------------- 返回按钮 ----------------- */
function backToIndex() {
uni.navigateBack({ url: '/pages/mall/delivery/index' })
}
/* 兼容模板中的 goBack 绑定,保持与其它页面一致 */
function goBack() {
uni.navigateBack()
}
/* ----------------- 数据 & 状态 ----------------- */
const driverInfo = ref({ id: '', real_name: '配送员', avatar_url: '', rating: 4.9, total_orders: 0, work_status: 1 })
const workStatus = ref(1)
const currentLocation = ref('定位中...')
const taskCounts = ref({ total: 0, pending: 0, ongoing: 0, completed: 0 })
const todayStats = ref({ deliveries: 0, earnings: '0.00', distance: 0, efficiency: 0 })
const currentTask = ref<DeliveryTaskType | null>(null)
const recentTasks = ref<DeliveryTaskType[]>([])
const weeklyEarnings = ref([{ day: '周一', amount: 0 }, { day: '周二', amount: 0 }, { day: '周三', amount: 0 }, { day: '周四', amount: 0 }, { day: '周五', amount: 0 }, { day: '周六', amount: 0 }, { day: '周日', amount: 0 }])
const maxEarnings = computed(() => Math.max(...weeklyEarnings.value.map(i => i.amount)))
const isLoading = ref(false)
/* ----------------- 生命周期 ----------------- */
onMounted(async () => {
await loadDriverInfo()
await Promise.all([loadTaskCounts(), loadTodayStats(), loadCurrentTask(), loadRecentTasks()])
})
/* ----------------- 后端数据加载(使用 supa ----------------- */
async function loadDriverInfo() {
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in loadDriverInfo - proceeding')
const userId = getCurrentUserId()
if (!userId) return
let res = await supa.from('ml_delivery_drivers').select('*').eq('user_id', userId).limit(1).execute()
if (!(res && Array.isArray(res.data) && res.data.length > 0)) {
const akRes = await supa.from('ak_users').select('id').eq('auth_id', userId).limit(1).execute()
let akId = ''
if (akRes && Array.isArray(akRes.data) && akRes.data.length > 0) akId = (akRes.data[0] as any).id
if (akId) res = await supa.from('ml_delivery_drivers').select('*').eq('user_id', akId).limit(1).execute()
}
if (res && Array.isArray(res.data) && res.data.length > 0) {
driverInfo.value = Object.assign(driverInfo.value, res.data[0])
workStatus.value = driverInfo.value.work_status ?? workStatus.value
}
} catch (e) {
console.error('loadDriverInfo error', e)
}
}
async function loadTaskCounts() {
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in loadTaskCounts - proceeding')
const driverId = driverInfo.value.id || getCurrentUserId()
if (!driverId) return
const res = await supa.from('ml_delivery_tasks').select('id,status').eq('driver_id', driverId).execute()
if (res && Array.isArray(res.data)) {
const rows = res.data as Array<any>
const total = rows.length
const pending = rows.filter(r => Number(r.status) === 1).length
const ongoing = rows.filter(r => Number(r.status) > 1 && Number(r.status) < 5).length
const completed = rows.filter(r => Number(r.status) >= 5).length
taskCounts.value = { total, pending, ongoing, completed }
}
} catch (e) {
console.error('loadTaskCounts error', e)
}
}
async function loadTodayStats() {
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in loadTodayStats - proceeding')
const driverId = driverInfo.value.id || getCurrentUserId()
if (!driverId) return
const start = new Date(); start.setHours(0,0,0,0)
const end = new Date(); end.setHours(23,59,59,999)
const res = await supa.from('ml_delivery_tasks').select('delivery_fee,distance,status,created_at').eq('driver_id', driverId).gte('created_at', start.toISOString()).lte('created_at', end.toISOString()).execute()
if (res && Array.isArray(res.data)) {
const rows = res.data as Array<any>
const deliveries = rows.filter(r => Number(r.status) >= 5).length
const earnings = rows.reduce((s, r) => s + (Number(r.delivery_fee) || 0), 0)
const distance = rows.reduce((s, r) => s + (Number(r.distance) || 0), 0)
todayStats.value = { deliveries, earnings: earnings.toFixed(2), distance: Number(distance.toFixed(2)), efficiency: deliveries === 0 ? 0 : Math.round((deliveries / Math.max(1, rows.length)) * 10000)/100 }
}
} catch (e) {
console.error('loadTodayStats error', e)
}
}
async function loadCurrentTask() {
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in loadCurrentTask - proceeding')
const driverId = driverInfo.value.id || getCurrentUserId()
if (!driverId) { currentTask.value = null; return }
const res = await supa.from('ml_delivery_tasks').select('*').eq('driver_id', driverId).lt('status', 5).order('created_at', { ascending: false }).limit(1).execute()
if (res && Array.isArray(res.data) && res.data.length > 0) {
currentTask.value = res.data[0]
} else {
currentTask.value = null
}
} catch (e) {
console.error('loadCurrentTask error', e)
}
}
async function loadRecentTasks() {
try {
const ready = await Promise.race([supaReady, new Promise(resolve => setTimeout(() => resolve(false), 1500))])
if (!ready) console.warn('supaReady timeout/failed in loadRecentTasks - proceeding')
const driverId = driverInfo.value.id || getCurrentUserId()
if (!driverId) { recentTasks.value = []; return }
const res = await supa.from('ml_delivery_tasks').select('*').eq('driver_id', driverId).order('created_at', { ascending: false }).range(0, 9).execute()
if (res && Array.isArray(res.data)) {
recentTasks.value = res.data as Array<any>
} else {
recentTasks.value = []
}
} catch (e) {
console.error('loadRecentTasks error', e)
recentTasks.value = []
}
}
/* ----------------- 小工具 & 交互 ----------------- */
function getWorkStatus(): string {
const m: Record<number, string> = { 0: '休息中', 1: '工作中', 2: '忙碌中' }
return m[driverInfo.value.work_status] || '未知状态'
}
function getTaskStatusText(status: number): string {
const m: Record<number, string> = { 1: '待接单', 2: '已接单', 3: '配送中', 4: '已完成', 5: '已取消' }
return m[status] || '未知'
}
function getAddressText(address: UTSJSONObject): string {
return (address && (address['address'] as string)) || (address && (address['detail'] as string)) || '地址信息'
}
function formatTime(dateStr: string): string {
if (!dateStr) return ''
const diff = Date.now() - new Date(dateStr).getTime()
const hours = Math.floor(diff / 36e5)
if (hours < 1) return '刚刚'
if (hours < 24) return `${hours}小时前`
return `${Math.floor(hours / 24)}天前`
}
function toggleWorkStatus() {
workStatus.value = workStatus.value === 1 ? 0 : 1
driverInfo.value.work_status = workStatus.value
uni.showToast({ title: workStatus.value === 1 ? '已开始工作' : '已停止工作', icon: 'success' })
}
function showRelocateConfirm() {
uni.showModal({ title: '重新定位', content: '确定要更新当前位置吗?', confirmText: '立即定位', success: (res) => { if (res.confirm) relocate() } })
}
function relocate() {
uni.showLoading({ title: '获取位置中...', mask: true })
setTimeout(() => {
uni.hideLoading()
currentLocation.value = '朝阳区建国门外大街附近'
uni.showToast({ title: '定位成功', icon: 'success', duration: 1500 })
}, 1200)
}
function contactCustomer() {
uni.showActionSheet({ itemList: ['拨打电话', '发送短信'], success: res => { if (res.tapIndex === 0) uni.makePhoneCall({ phoneNumber: '13888888888' }) } })
}
function viewTaskDetail(taskId = '') {
const id = taskId || currentTask.value?.id || ''
uni.navigateTo({ url: `/pages/mall/delivery/task-detail?id=${id}` })
}
function editProfile() { uni.navigateTo({ url: '/pages/mall/delivery/profile-edit' }) }
function goToSettings() { uni.navigateTo({ url: '/pages/mall/delivery/settings' }) }
function goToTasks(type: string) { uni.navigateTo({ url: `/pages/mall/delivery/tasks?type=${type}` }) }
function goToEarnings() { uni.navigateTo({ url: '/pages/mall/delivery/earnings' }) }
function goToVehicle() { uni.navigateTo({ url: '/pages/mall/delivery/vehicle' }) }
function goToRatings() { uni.navigateTo({ url: '/pages/mall/delivery/ratings' }) }
function goToHelp() { uni.navigateTo({ url: '/pages/mall/delivery/help-center' }) }
function goToFeedback() { uni.navigateTo({ url: '/pages/mall/delivery/feedback' }) }
</script>
<style scoped>
/* ---------- 返回按钮:蓝色条最左边垂直居中 ---------- */
.profile-header {
position: relative;
}
.nav-left {
position: absolute;
left: 30rpx;
top: 50%;
transform: translateY(-50%);
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background: rgba(0, 0, 0, .15);
display: flex;
align-items: center;
justify-content: center;
}
.nav-left:active {
background: rgba(0, 0, 0, .3);
}
.nav-icon {
font-size: 40rpx;
color: #fff;
}
/* ---------- 以下与原样式一致,仅修改工作状态区域 ---------- */
.delivery-profile {
padding: 0 0 120rpx 0;
background-color: #f5f5f5;
min-height: 100vh;
}
.profile-header {
display: flex;
align-items: center;
padding: 40rpx 30rpx;
background: linear-gradient(135deg, #74b9ff 0%, #0984e3 100%);
}
.driver-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
margin-left: 80rpx; /* 给返回按钮留位置 */
margin-right: 30rpx;
border: 4rpx solid rgba(255, 255, 255, 0.3);
}
.driver-info {
flex: 1;
}
.driver-name {
font-size: 36rpx;
font-weight: bold;
color: white;
margin-bottom: 10rpx;
}
.driver-status {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 15rpx;
}
.driver-stats {
display: flex;
gap: 30rpx;
}
.stat-item {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.9);
}
.settings-icon {
font-size: 36rpx;
color: white;
padding: 10rpx;
}
.work-status, .task-shortcuts, .today-stats, .current-task, .recent-tasks, .earnings-chart, .function-menu {
margin: 20rpx 30rpx;
background: white;
border-radius: 20rpx;
padding: 30rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.view-all, .view-more {
font-size: 24rpx;
color: #74b9ff;
}
.status-controls {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.status-toggle {
display: flex;
justify-content: space-between;
align-items: center;
}
.toggle-label {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.toggle-switch {
position: relative;
width: 100rpx;
height: 50rpx;
background: #ddd;
border-radius: 25rpx;
transition: all 0.3s;
}
.toggle-switch.active {
background: #74b9ff;
}
.toggle-handle {
position: absolute;
top: 5rpx;
left: 5rpx;
width: 40rpx;
height: 40rpx;
background: white;
border-radius: 50%;
transition: all 0.3s;
}
.toggle-switch.active .toggle-handle {
left: 55rpx;
}
/* --- 修改后的定位信息区域:地址 + 图标 --- */
.current-location-section {
display: flex;
align-items: center;
padding: 15rpx 20rpx;
background: #e8f4fd; /* 与原样式保持一致 */
border-radius: 15rpx;
}
.location-text {
font-size: 24rpx; /* 与截图字号一致 */
color: #74b9ff; /* 与截图颜色一致 */
flex: 1; /* 让文字占据剩余空间 */
margin-right: 16rpx; /* 与图标留空隙 */
}
.relocate-icon {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: rgba(116, 185, 255, 0.15); /* 浅蓝底 */
color: #74b9ff; /* 主色文字 */
font-size: 24rpx;
cursor: pointer;
transition: all 0.2s ease;
}
.relocate-icon:hover {
background: rgba(116, 185, 255, 0.3); /* 悬停效果 */
}
.relocate-icon:active {
background: rgba(116, 185, 255, 0.4); /* 点击效果 */
}
.task-tabs {
display: flex;
justify-content: space-between;
}
.task-tab {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.tab-icon {
font-size: 48rpx;
margin-bottom: 10rpx;
}
.tab-text {
font-size: 24rpx;
color: #666;
}
.tab-badge {
position: absolute;
top: -10rpx;
right: 20rpx;
background: #ff6b6b;
color: white;
font-size: 20rpx;
padding: 4rpx 8rpx;
border-radius: 10rpx;
min-width: 30rpx;
text-align: center;
}
.tab-badge.alert {
background: #ff4757;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.stats-grid {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 20rpx;
}
.stat-card {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
min-width: 150rpx;
padding: 20rpx;
background: #e8f4fd;
border-radius: 15rpx;
}
.stat-value {
font-size: 36rpx;
font-weight: bold;
color: #74b9ff;
margin-bottom: 5rpx;
}
.stat-label {
font-size: 24rpx;
color: #666;
}
.task-card {
padding: 25rpx;
background: #e8f4fd;
border-radius: 15rpx;
border-left: 6rpx solid #74b9ff;
}
.task-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.task-id {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.task-status {
font-size: 22rpx;
padding: 6rpx 12rpx;
border-radius: 20rpx;
background: #74b9ff;
color: white;
}
.task-route {
margin-bottom: 25rpx;
}
.route-point {
display: flex;
align-items: flex-start;
margin-bottom: 15rpx;
}
.point-icon {
font-size: 32rpx;
margin-right: 15rpx;
margin-top: 5rpx;
}
.point-info {
flex: 1;
}
.point-label {
font-size: 22rpx;
color: #666;
margin-bottom: 5rpx;
}
.point-address {
font-size: 26rpx;
color: #333;
line-height: 1.4;
}
.route-line {
width: 2rpx;
height: 30rpx;
background: #ddd;
margin-left: 16rpx;
margin-bottom: 5rpx;
}
.task-actions {
display: flex;
gap: 20rpx;
}
.action-btn {
flex: 1;
padding: 20rpx;
border-radius: 15rpx;
font-size: 26rpx;
background: #f0f0f0;
color: #333;
border: none;
}
.action-btn.primary {
background: #74b9ff;
color: white;
}
.task-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.task-item {
padding: 25rpx;
background: #f8f9ff;
border-radius: 15rpx;
border-left: 6rpx solid #74b9ff;
}
.task-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
}
.task-order {
font-size: 26rpx;
color: #333;
font-weight: 500;
}
.task-fee {
font-size: 24rpx;
color: #74b9ff;
font-weight: bold;
}
.task-time {
display: flex;
justify-content: space-between;
align-items: center;
}
.time-text {
font-size: 22rpx;
color: #999;
}
.status-text {
font-size: 22rpx;
padding: 4rpx 8rpx;
border-radius: 15rpx;
background: #e3f2fd;
color: #1976d2;
}
.status-4 {
background: #e8f5e8;
color: #388e3c;
}
.chart-container {
padding: 20rpx 0;
}
.chart-bar {
display: flex;
justify-content: space-between;
align-items: flex-end;
height: 200rpx;
gap: 10rpx;
}
.bar-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
position: relative;
}
.bar-item::before {
content: '';
width: 100%;
background: linear-gradient(180deg, #74b9ff 0%, #0984e3 100%);
border-radius: 8rpx 8rpx 0 0;
min-height: 20rpx;
}
.bar-label {
font-size: 20rpx;
color: #666;
margin-top: 10rpx;
}
.menu-group {
margin-bottom: 30rpx;
}
.menu-group:last-child {
margin-bottom: 0;
}
.menu-item {
display: flex;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-icon {
font-size: 36rpx;
width: 60rpx;
margin-right: 25rpx;
}
.menu-label {
flex: 1;
font-size: 28rpx;
color: #333;
}
.menu-arrow {
font-size: 24rpx;
color: #ccc;
}
.no-data {
text-align: center;
padding: 60rpx 0;
}
.no-data-text {
font-size: 24rpx;
color: #999;
}
</style>