Files
test/uniapp/pages/mine/profile/index.vue
T
2026-06-11 09:53:11 +08:00

256 lines
5.7 KiB
Vue
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="app-page" :class="themeClass">
<view class="surface-card page-hero">
<text class="hero-kicker">ACCOUNT</text>
<text class="hero-title">账户资料</text>
<text class="hero-desc">管理本地昵称显示资料与本机记账模式说明</text>
<view class="hero-tags">
<text class="hero-tag">本地资料</text>
<text class="hero-tag soft">本地存储</text>
</view>
</view>
<section-card title="昵称设置" subtitle="修改后仅用于个人页展示和首字头像,不参与账单计算">
<view class="profile-card surface-strong">
<view class="avatar-shell">{{ avatarText }}</view>
<view class="profile-body">
<text class="profile-name">{{ profileName }}</text>
<text class="profile-meta">当前昵称仅保存在本地设备可随时修改</text>
</view>
<view class="status-badge">本地</view>
</view>
<view class="editor-block">
<view class="input-shell">
<input v-model="nicknameInput" maxlength="12" placeholder="请输入昵称" />
</view>
<text class="tiny-text editor-tip">留空时页面会统一显示用户</text>
</view>
<view class="action-row">
<view class="ghost-button" @click="clearNickname">清空昵称</view>
<view class="primary-button" @click="saveNickname">保存昵称</view>
</view>
</section-card>
<section-card title="显示与模式" subtitle="集中展示当前账户页的生效状态">
<view class="info-list">
<view class="info-item surface-strong">
<view>
<text class="info-title">昵称首字头像</text>
<text class="info-desc">当前显示 {{ avatarText }}自动根据昵称生成</text>
</view>
<text class="info-mark">已启用</text>
</view>
<view class="info-item surface-strong">
<view>
<text class="info-title">本机记账模式</text>
<text class="info-desc">账单与预算默认仅保存在当前设备本地</text>
</view>
<text class="info-mark">默认</text>
</view>
</view>
</section-card>
<section-card title="使用提示" subtitle="帮助用户理解昵称显示与数据边界">
<view class="tips-card surface-strong">
<view v-for="(tip, index) in tips" :key="tip" class="tip-row">
<text class="tip-index">0{{ index + 1 }}</text>
<text class="tip-line">{{ tip }}</text>
</view>
</view>
</section-card>
</view>
</template>
<script setup>
import { computed, ref, watch } from 'vue'
import SectionCard from '../../../components/SectionCard.vue'
import { useAppStore } from '../../../utils/store'
const store = useAppStore()
const themeClass = computed(() => (store.state.settings.theme === 'dark' ? 'theme-dark' : ''))
const profileName = computed(() => store.state.settings.profile.nickname || '用户')
const avatarText = computed(() => profileName.value.slice(0, 1))
const nicknameInput = ref(store.state.settings.profile.nickname || '')
const tips = [
'昵称仅用于个人页展示和首字头像,不参与账单计算。',
'账单、预算和设置默认不会自动上传云端。',
'如需更换设备,请先在“备份与恢复”页面导出 JSON 备份。'
]
watch(
() => store.state.settings.profile.nickname,
(value) => {
nicknameInput.value = value || ''
}
)
function saveNickname() {
const nextName = nicknameInput.value.trim()
store.setProfile({
authorized: false,
nickname: nextName,
avatarUrl: ''
})
uni.showToast({ title: '昵称已保存', icon: 'none' })
}
function clearNickname() {
nicknameInput.value = ''
store.setProfile({
authorized: false,
nickname: '',
avatarUrl: ''
})
uni.showToast({ title: '昵称已清空', icon: 'none' })
}
</script>
<style lang="scss" scoped>
.page-hero {
padding: 30rpx;
background: linear-gradient(145deg, rgba(16, 42, 67, 0.96) 0%, rgba(31, 111, 95, 0.92) 100%);
color: #ffffff;
}
.hero-kicker,
.hero-desc,
.hero-tag.soft {
color: rgba(255, 255, 255, 0.76);
}
.hero-kicker {
font-size: 20rpx;
letter-spacing: 4rpx;
}
.hero-title {
display: block;
margin-top: 12rpx;
font-size: 44rpx;
font-weight: 700;
}
.hero-desc {
display: block;
margin-top: 14rpx;
font-size: 24rpx;
line-height: 1.7;
}
.hero-tags {
display: flex;
flex-wrap: wrap;
gap: 14rpx;
margin-top: 22rpx;
}
.hero-tag {
padding: 12rpx 18rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.16);
font-size: 22rpx;
}
.profile-card,
.info-item,
.action-row,
.tip-row {
display: flex;
align-items: center;
gap: 16rpx;
}
.profile-card,
.tips-card {
padding: 26rpx;
border-radius: 28rpx;
}
.avatar-shell {
width: 108rpx;
height: 108rpx;
border-radius: 32rpx;
background: var(--bg-accent);
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
font-weight: 700;
box-shadow: 0 18rpx 32rpx rgba(16, 42, 67, 0.16);
}
.profile-body {
flex: 1;
}
.profile-name,
.info-title {
display: block;
font-size: 31rpx;
font-weight: 700;
color: var(--text-primary);
}
.profile-meta,
.info-desc,
.tip-line {
display: block;
margin-top: 10rpx;
font-size: 24rpx;
line-height: 1.7;
color: var(--text-secondary);
}
.status-badge,
.info-mark {
padding: 10rpx 18rpx;
border-radius: 999rpx;
background: var(--brand-soft);
font-size: 22rpx;
color: var(--brand);
}
.editor-block {
margin-top: 18rpx;
}
.editor-tip {
display: block;
margin-top: 12rpx;
}
.action-row {
margin-top: 18rpx;
}
.action-row .ghost-button,
.action-row .primary-button {
flex: 1;
}
.info-list {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.info-item {
justify-content: space-between;
padding: 24rpx;
border-radius: 26rpx;
}
.tip-row {
align-items: flex-start;
padding: 12rpx 0;
}
.tip-index {
width: 56rpx;
font-size: 24rpx;
font-weight: 700;
color: var(--brand);
}
</style>