hookehuyr

调整我的页样式与昵称交互

<template>
<view class="mine-page">
<!-- 顶部装饰背景 -->
<view class="header-bg">
<view class="header-pattern" />
</view>
<view class="page-content">
<view class="hero-card">
<text class="hero-title">我的</text>
<text class="hero-desc">
这里预留给个人资料、授权信息和常用入口。当前阶段先提供基础占位和授权状态展示。
<view class="hero-banner">
<view class="hero-banner__ornament">
<view class="hero-banner__orb hero-banner__orb--lg" />
<view class="hero-banner__orb hero-banner__orb--sm" />
</view>
<view class="profile-panel" @tap="handleProfileTap">
<view class="profile-panel__avatar">
<view class="avatar-ring">
<button
class="avatar-btn"
open-type="chooseAvatar"
@chooseavatar="onChooseAvatar"
>
<image
class="avatar-img"
:src="avatarUrl || defaultAvatar"
mode="aspectFill"
/>
</button>
</view>
</view>
<view class="profile-panel__body">
<view class="profile-panel__head" @tap.stop="openNicknameDialog">
<view class="profile-panel__name-block">
<text class="nickname-text">{{ nickname || defaultNickname }}</text>
<text class="profile-panel__subtext">点击修改昵称</text>
</view>
<text class="profile-panel__action">›</text>
</view>
<text class="profile-panel__tip">
当前还没接微信昵称接口,先支持手动输入并直接显示在这里。
</text>
</view>
</view>
</view>
<view class="status-card">
<text class="section-title">授权状态</text>
<view class="status-row">
<text class="section-desc">当前小程序登录态</text>
<text class="status-tag" :class="{ authed: isAuthed }">
{{ isAuthed ? '已授权' : '未授权' }}
</text>
<!-- 功能菜单 -->
<view class="menu-section">
<view class="section-title">
<text class="section-title-text">常用服务</text>
</view>
<view class="menu-grid">
<view
v-for="item in menuItems"
:key="item.key"
class="menu-item"
@tap="handleMenuTap(item)"
>
<view class="menu-item__main">
<view class="menu-icon-wrap" :class="`menu-icon--${item.key}`">
<component
:is="item.icon"
size="20"
color="#a67939"
/>
</view>
<view class="menu-item__content">
<text class="menu-item-title">{{ item.title }}</text>
<text class="menu-item-desc">{{ item.desc }}</text>
</view>
</view>
<text class="menu-item-arrow">›</text>
</view>
</view>
</view>
<!-- 更多菜单 -->
<view class="menu-section">
<view class="section-title">
<text class="section-title-text">更多</text>
</view>
<view class="menu-list">
<view
v-for="(item, idx) in bottomMenuItems"
:key="item.key"
class="menu-list-item"
:class="{ 'menu-list-item--last': idx === bottomMenuItems.length - 1 }"
@tap="handleMenuTap(item)"
>
<view class="menu-list-icon" :class="`menu-icon--${item.key}`">
<component
:is="item.icon"
size="18"
color="#a67939"
/>
</view>
<view class="menu-list-content">
<text class="menu-list-title">{{ item.title }}</text>
<text class="menu-list-desc">{{ item.desc }}</text>
</view>
<text class="arrow-text arrow-text--light">›</text>
</view>
</view>
</view>
<view class="page-footer-tip">
<text class="page-footer-tip__text">山门清净,信息常新</text>
</view>
</view>
<view
v-if="nicknameDialogVisible"
class="nickname-dialog-mask"
@tap="closeNicknameDialog"
>
<view class="nickname-dialog" @tap.stop>
<text class="nickname-dialog__title">设置昵称</text>
<text class="nickname-dialog__desc">当前版本先手动输入昵称,留空时显示默认昵称。</text>
<input
class="nickname-dialog__input"
:value="nicknameDraft"
maxlength="20"
placeholder="请输入昵称"
focus
@input="onNicknameDraftInput"
/>
<view class="nickname-dialog__actions">
<view
class="nickname-dialog__btn nickname-dialog__btn--ghost"
@tap="closeNicknameDialog"
>
取消
</view>
<view
class="nickname-dialog__btn"
@tap="confirmNickname"
>
确定
</view>
</view>
</view>
</view>
......@@ -25,85 +145,527 @@
<script setup>
import { ref } from 'vue'
import { useDidShow } from '@tarojs/taro'
import Taro from '@tarojs/taro'
import AppTabbar from '@/components/AppTabbar.vue'
import { hasAuth } from '@/utils/authRedirect'
import {
Footprint,
Location,
Notice,
Order,
Service,
Setting,
Star,
} from '@nutui/icons-vue-taro'
const isAuthed = ref(false)
const avatarUrl = ref('')
const nickname = ref('')
const nicknameDraft = ref('')
const nicknameDialogVisible = ref(false)
const defaultNickname = '觉林寺访客'
const defaultAvatar = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIwIiBoZWlnaHQ9IjEyMCIgdmlld0JveD0iMCAwIDEyMCAxMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMTgiIHkxPSIxNCIgeDI9IjEwMSIgeTI9IjEwNiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiNDRkE4NUEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM5RTcxMkQiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTIwIiBoZWlnaHQ9IjEyMCIgcng9IjYwIiBmaWxsPSJ1cmwoI2cpIi8+PGNpcmNsZSBjeD0iNjAiIGN5PSI0MiIgcj0iMTgiIGZpbGw9IiNGRkY3RTYiIGZpbGwtb3BhY2l0eT0iMC45NiIvPjxwYXRoIGQ9Ik0yNSA5MEMyNSA3NC41MzYgMzcuNTM2IDYyIDUzIDYySDY3QzgyLjQ2NCA2MiA5NSA3NC41MzYgOTUgOTBWOTJIMjVWOTBaIiBmaWxsPSIjRkZGN0U2IiBmaWxsLW9wYWNpdHk9IjAuOTYiLz48Y2lyY2xlIGN4PSI4OSIgY3k9IjI5IiByPSIxMCIgZmlsbD0iI0Y3REU5RCIgZmlsbC1vcGFjaXR5PSIwLjcyIi8+PC9zdmc+'
useDidShow(() => {
isAuthed.value = hasAuth()
})
const menuItems = [
{ key: 'orders', title: '我的订单', desc: '查看法物订单', icon: Order },
{ key: 'favorites', title: '我的收藏', desc: '收藏的法物', icon: Star },
{ key: 'history', title: '浏览记录', desc: '最近浏览', icon: Footprint },
{ key: 'address', title: '收货地址', desc: '管理地址', icon: Location },
]
const bottomMenuItems = [
{ key: 'settings', title: '设置', desc: '通知与隐私', icon: Setting },
{ key: 'about', title: '关于觉林寺', desc: '了解寺院历史', icon: Service },
{ key: 'help', title: '联系客堂', desc: '在线咨询', icon: Notice },
]
const onChooseAvatar = (e) => {
const { avatarUrl: url } = e.detail
avatarUrl.value = url
}
const openNicknameDialog = () => {
nicknameDraft.value = nickname.value
nicknameDialogVisible.value = true
}
const closeNicknameDialog = () => {
nicknameDialogVisible.value = false
}
const onNicknameDraftInput = (e) => {
nicknameDraft.value = e.detail.value
}
const confirmNickname = () => {
nickname.value = nicknameDraft.value.trim()
closeNicknameDialog()
}
const handleProfileTap = () => {}
const handleMenuTap = ({ title }) => {
Taro.showToast({ title: `${title}功能开发中`, icon: 'none' })
}
</script>
<style lang="less">
.mine-page {
min-height: 100vh;
background:
radial-gradient(circle at top right, rgba(166, 121, 57, 0.14), transparent 30%),
linear-gradient(180deg, #fffaf4 0%, #f3f5f9 100%);
radial-gradient(circle at top right, rgba(201, 163, 85, 0.16), transparent 26%),
radial-gradient(circle at 8% 22%, rgba(255, 244, 220, 0.9), transparent 22%),
linear-gradient(180deg, #fbf8f1 0%, #f4efe5 54%, #f8f5ee 100%);
position: relative;
// 顶部装饰背景
.header-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 304rpx;
border-radius: 0 0 48rpx 48rpx;
overflow: hidden;
}
.header-pattern {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image:
radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.08) 0%, transparent 50%),
radial-gradient(circle at 80% 60%, rgba(255, 255, 255, 0.06) 0%, transparent 40%);
}
.page-content {
padding: 32rpx 24rpx 0;
position: relative;
padding: 32rpx 28rpx 176rpx;
box-sizing: border-box;
z-index: 1;
}
.hero-card,
.status-card {
padding: 32rpx;
.hero-banner {
position: relative;
margin-top: 10rpx;
padding: 28rpx;
border-radius: 32rpx;
background: linear-gradient(135deg, rgba(122, 88, 31, 0.18), rgba(255, 255, 255, 0.18));
border: 2rpx solid rgba(255, 255, 255, 0.18);
box-shadow: inset 0 2rpx 0 rgba(255, 255, 255, 0.24);
backdrop-filter: blur(16rpx);
overflow: hidden;
box-sizing: border-box;
&__ornament {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 200rpx;
pointer-events: none;
}
&__orb {
position: absolute;
border-radius: 50%;
background: radial-gradient(circle, rgba(255, 255, 255, 0.36) 0%, rgba(255, 255, 255, 0.04) 70%, transparent 100%);
&--lg {
width: 180rpx;
height: 180rpx;
top: -24rpx;
right: -22rpx;
}
&--sm {
width: 92rpx;
height: 92rpx;
right: 42rpx;
bottom: 22rpx;
}
}
}
.profile-panel {
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 24rpx;
padding: 30rpx 28rpx;
min-height: 176rpx;
border-radius: 28rpx;
background: rgba(255, 255, 255, 0.94);
border: 2rpx solid rgba(166, 121, 57, 0.08);
box-shadow: 0 20rpx 60rpx rgba(15, 23, 42, 0.06);
background: linear-gradient(135deg, rgba(255, 252, 246, 0.9), rgba(255, 250, 240, 0.72));
border: 1rpx solid rgba(255, 255, 255, 0.45);
box-shadow: 0 18rpx 48rpx rgba(82, 58, 24, 0.08);
box-sizing: border-box;
&__avatar {
flex-shrink: 0;
}
.hero-title,
.section-title {
display: block;
font-size: 40rpx;
font-weight: 700;
color: #111827;
&__body {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 12rpx;
}
&__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
}
&__action {
flex-shrink: 0;
font-size: 30rpx;
line-height: 1;
color: rgba(142, 99, 37, 0.68);
}
&__name-block {
min-width: 0;
display: flex;
flex-direction: column;
gap: 6rpx;
}
&__subtext {
font-size: 22rpx;
line-height: 1.4;
color: #8f816b;
}
&__tip {
font-size: 22rpx;
line-height: 1.6;
color: #8f816b;
}
}
.avatar-ring {
width: 128rpx;
height: 128rpx;
border-radius: 50%;
padding: 4rpx;
background: linear-gradient(135deg, #e8d5a8, #c9a355);
}
.hero-desc,
.section-desc {
.avatar-btn {
padding: 0;
margin: 0;
width: 120rpx;
height: 120rpx;
border-radius: 50%;
overflow: hidden;
background: transparent;
border: none;
line-height: 1;
&::after {
border: none;
}
}
.avatar-img {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
display: block;
margin-top: 16rpx;
font-size: 26rpx;
line-height: 1.8;
color: #6b7280;
}
.status-card {
margin-top: 24rpx;
.profile-info {
display: none;
}
.nickname-text {
font-size: 36rpx;
font-weight: 700;
color: #2f2212;
line-height: 1.4;
}
.arrow-text {
font-size: 28rpx;
color: #c4a86a;
line-height: 1;
&--light {
color: #d1c9b5;
}
}
// 菜单分区
.section-title {
font-size: 30rpx;
padding: 34rpx 8rpx 18rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.section-title-text {
font-size: 26rpx;
font-weight: 600;
color: #8c7a5e;
letter-spacing: 2rpx;
position: relative;
padding-left: 18rpx;
&::before {
content: '';
position: absolute;
left: 0;
top: 8rpx;
bottom: 8rpx;
width: 6rpx;
border-radius: 999rpx;
background: linear-gradient(180deg, #d3af6b, #a67939);
}
}
// 网格菜单(常用服务)
.menu-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 18rpx;
background: rgba(255, 255, 255, 0.98);
border-radius: 24rpx;
border: 2rpx solid rgba(166, 121, 57, 0.08);
box-shadow: 0 18rpx 48rpx rgba(56, 40, 19, 0.06);
padding: 22rpx;
box-sizing: border-box;
}
.status-row {
margin-top: 20rpx;
.menu-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24rpx;
gap: 16rpx;
padding: 22rpx 20rpx;
min-height: 0;
border-radius: 20rpx;
background: linear-gradient(180deg, #fffdfa 0%, #fcf7ee 100%);
border: 1rpx solid rgba(166, 121, 57, 0.08);
box-sizing: border-box;
}
.status-tag {
.menu-item__main {
min-width: 0;
flex: 1;
display: flex;
align-items: center;
gap: 16rpx;
}
.menu-item__content {
min-width: 0;
display: flex;
flex-direction: column;
gap: 6rpx;
}
.menu-item-arrow {
flex-shrink: 0;
padding: 12rpx 22rpx;
border-radius: 999rpx;
font-size: 26rpx;
color: #c8b38d;
line-height: 1;
}
.menu-icon-wrap {
width: 72rpx;
height: 72rpx;
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: inset 0 2rpx 0 rgba(255, 255, 255, 0.45);
&.menu-icon--orders {
background: linear-gradient(135deg, #fef3e2, #fde8c8);
}
&.menu-icon--favorites {
background: linear-gradient(135deg, #fef1e8, #fde2cc);
}
&.menu-icon--history {
background: linear-gradient(135deg, #eef6ee, #ddefde);
}
&.menu-icon--address {
background: linear-gradient(135deg, #eef2f8, #dde5f0);
}
&.menu-icon--settings {
background: linear-gradient(135deg, #f0f0f0, #e4e4e4);
}
&.menu-icon--about {
background: linear-gradient(135deg, #fef3e2, #fde8c8);
}
&.menu-icon--help {
background: linear-gradient(135deg, #eef6ee, #ddefde);
}
}
.menu-item-title {
font-size: 24rpx;
font-weight: 600;
color: #b45309;
background: #fef3c7;
color: #342616;
line-height: 1.2;
}
.menu-item-desc {
font-size: 20rpx;
color: #978875;
line-height: 1.3;
}
// 列表菜单(更多)
.menu-list {
background: rgba(255, 255, 255, 0.98);
border-radius: 24rpx;
border: 2rpx solid rgba(166, 121, 57, 0.08);
box-shadow: 0 18rpx 48rpx rgba(56, 40, 19, 0.06);
overflow: hidden;
}
.status-tag.authed {
color: #166534;
background: #dcfce7;
.menu-list-item {
display: flex;
align-items: center;
padding: 28rpx 32rpx;
gap: 24rpx;
position: relative;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(252, 248, 240, 0.98));
&::after {
content: '';
position: absolute;
bottom: 0;
left: 100rpx;
right: 32rpx;
height: 1rpx;
background: rgba(0, 0, 0, 0.05);
}
&--last::after {
display: none;
}
}
.menu-list-icon {
width: 64rpx;
height: 64rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.menu-list-content {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 6rpx;
}
.menu-list-title {
font-size: 28rpx;
font-weight: 500;
color: #342616;
line-height: 1.3;
}
.menu-list-desc {
font-size: 22rpx;
color: #978875;
line-height: 1.2;
}
.page-footer-tip {
display: flex;
justify-content: center;
padding: 28rpx 0 8rpx;
&__text {
font-size: 22rpx;
letter-spacing: 4rpx;
color: rgba(143, 129, 107, 0.9);
}
}
.nickname-dialog-mask {
position: fixed;
inset: 0;
z-index: 20;
display: flex;
align-items: center;
justify-content: center;
padding: 32rpx;
background: rgba(33, 24, 12, 0.32);
box-sizing: border-box;
}
.nickname-dialog {
width: 100%;
padding: 34rpx 30rpx 30rpx;
border-radius: 28rpx;
background: #fffdf9;
box-shadow: 0 24rpx 64rpx rgba(33, 24, 12, 0.18);
box-sizing: border-box;
&__title {
display: block;
font-size: 32rpx;
font-weight: 600;
color: #2f2212;
line-height: 1.3;
}
&__desc {
display: block;
margin-top: 12rpx;
font-size: 24rpx;
line-height: 1.6;
color: #8f816b;
}
&__input {
margin-top: 24rpx;
height: 88rpx;
padding: 0 24rpx;
border-radius: 18rpx;
background: #f8f2e9;
border: 1rpx solid rgba(166, 121, 57, 0.14);
font-size: 28rpx;
color: #2f2212;
box-sizing: border-box;
}
&__actions {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 18rpx;
margin-top: 28rpx;
}
&__btn {
display: flex;
align-items: center;
justify-content: center;
height: 84rpx;
border-radius: 18rpx;
font-size: 28rpx;
font-weight: 600;
color: #fffdf8;
background: linear-gradient(135deg, #b98b42, #9e712d);
&--ghost {
color: #8e6325;
background: #f8f2e9;
}
}
}
}
</style>
......