82 lines
5.6 KiB
Vue
82 lines
5.6 KiB
Vue
<!-- 页面编号:M-02;用途:编辑个人资料。 -->
|
||
<template>
|
||
<view class="profile-edit-page" :class="{ 'profile-state--ready': profileState === 'ready', 'profile-state--saving': profileState === 'saving' }">
|
||
<ModulePageBackground module="profile" />
|
||
<view class="page-layer"><PageHeader title="个人资料" /></view>
|
||
<view class="page-content page-layer">
|
||
<view class="profile-avatar-card">
|
||
<image class="avatar-seal" src="/static/assets/foundation/transparent/brand-seal.png" mode="aspectFit" />
|
||
<view class="avatar-copy"><text>{{ profileForm.nickname || "未填写昵称" }}</text><text>头像将在相册权限接入后支持上传</text></view>
|
||
<view class="text-action" role="button" aria-label="选择头像" @click="chooseAvatar">选择头像</view>
|
||
</view>
|
||
|
||
<view class="form-panel">
|
||
<view class="form-row"><text>昵称</text><input v-model.trim="profileForm.nickname" maxlength="20" aria-label="昵称" placeholder="请输入昵称" /></view>
|
||
<text v-if="errors.nickname" class="field-error">{{ errors.nickname }}</text>
|
||
<view class="form-row"><text>真实姓名</text><input v-model.trim="profileForm.realName" maxlength="20" aria-label="真实姓名" placeholder="请输入真实姓名" /></view>
|
||
<view class="form-row"><text>常住地区</text><input v-model.trim="profileForm.region" maxlength="40" aria-label="常住地区" placeholder="省 / 市 / 区县" /></view>
|
||
<view class="textarea-row"><text>个人简介</text><textarea v-model.trim="profileForm.bio" auto-height maxlength="300" aria-label="个人简介" placeholder="介绍你的家族身份或经历" /></view>
|
||
<text class="counter">{{ profileForm.bio.length }}/300</text>
|
||
<text v-if="errors.bio" class="field-error">{{ errors.bio }}</text>
|
||
</view>
|
||
<AppButton block :disabled="profileState === 'saving'" :label="profileState === 'saving' ? '正在保存' : '保存资料'" @click="saveProfile" />
|
||
</view>
|
||
<AppToast :visible="toastVisible" :message="toastMessage" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { onUnmounted, reactive, ref } from "vue";
|
||
import AppButton from "@/components/AppButton.vue";
|
||
import AppToast from "@/components/AppToast.vue";
|
||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||
import PageHeader from "@/components/PageHeader.vue";
|
||
|
||
const profileForm = reactive({ nickname: "汤文清", realName: "汤文清", region: "河南省 周口市", bio: "热心参与家谱资料整理。" });
|
||
const errors = reactive({ nickname: "", bio: "" });
|
||
const profileState = ref("ready");
|
||
const toastVisible = ref(false);
|
||
const toastMessage = ref("");
|
||
let timer = null;
|
||
const showToast = (message) => { toastMessage.value = message; toastVisible.value = true; clearTimeout(timer); timer = setTimeout(() => (toastVisible.value = false), 1800); };
|
||
const chooseAvatar = () => showToast("头像选择将在相册权限接入后开放");
|
||
const validateProfile = () => {
|
||
errors.nickname = profileForm.nickname ? "" : "请填写昵称";
|
||
errors.bio = profileForm.bio.length > 300 ? "个人简介不能超过 300 字" : "";
|
||
return !errors.nickname && !errors.bio;
|
||
};
|
||
const saveProfile = () => {
|
||
if (!validateProfile() || profileState.value === "saving") return;
|
||
profileState.value = "saving";
|
||
clearTimeout(timer);
|
||
timer = setTimeout(() => { profileState.value = "ready"; showToast("个人资料已保存"); }, 500);
|
||
};
|
||
onUnmounted(() => clearTimeout(timer));
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
|
||
|
||
.profile-edit-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
|
||
.page-layer { z-index: 1; }
|
||
.page-content { flex: 1; padding: 26rpx 30rpx 70rpx; }
|
||
.profile-avatar-card, .form-panel { @include adaptive.adaptive-profile-field; }
|
||
.profile-avatar-card { display: grid; grid-template-columns: 92rpx minmax(0,1fr) auto; align-items: center; gap: 20rpx; min-height: 150rpx; padding: 24rpx 34rpx; box-sizing: border-box; }
|
||
.avatar-seal { width: 82rpx; height: auto; max-height: 92rpx; aspect-ratio: 82 / 92; }
|
||
.avatar-copy { min-width: 0; }
|
||
.avatar-copy text { display: block; overflow-wrap: anywhere; }
|
||
.avatar-copy text:first-child { color: $ink; font-size: 29rpx; font-weight: 700; }
|
||
.avatar-copy text:last-child { margin-top: 6rpx; color: $ink-muted; font-size: 20rpx; line-height: 1.45; }
|
||
.text-action { min-height: 72rpx; display: flex; align-items: center; color: $brand-red; font-size: 22rpx; }
|
||
.form-panel { margin-top: 22rpx; padding: 20rpx 34rpx 30rpx; box-sizing: border-box; }
|
||
.form-row { display: grid; grid-template-columns: 150rpx minmax(0,1fr); min-height: 92rpx; align-items: center; border-bottom: 1px solid rgba(181,137,63,.42); gap: 18rpx; }
|
||
.form-row > text, .textarea-row > text { color: $ink; font-size: 24rpx; font-weight: 700; }
|
||
.form-row input { width: auto; min-width: 0; min-height: 70rpx; color: $ink; font-size: 24rpx; text-align: right; }
|
||
.textarea-row { padding-top: 24rpx; }
|
||
.textarea-row textarea { display: block; width: auto; min-width: 0; min-height: 150rpx; margin-top: 14rpx; color: $ink; font-size: 24rpx; line-height: 1.6; }
|
||
.counter { display: block; color: $ink-muted; font-size: 20rpx; text-align: right; }
|
||
.field-error { display: block; padding-top: 7rpx; color: #b42318; font-size: 21rpx; text-align: right; }
|
||
.page-content > .app-button { margin-top: 26rpx; }
|
||
@media(max-width:340px){.page-content{padding-right:22rpx;padding-left:22rpx}.profile-avatar-card{grid-template-columns:72rpx minmax(0,1fr);padding-right:26rpx;padding-left:26rpx}.text-action{grid-column:2}.avatar-seal{width:68rpx;max-height:78rpx}.form-row{grid-template-columns:126rpx minmax(0,1fr)}}
|
||
</style>
|