基于移动优先原则优化布局、排版、颜色和间距,提升设计感和专业度。 - 重构全局设计系统:颜色、阴影、间距、圆角、排版 - 优化 UserInfoCard:更大的头像和状态徽章设计 - 优化 DeviceStatusCard:信息层级和指标网格 - 优化 TrafficCard:进度条和数字排版 - 优化 FunctionCard:4列布局和触摸反馈 - 优化 WifiCard:列表设计和信息层次 - 优化 FloatingButton:去除渐变,纯色设计 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
138 lines
4.0 KiB
Vue
138 lines
4.0 KiB
Vue
<template>
|
|
<view class="card function-card">
|
|
<view class="card-header mb-5">
|
|
<text class="text-heading">功能服务</text>
|
|
</view>
|
|
<view class="function-grid">
|
|
<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>
|
|
<text class="function-name" :class="{ 'text-primary-color': item.highlight }">{{ item.name }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<slot></slot>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue';
|
|
|
|
const props = defineProps({
|
|
realNameStatus: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
alreadyBindPhone: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
isDevice: {
|
|
type: Boolean,
|
|
default: true
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(['enter', 'sync']);
|
|
|
|
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);
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.function-card {
|
|
.card-header {
|
|
padding-bottom: var(--space-4);
|
|
border-bottom: 1rpx solid var(--gray-100);
|
|
}
|
|
}
|
|
|
|
.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> |