|
|
@@ -0,0 +1,242 @@
|
|
|
+<template>
|
|
|
+ <view class="profile-container">
|
|
|
+ <view class="header">
|
|
|
+ <view class="avatar-section">
|
|
|
+ <view class="avatar">
|
|
|
+ <view class="avatar-frame">
|
|
|
+ <image class="avatar-img" :src="avatarSrc" mode="aspectFill" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="user-info">
|
|
|
+ <text class="username">{{ user.nickname || '慢病患者' }}</text>
|
|
|
+ <text class="user-id" v-if="user.nickname">ID: 123456</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ <view class="menu-list">
|
|
|
+ <view class="menu-item" @click="onMenuClick('health')">
|
|
|
+ <text class="menu-text">健康档案</text>
|
|
|
+ <text class="menu-arrow">></text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" @click="onMenuClick('settings')">
|
|
|
+ <text class="menu-text">设置</text>
|
|
|
+ <text class="menu-arrow">></text>
|
|
|
+ </view>
|
|
|
+ <view class="menu-item" @click="onMenuClick('about')">
|
|
|
+ <text class="menu-text">关于我们</text>
|
|
|
+ <text class="menu-arrow">></text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="logout-section">
|
|
|
+ <button class="logout-btn" @click="onLogout">退出登录</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref } from 'vue'
|
|
|
+import { computed } from 'vue'
|
|
|
+import { onShow } from '@dcloudio/uni-app'
|
|
|
+import PageTitle from '@/components/PageTitle.vue'
|
|
|
+
|
|
|
+const title = ref('个人中心')
|
|
|
+
|
|
|
+const user = ref<{ avatar?: string; nickname?: string }>({})
|
|
|
+
|
|
|
+const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
|
|
|
+
|
|
|
+// 计算一个安全的 avatar src:优先使用有效的 user.avatar,否则使用默认头像
|
|
|
+const avatarSrc = computed(() => {
|
|
|
+ const a = user.value?.avatar
|
|
|
+ if (!a) return defaultAvatarUrl
|
|
|
+ try {
|
|
|
+ const s = String(a)
|
|
|
+ // 常见有效前缀:http(s), data:, wxfile://, file://, /static
|
|
|
+ if (/^(https?:\/\/|data:|wxfile:\/\/|file:\/\/|\/static\/)/i.test(s)) {
|
|
|
+ return s
|
|
|
+ }
|
|
|
+ // 有时候小程序临时路径也以 / 开头或以 temp 开头,尽量允许以 ./ 或 / 开头
|
|
|
+ if (/^(\.|\/|temp)/i.test(s)) return s
|
|
|
+ } catch (e) {
|
|
|
+ // fallback
|
|
|
+ }
|
|
|
+ return defaultAvatarUrl
|
|
|
+})
|
|
|
+
|
|
|
+const loadUser = () => {
|
|
|
+ try {
|
|
|
+ const u = uni.getStorageSync('user_info')
|
|
|
+ if (u) {
|
|
|
+ user.value = u
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ // ignore
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 如果在微信小程序端且未登录,自动跳转到登录页
|
|
|
+onShow(() => {
|
|
|
+ loadUser()
|
|
|
+ if (!user.value?.nickname) {
|
|
|
+ // 使用 uni.reLaunch 替代 navigateTo,确保页面栈被清空
|
|
|
+ uni.reLaunch({ url: '/pages/login/login' })
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const onMenuClick = (type: string) => {
|
|
|
+ console.log('Menu clicked:', type)
|
|
|
+ uni.showToast({
|
|
|
+ title: `${type} 功能开发中`,
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const onLogout = () => {
|
|
|
+ uni.showModal({
|
|
|
+ title: '提示',
|
|
|
+ content: '确定要退出登录吗?',
|
|
|
+ success: (res) => {
|
|
|
+ if (res.confirm) {
|
|
|
+ uni.removeStorageSync('user_info')
|
|
|
+ user.value = {}
|
|
|
+ uni.showToast({ title: '已退出登录', icon: 'none' })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+.profile-container {
|
|
|
+ min-height: 100vh;
|
|
|
+ background-color: #f5f5f5;
|
|
|
+ padding-bottom: 100rpx; /* 给底边栏留空间 */
|
|
|
+}
|
|
|
+
|
|
|
+.header {
|
|
|
+ background-color: #fff;
|
|
|
+ padding: 40rpx;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar-section {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar {
|
|
|
+ width: 120rpx;
|
|
|
+ height: 120rpx;
|
|
|
+ border-radius: 60rpx;
|
|
|
+ background-color: #007aff;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-right: 30rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.custom-nav {
|
|
|
+ position: fixed;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ top: 0;
|
|
|
+ height: 88rpx;
|
|
|
+ padding-top: constant(safe-area-inset-top);
|
|
|
+ padding-top: env(safe-area-inset-top);
|
|
|
+ background-color: #fff;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ z-index: 1000;
|
|
|
+ border-bottom: 1rpx solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.nav-title {
|
|
|
+ font-size: 34rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar-frame {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ border-radius: 60rpx;
|
|
|
+ overflow: hidden; /* 裁切超出部分,避免溢出 */
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.avatar-img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover; /* 在部分平台下生效,保证图片填充同时裁切 */
|
|
|
+}
|
|
|
+
|
|
|
+.avatar-text {
|
|
|
+ color: #fff;
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+.user-info {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.username {
|
|
|
+ font-size: 36rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ display: block;
|
|
|
+ margin-bottom: 10rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.user-id {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.menu-list {
|
|
|
+ background-color: #fff;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.menu-item {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 30rpx 40rpx;
|
|
|
+ border-bottom: 1rpx solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.menu-item:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+
|
|
|
+.menu-text {
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.menu-arrow {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #ccc;
|
|
|
+}
|
|
|
+
|
|
|
+.logout-section {
|
|
|
+ padding: 40rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.logout-btn {
|
|
|
+ width: 100%;
|
|
|
+ background-color: #ff4757;
|
|
|
+ color: #fff;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ line-height: 80rpx;
|
|
|
+}
|
|
|
+</style>
|