feat: 重构移动端 H5 页面视觉设计
基于移动优先原则优化布局、排版、颜色和间距,提升设计感和专业度。 - 重构全局设计系统:颜色、阴影、间距、圆角、排版 - 优化 UserInfoCard:更大的头像和状态徽章设计 - 优化 DeviceStatusCard:信息层级和指标网格 - 优化 TrafficCard:进度条和数字排版 - 优化 FunctionCard:4列布局和触摸反馈 - 优化 WifiCard:列表设计和信息层次 - 优化 FloatingButton:去除渐变,纯色设计 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,111 +1,22 @@
|
||||
<template>
|
||||
<view class="card function-card">
|
||||
<view class="card-header">
|
||||
<view class="title">功能菜单</view>
|
||||
<!-- <view class="header-actions">
|
||||
<button class="btn-apple btn-primary" @tap="$emit('sync')">运营商数据同步</button>
|
||||
</view> -->
|
||||
<view class="card-header mb-5">
|
||||
<text class="text-heading">功能服务</text>
|
||||
</view>
|
||||
<view class="function-grid">
|
||||
<view class="function-item interactive card-interactive" @tap="$emit('enter', 'authentication')"
|
||||
role="button" tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/authentication.png" mode="aspectFit" alt="实名认证"></image>
|
||||
<view
|
||||
v-for="item in displayFunctions"
|
||||
:key="item.key"
|
||||
class="function-item interactive"
|
||||
:class="{ 'is-disabled': item.disabled }"
|
||||
@click="handleItemClick(item)"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<view class="function-icon-wrap">
|
||||
<image class="function-icon" :src="item.icon" mode="aspectFit" :alt="item.name"></image>
|
||||
</view>
|
||||
<view :class="['function-name', realNameStatus === '已实名' ? 'text-primary' : '']">
|
||||
{{ realNameStatus || '实名认证' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" @tap="$emit('sync')" role="button" tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/data.png" mode="aspectFit" alt="运营数据同步"></image>
|
||||
</view>
|
||||
<view class="function-name">运营数据同步</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" @tap="$emit('enter', 'package-order')"
|
||||
role="button" tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/shop.png" mode="aspectFit" alt="套餐订购"></image>
|
||||
</view>
|
||||
<view class="function-name">套餐订购</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" @tap="$emit('enter', 'order-list')" role="button"
|
||||
tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/order.png" mode="aspectFit" alt="我的订单"></image>
|
||||
</view>
|
||||
<view class="function-name">我的订单</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" v-if="isDevice" @tap="$emit('enter', 'back')"
|
||||
role="button" tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/back.png" mode="aspectFit" alt="后台管理"></image>
|
||||
</view>
|
||||
<view class="function-name">后台管理</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" v-if="isDevice" @tap="$emit('enter', 'switch')"
|
||||
role="button" tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/change.png" mode="aspectFit" alt="切换运营商"></image>
|
||||
</view>
|
||||
<view class="function-name">切换运营商</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" @tap="$emit('enter', 'asset-package')"
|
||||
role="button" tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/shop.png" mode="aspectFit" alt="资产套餐历史"></image>
|
||||
</view>
|
||||
<view class="function-name">资产套餐历史</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" @tap="handleBindPhone" role="button" tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/bind-phone.png" mode="aspectFit" alt="绑定手机号"></image>
|
||||
</view>
|
||||
<view :class="['function-name', alreadyBindPhone ? 'text-primary' : '']">
|
||||
{{ alreadyBindPhone ? '已绑定' : '绑定手机号' }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" @tap="$emit('enter', 'change-phone')" role="button"
|
||||
tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/change-phone.png" mode="aspectFit" alt="更换手机号"></image>
|
||||
</view>
|
||||
<view class="function-name">更换手机号</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" v-if="isDevice"
|
||||
@tap="$emit('enter', 'device-exchange')" role="button" tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/change.png" mode="aspectFit" alt="设备换货"></image>
|
||||
</view>
|
||||
<view class="function-name">设备换货</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" @tap="$emit('enter', 'wallet')" role="button"
|
||||
tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/wallet.png" mode="aspectFit" alt="我的钱包"></image>
|
||||
</view>
|
||||
<view class="function-name">我的钱包</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" v-if="isDevice" @tap="$emit('enter', 'recover')"
|
||||
role="button" tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/recover.png" mode="aspectFit" alt="重启设备"></image>
|
||||
</view>
|
||||
<view class="function-name">重启设备</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" v-if="isDevice" @tap="$emit('enter', 'restart')"
|
||||
role="button" tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/restart.png" mode="aspectFit" alt="恢复出厂"></image>
|
||||
</view>
|
||||
<view class="function-name">恢复出厂</view>
|
||||
</view>
|
||||
<view class="function-item interactive card-interactive" @tap="$emit('enter', 'out')" role="button"
|
||||
tabindex="0">
|
||||
<view class="function-icon">
|
||||
<image src="/static/out.png" mode="aspectFit" alt="退出登录"></image>
|
||||
</view>
|
||||
<view class="function-name">退出登录</view>
|
||||
<text class="function-name" :class="{ 'text-primary-color': item.highlight }">{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -114,6 +25,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
realNameStatus: {
|
||||
type: String,
|
||||
@@ -131,62 +44,95 @@
|
||||
|
||||
const emit = defineEmits(['enter', 'sync']);
|
||||
|
||||
const handleBindPhone = () => {
|
||||
if (props.alreadyBindPhone) {
|
||||
// 已绑定,不可点击
|
||||
return;
|
||||
const functions = computed(() => {
|
||||
const list = [
|
||||
{ key: 'authentication', name: props.realNameStatus || '实名认证', icon: '/static/authentication.png', action: 'authentication', highlight: props.realNameStatus === '已实名' },
|
||||
{ key: 'sync', name: '运营数据同步', icon: '/static/data.png', action: 'sync' },
|
||||
{ key: 'package-order', name: '套餐订购', icon: '/static/shop.png', action: 'package-order' },
|
||||
{ key: 'order-list', name: '我的订单', icon: '/static/order.png', action: 'order-list' },
|
||||
{ key: 'back', name: '后台管理', icon: '/static/back.png', action: 'back', deviceOnly: true },
|
||||
{ key: 'switch', name: '切换运营商', icon: '/static/change.png', action: 'switch', deviceOnly: true },
|
||||
{ key: 'asset-package', name: '资产套餐历史', icon: '/static/shop.png', action: 'asset-package' },
|
||||
{ key: 'bind', name: props.alreadyBindPhone ? '已绑定' : '绑定手机号', icon: '/static/bind-phone.png', action: 'bind', highlight: props.alreadyBindPhone, disabled: props.alreadyBindPhone },
|
||||
{ key: 'change-phone', name: '更换手机号', icon: '/static/change-phone.png', action: 'change-phone' },
|
||||
{ key: 'device-exchange', name: '设备换货', icon: '/static/change.png', action: 'device-exchange', deviceOnly: true },
|
||||
{ key: 'wallet', name: '我的钱包', icon: '/static/wallet.png', action: 'wallet' },
|
||||
{ key: 'recover', name: '重启设备', icon: '/static/recover.png', action: 'recover', deviceOnly: true },
|
||||
{ key: 'restart', name: '恢复出厂', icon: '/static/restart.png', action: 'restart', deviceOnly: true },
|
||||
{ key: 'out', name: '退出登录', icon: '/static/out.png', action: 'out' }
|
||||
];
|
||||
|
||||
return list.filter(item => !item.deviceOnly || props.isDevice);
|
||||
});
|
||||
|
||||
const displayFunctions = computed(() => functions.value);
|
||||
|
||||
const handleItemClick = (item) => {
|
||||
if (item.disabled) return;
|
||||
if (item.action === 'bind' && props.alreadyBindPhone) return;
|
||||
if (item.action === 'sync') {
|
||||
emit('sync');
|
||||
} else {
|
||||
emit('enter', item.action);
|
||||
}
|
||||
// 未绑定,可以点击进入绑定页面
|
||||
emit('enter', 'bind');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.function-card {
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.function-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16rpx;
|
||||
width: 100%;
|
||||
|
||||
.function-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20rpx;
|
||||
|
||||
.function-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.function-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: var(--text-primary);
|
||||
text-align: center;
|
||||
margin-top: 15rpx;
|
||||
}
|
||||
}
|
||||
padding-bottom: var(--space-4);
|
||||
border-bottom: 1rpx solid var(--gray-100);
|
||||
}
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: var(--primary) !important;
|
||||
.function-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: var(--space-4);
|
||||
padding-top: var(--space-5);
|
||||
}
|
||||
|
||||
.function-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
padding: var(--space-3);
|
||||
border-radius: var(--radius-md);
|
||||
transition: background var(--duration-fast) var(--ease-out);
|
||||
|
||||
&:active {
|
||||
background: var(--gray-50);
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.function-icon-wrap {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--gray-50);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.function-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.function-name {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
text-align: center;
|
||||
line-height: 1.3;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user