Files
jiapuapp/pages/profile/m01-profile-home.vue
T
2026-07-27 06:50:23 +08:00

119 lines
6.1 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.
<!-- 页面编号M-01用途个人中心显示已核实的当前用户资料 -->
<template>
<view class="profile-page">
<ModulePageBackground module="profile" />
<view class="page-header"><PageHeader root title="我的" /></view>
<view class="page-content">
<view v-if="loading" class="state-card"><text>正在读取个人资料</text></view>
<view v-else-if="loadError" class="state-card">
<text>个人资料暂时无法读取</text>
<text>{{ loadError }}</text>
<AppButton block label="重新读取" @click="loadProfile" />
</view>
<template v-else>
<view class="profile-summary">
<view class="profile-summary__avatar"><image src="/static/assets/foundation/transparent/auth-login-outline.png" mode="aspectFit" /></view>
<view class="profile-summary__identity">
<text>{{ profile.nickName || "未设置昵称" }}</text>
<text>{{ profile.realName || "未设置真实姓名" }}</text>
<text>{{ profile.phone || "未提供手机号" }}</text>
</view>
<AppButton class="profile-summary__edit" label="编辑资料" @click="openEdit" />
</view>
<view class="profile-fields">
<view class="profile-field"><text>性别</text><text>{{ sexText }}</text></view>
<view class="profile-field"><text>生日</text><text>{{ birthdayText }}</text></view>
<view class="profile-field"><text>邮箱</text><text>{{ profile.email || "未设置" }}</text></view>
</view>
<view class="profile-menu">
<AppButton block label="账号与安全" @click="open('M03')" />
<AppButton block label="消息中心" @click="open('N01')" />
<AppButton block label="帮助中心" @click="open('M06')" />
<AppButton block label="意见反馈" @click="open('M07')" />
<AppButton block label="应用推广" @click="open('M08')" />
<AppButton block label="VIP 服务" @click="open('M09')" />
<AppButton block label="关于与设置" @click="open('M10')" />
</view>
</template>
</view>
<AppTabbar active="profile" />
</view>
</template>
<script setup>
import { computed, reactive, ref } from "vue";
import { onShow, onUnload } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppTabbar from "@/components/AppTabbar.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
import { appApi, createRequestController, isRequestCancelled } from "@/utils/api.js";
import { openPage } from "@/utils/navigation.js";
const profile = reactive({ avatar: null, nickName: "", realName: "", phone: "", sex: "", birthday: "", email: "" });
const loading = ref(false);
const loadError = ref("");
const requestController = createRequestController();
let pageActive = true;
const sexText = computed(() => (profile.sex ? "待确认" : "未设置"));
const birthdayText = computed(() => /^\d{4}-\d{2}-\d{2}/.test(profile.birthday) ? profile.birthday.slice(0, 10) : "未设置");
const loadProfile = async () => {
if (loading.value) return;
loading.value = true;
loadError.value = "";
try {
const data = await appApi.getProfile({ requestController });
if (!pageActive) return;
Object.assign(profile, data);
} catch (error) {
if (pageActive && !isRequestCancelled(error)) {
loadError.value = error?.message || "请稍后重试";
}
} finally {
if (pageActive) loading.value = false;
}
};
const open = (route) => openPage(route, {}, "M01");
const openEdit = () => open("M02");
onShow(loadProfile);
onUnload(() => {
pageActive = false;
requestController.abort();
});
</script>
<style scoped lang="scss">
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
.profile-page { display: flex; min-height: 100vh; flex-direction: column; background: $paper; }
.page-header, .page-content { z-index: 1; }
.page-content { flex: 1; padding: 18rpx 24rpx 190rpx; }
.state-card, .profile-summary, .profile-fields { @include adaptive.adaptive-family-content; }
.state-card { box-sizing: border-box; min-height: 340rpx; padding: 78rpx 52rpx 50rpx; text-align: center; }
.state-card text { display: block; }
.state-card text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 35rpx; font-weight: 700; }
.state-card text:nth-child(2) { margin-top: 18rpx; color: $ink-muted; font-size: 24rpx; line-height: 1.65; }
.state-card .app-button { margin-top: 28rpx; }
.profile-summary { display: grid; grid-template-columns: 96rpx minmax(0, 1fr); align-items: center; gap: 18rpx; padding: 30rpx; }
.profile-summary__avatar { display: flex; width: 96rpx; height: 96rpx; align-items: center; justify-content: center; border: 1rpx solid rgba(159, 23, 15, .32); border-radius: 50%; background: rgba(159, 23, 15, .06); }
.profile-summary__avatar image { width: 64rpx; height: 64rpx; opacity: .8; }
.profile-summary__identity { min-width: 0; }
.profile-summary__identity text { display: block; overflow-wrap: anywhere; }
.profile-summary__identity text:first-child { color: $ink; font-family: STKaiti, KaiTi, serif; font-size: 32rpx; font-weight: 700; }
.profile-summary__identity text:nth-child(2), .profile-summary__identity text:last-child { margin-top: 5rpx; color: $ink-muted; font-size: 21rpx; }
.profile-summary__edit { grid-column: 1 / -1; margin-top: 6rpx; }
.profile-fields { display: flex; flex-direction: column; margin-top: 18rpx; padding: 8rpx 30rpx; }
.profile-field { display: flex; min-height: 82rpx; align-items: center; justify-content: space-between; gap: 20rpx; border-bottom: 1rpx solid rgba(181, 137, 63, .3); }
.profile-field:last-child { border-bottom: 0; }
.profile-field text:first-child { flex: 0 0 auto; color: $ink; font-size: 24rpx; font-weight: 700; }
.profile-field text:last-child { min-width: 0; color: $ink-muted; font-size: 22rpx; overflow-wrap: anywhere; text-align: right; }
.profile-menu { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14rpx; margin-top: 24rpx; }
.profile-menu .app-button { margin: 0; }
</style>