256 lines
5.7 KiB
Vue
256 lines
5.7 KiB
Vue
<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>
|