211 lines
9.1 KiB
Vue
211 lines
9.1 KiB
Vue
<template>
|
|
<view class="profile-page">
|
|
<view class="header-wrap" :style="{ paddingTop: statusBarHeight + 'px' }">
|
|
<view class="nav-bar" :style="{ height: navBarHeight + 'px' }">
|
|
<text class="nav-title">● 个人中心</text>
|
|
<text class="settings-symbol" @click="goToSettings">⚙</text>
|
|
</view>
|
|
</view>
|
|
|
|
<scroll-view class="content" scroll-y :style="{ height: 'calc(100vh - ' + totalHeaderHeight + 'px)' }">
|
|
<view class="content-inner">
|
|
<view class="user-card">
|
|
<view class="user-row">
|
|
<view class="big-avatar"><text>{{ avatarText }}</text></view>
|
|
<view class="user-info">
|
|
<text class="user-name">{{ userInfo.nickname }}</text>
|
|
<view class="user-tag">
|
|
<text>{{ userInfo.memberNo }}</text>
|
|
</view>
|
|
</view>
|
|
<text class="edit-symbol" @click="editProfile">✎</text>
|
|
</view>
|
|
<view class="user-details">
|
|
<view class="detail-row">
|
|
<text class="detail-label">手机号</text>
|
|
<text class="detail-value">{{ userInfo.phone }}</text>
|
|
</view>
|
|
<view class="detail-row">
|
|
<text class="detail-label">性别</text>
|
|
<text class="detail-value">{{ genderLabel }}</text>
|
|
</view>
|
|
<view class="detail-row">
|
|
<text class="detail-label">生日</text>
|
|
<text class="detail-value">{{ userInfo.birthday || '未设置' }}</text>
|
|
</view>
|
|
<view class="detail-row">
|
|
<text class="detail-label">上次登录</text>
|
|
<text class="detail-value">{{ userInfo.lastLoginAt || '暂无' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="stats-card">
|
|
<view class="stat-item">
|
|
<text class="stat-number">{{ userInfo.totalSignInDays }}</text>
|
|
<text class="stat-label">累计签到(天)</text>
|
|
</view>
|
|
<view class="stat-divider"></view>
|
|
<view class="stat-item">
|
|
<text class="stat-number">{{ userInfo.monthSignInCount }}</text>
|
|
<text class="stat-label">本月签到</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="sign-section">
|
|
<view class="sign-header">
|
|
<view class="section-title-row">
|
|
<text class="section-symbol">☰</text>
|
|
<text class="section-title">最近签到</text>
|
|
</view>
|
|
<text class="section-more" @click="viewAllSignIn">查看全部</text>
|
|
</view>
|
|
<view v-for="(record, idx) in signInRecords" :key="idx" class="sign-item">
|
|
<view class="sign-left">
|
|
<text class="sign-date">{{ record.date }}</text>
|
|
<text class="sign-time">{{ record.time }}</text>
|
|
</view>
|
|
<view class="sign-right">
|
|
<text class="sign-type">{{ record.typeLabel }}</text>
|
|
<text class="sign-source">{{ record.sourceLabel }}</text>
|
|
<text class="sign-status" :class="record.status">{{ record.statusLabel }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="bottom-safe"></view>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
const store = require('../../store/index')
|
|
const memberApi = require('../../api/member')
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
statusBarHeight: 0,
|
|
navBarHeight: 44,
|
|
totalHeaderHeight: 44,
|
|
loading: true,
|
|
userInfo: { nickname: '加载中...', memberNo: '', phone: '', gender: 0, birthday: '', lastLoginAt: '', totalSignInDays: 0, monthSignInCount: 0 },
|
|
signInRecords: []
|
|
}
|
|
},
|
|
onLoad() {
|
|
const systemInfo = uni.getSystemInfoSync()
|
|
const statusBarHeight = systemInfo.statusBarHeight || 20
|
|
let navBarHeight = 44
|
|
// #ifdef MP-WEIXIN
|
|
const menuButton = uni.getMenuButtonBoundingClientRect()
|
|
if (menuButton) {
|
|
navBarHeight = (menuButton.top - statusBarHeight) * 2 + menuButton.height
|
|
}
|
|
// #endif
|
|
this.statusBarHeight = statusBarHeight
|
|
this.navBarHeight = navBarHeight
|
|
this.totalHeaderHeight = statusBarHeight + navBarHeight
|
|
},
|
|
onShow() {
|
|
if (!store.isLoggedIn) {
|
|
return
|
|
}
|
|
this.loadData()
|
|
},
|
|
methods: {
|
|
async loadData() {
|
|
this.loading = true
|
|
try {
|
|
const [memberResult, signInResult] = await Promise.allSettled([
|
|
memberApi.getMemberInfo(),
|
|
memberApi.getCheckInRecords({ page: 0, size: 5 })
|
|
])
|
|
|
|
if (memberResult.status === 'fulfilled') {
|
|
const info = memberResult.value
|
|
this.userInfo = {
|
|
nickname: info.nickname || '会员',
|
|
memberNo: info.memberNo || ('ID' + (info.id || info.memberId || '')),
|
|
phone: info.phone ? info.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') : '未绑定',
|
|
gender: info.gender || 0,
|
|
birthday: info.birthday || '',
|
|
lastLoginAt: info.lastLoginAt || '',
|
|
totalSignInDays: info.totalSignInDays || 0,
|
|
monthSignInCount: info.monthSignInCount || 0
|
|
}
|
|
}
|
|
|
|
if (signInResult.status === 'fulfilled') {
|
|
const records = Array.isArray(signInResult.value)
|
|
? signInResult.value
|
|
: (signInResult.value.content || [])
|
|
this.signInRecords = records.map(r => ({
|
|
date: (r.checkInTime || r.createdAt || '').substring(5, 10) || '--',
|
|
time: (r.checkInTime || r.createdAt || '').substring(11, 16) || '--',
|
|
typeLabel: r.typeLabel || '签到',
|
|
sourceLabel: r.sourceLabel || '小程序',
|
|
status: r.status === 'SUCCESS' || r.status === 1 ? 'success' : 'fail',
|
|
statusLabel: r.status === 'SUCCESS' || r.status === 1 ? '成功' : '失败'
|
|
}))
|
|
}
|
|
} catch (e) {
|
|
console.error('个人中心数据加载失败:', e)
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
editProfile() { uni.showToast({ title: '编辑资料', icon: 'none' }) },
|
|
goToSettings() { uni.showToast({ title: '设置页面', icon: 'none' }) },
|
|
viewAllSignIn() { uni.showToast({ title: '全部签到记录', icon: 'none' }) }
|
|
},
|
|
computed: {
|
|
avatarText() { return (this.userInfo.nickname || '?').charAt(0) },
|
|
genderLabel() { const g = this.userInfo.gender; return g === 1 ? '男' : g === 2 ? '女' : '未设置' }
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.profile-page { min-height: 100vh; background: #F5F7FA; overflow-x: hidden; }
|
|
.header-wrap { background: #1A1A1A; }
|
|
.nav-bar { background: #1A1A1A; padding: 0 20px; display: flex; justify-content: space-between; align-items: center; }
|
|
.nav-title { font-size: 18px; font-weight: 700; color: #FFFFFF; }
|
|
.settings-symbol { font-size: 20px; color: rgba(255,255,255,0.8); }
|
|
.content-inner { padding: 16px 16px 0; }
|
|
.user-card { background: #FFFFFF; border-radius: 20px; padding: 20px; margin-bottom: 16px; box-shadow: 0 4px 12px rgba(0,0,0,0.06); }
|
|
.user-row { display: flex; align-items: center; margin-bottom: 18px; }
|
|
.big-avatar { width: 56px; height: 56px; background: linear-gradient(135deg, #00E676, #00BFA5); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 24px; color: #1A1A1A; flex-shrink: 0; margin-right: 14px; }
|
|
.user-info { flex: 1; display: flex; flex-direction: column; }
|
|
.user-name { font-size: 18px; font-weight: 700; color: #1E1E1E; }
|
|
.user-tag { font-size: 13px; color: #00C853; font-weight: 500; }
|
|
.edit-symbol { font-size: 20px; color: #7A7E84; }
|
|
.user-details { background: #F5F7FA; border-radius: 12px; padding: 14px 16px; display: flex; flex-direction: column; }
|
|
.detail-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
|
|
.detail-label { font-size: 13px; color: #7A7E84; }
|
|
.detail-value { font-size: 14px; font-weight: 600; color: #1E1E1E; }
|
|
.stats-card { background: #1A1A1A; border-radius: 20px; padding: 18px 20px; display: flex; justify-content: space-around; margin-bottom: 16px; box-shadow: 0 4px 12px rgba(0,0,0,0.06); }
|
|
.stat-item { display: flex; flex-direction: column; align-items: center; }
|
|
.stat-number { font-size: 24px; font-weight: 700; color: #00E676; }
|
|
.stat-label { font-size: 12px; color: rgba(255,255,255,0.6); margin-top: 4px; }
|
|
.stat-divider { width: 1px; background: rgba(255,255,255,0.15); }
|
|
.section-title-row { display: flex; align-items: center; margin-bottom: 12px; }
|
|
.section-symbol { font-size: 16px; color: #00E676; margin-right: 8px; }
|
|
.section-title { font-size: 16px; font-weight: 700; color: #1E1E1E; }
|
|
.sign-section { margin-bottom: 16px; }
|
|
.sign-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
|
|
.sign-header .section-title-row { margin-bottom: 0; }
|
|
.section-more { font-size: 13px; color: #00C853; }
|
|
.sign-item { background: #FFFFFF; border-radius: 12px; padding: 12px 16px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.06); }
|
|
.sign-left { display: flex; flex-direction: column; }
|
|
.sign-date { font-size: 14px; font-weight: 600; color: #1E1E1E; }
|
|
.sign-time { font-size: 12px; color: #7A7E84; margin-top: 2px; }
|
|
.sign-right { display: flex; align-items: center; }
|
|
.sign-type { font-size: 12px; color: #7A7E84; margin-right: 6px; }
|
|
.sign-source { font-size: 11px; color: #BDBDBD; background: #F5F7FA; padding: 1px 6px; border-radius: 8px; margin-right: 6px; }
|
|
.sign-status { font-size: 12px; font-weight: 600; padding: 3px 10px; border-radius: 20px; }
|
|
.sign-status.success { background: rgba(0,230,118,0.15); color: #00C853; }
|
|
.sign-status.fail { background: rgba(255,82,82,0.15); color: #FF5252; }
|
|
.bottom-safe { height: 50px; }
|
|
</style>
|