chore: 首次提交 - 关联 GitHub fnvtk/MBTI_wang
Made-with: Cursor
This commit is contained in:
318
miniprogram/pages/profile/index.js
Normal file
318
miniprogram/pages/profile/index.js
Normal file
@@ -0,0 +1,318 @@
|
||||
// pages/profile/index.js - 我的页面
|
||||
const app = getApp()
|
||||
const { getTypeOnly } = require('../../utils/resultFormat')
|
||||
const { request } = require('../../utils/request')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
hasLogin: false,
|
||||
userInfo: null,
|
||||
balance: 0,
|
||||
testCount: 0,
|
||||
hasResults: false,
|
||||
mbtiType: '',
|
||||
discType: '',
|
||||
pdpType: '',
|
||||
aiType: '',
|
||||
mbtiTime: '',
|
||||
discTime: '',
|
||||
pdpTime: '',
|
||||
aiTime: '',
|
||||
/** 最近记录的数据库 ID,用于跳转时传参 */
|
||||
mbtiResultId: null,
|
||||
discResultId: null,
|
||||
pdpResultId: null,
|
||||
aiResultId: null,
|
||||
loginLoading: false,
|
||||
loginFailed: false,
|
||||
nicknameDisplay: '',
|
||||
displayNickname: '',
|
||||
/** 默认头像(根据昵称):无头像时显示首字+背景色 */
|
||||
avatarLetter: '登',
|
||||
avatarBgColor: '#6366f1',
|
||||
// 推广中心统计与配置(来自 /api/distribution/stats)
|
||||
promoDistributionEnabled: true,
|
||||
promoCenterTitle: '推广中心',
|
||||
promoTotalInvite: 0,
|
||||
promoTotalEarned: '0.00',
|
||||
promoWithdrawable: '0.00',
|
||||
/** 是否有企业权限(绑定企业):有则显示「我的简历」 */
|
||||
hasEnterprise: false
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
// 仅在 onLoad 执行一次,onShow 里的 runLoginThenLoad 会导致重复请求
|
||||
this.runLoginThenLoad()
|
||||
},
|
||||
onShow() {
|
||||
// 如果是从其他页面返回,且已经登录,则只刷新数据而不重新执行登录流程
|
||||
if (this.data.hasLogin) {
|
||||
this.loadData()
|
||||
}
|
||||
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||
this.getTabBar().setData({ selected: 2 })
|
||||
}
|
||||
},
|
||||
|
||||
/** 先确保登录完成(静默登录),再刷新页面数据 */
|
||||
runLoginThenLoad() {
|
||||
this.setData({ loginLoading: true, loginFailed: false })
|
||||
app.ensureLogin().then((ok) => {
|
||||
this.setData({ loginLoading: false, loginFailed: !ok })
|
||||
this.loadData()
|
||||
}).catch(() => {
|
||||
this.setData({ loginLoading: false, loginFailed: true })
|
||||
this.loadData()
|
||||
})
|
||||
},
|
||||
|
||||
/** 生成随机后缀:仅 26 英文字母(大小写)与数字,不含特殊字符,默认 4 位 */
|
||||
_randomWechatUserSuffix(len) {
|
||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
||||
let s = ''
|
||||
for (let i = 0; i < (len || 4); i++) {
|
||||
s += chars[Math.floor(Math.random() * chars.length)]
|
||||
}
|
||||
return s
|
||||
},
|
||||
|
||||
/** 根据昵称生成默认头像的首字与背景色(同一昵称同色) */
|
||||
_avatarFromNickname(name) {
|
||||
const str = (name && String(name).trim()) || '登'
|
||||
const letter = str.charAt(0).toUpperCase() || '登'
|
||||
const palette = ['#6366f1', '#8b5cf6', '#ec4899', '#f43f5e', '#14b8a6', '#0ea5e9', '#3b82f6', '#eab308']
|
||||
let hash = 0
|
||||
for (let i = 0; i < str.length; i++) hash += str.charCodeAt(i)
|
||||
const bgColor = palette[Math.abs(hash) % palette.length]
|
||||
return { avatarLetter: letter, avatarBgColor: bgColor }
|
||||
},
|
||||
|
||||
/** 获取或生成当前用户的「微信用户XXXX」后缀(同一用户固定,仅 26 字母+数字) */
|
||||
_getOrCreateWechatUserSuffix(userInfo) {
|
||||
const uid = (userInfo && (userInfo.id || userInfo.userId)) ? String(userInfo.id || userInfo.userId) : '_default'
|
||||
const storageKey = 'wechat_user_suffix'
|
||||
const stored = wx.getStorageSync(storageKey)
|
||||
if (stored && typeof stored === 'object' && stored[uid]) return stored[uid]
|
||||
const suffix = this._randomWechatUserSuffix(4)
|
||||
const next = { ...(stored && typeof stored === 'object' ? stored : {}), [uid]: suffix }
|
||||
wx.setStorageSync(storageKey, next)
|
||||
return suffix
|
||||
},
|
||||
|
||||
loadData() {
|
||||
const userInfo = app.globalData.userInfo || wx.getStorageSync('userInfo')
|
||||
const token = app.globalData.token || wx.getStorageSync('token')
|
||||
|
||||
// 1. 先同步渲染用户基础信息(昵称/头像)
|
||||
const nickname = (userInfo && (userInfo.nickname || userInfo.nickName))
|
||||
? String(userInfo.nickname || userInfo.nickName).trim() : ''
|
||||
let nicknameDisplay = nickname
|
||||
let displayNickname = ''
|
||||
if (!nickname && userInfo) {
|
||||
const suffix = this._getOrCreateWechatUserSuffix(userInfo)
|
||||
displayNickname = '微信用户' + suffix
|
||||
nicknameDisplay = displayNickname
|
||||
}
|
||||
const avatarFromName = userInfo
|
||||
? this._avatarFromNickname(nicknameDisplay)
|
||||
: this._avatarFromNickname('点击登录')
|
||||
|
||||
const hasEnterprise = !!(userInfo && (userInfo.hasEnterprise === true || (userInfo.enterpriseId && Number(userInfo.enterpriseId) > 0)))
|
||||
this.setData({
|
||||
hasLogin: !!token || !!userInfo,
|
||||
userInfo: userInfo || null,
|
||||
hasEnterprise,
|
||||
nicknameDisplay,
|
||||
displayNickname,
|
||||
avatarLetter: avatarFromName.avatarLetter,
|
||||
avatarBgColor: avatarFromName.avatarBgColor
|
||||
})
|
||||
|
||||
// 2. 已登录则从服务端拉取最近记录;失败降级读 localStorage
|
||||
if (token || userInfo) {
|
||||
this._loadRecentFromAPI()
|
||||
this._loadPromoStats()
|
||||
}
|
||||
},
|
||||
|
||||
/** 从 /api/test/recent 拉取各类型最新记录 */
|
||||
_loadRecentFromAPI() {
|
||||
const scope = app.globalData.appScope || 'personal'
|
||||
request({
|
||||
url: `/api/test/recent?scope=${scope}`,
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
// res 是 wx.request 原始响应:{ statusCode, data: { code, data, message } }
|
||||
const payload = res && res.data
|
||||
if (!payload || payload.code !== 200 || !payload.data) {
|
||||
this._loadRecentFromStorage()
|
||||
return
|
||||
}
|
||||
const { records = {}, totalCount = 0 } = payload.data
|
||||
const r = records
|
||||
|
||||
// DISC resultText 后端已含「型」,type badge 只显示字母,去掉「型」
|
||||
const discType = r.disc ? r.disc.resultText.replace(/型$/, '') : ''
|
||||
|
||||
this.setData({
|
||||
testCount: totalCount,
|
||||
hasResults: !!(r.mbti || r.disc || r.pdp || r.ai),
|
||||
mbtiType: r.mbti ? r.mbti.resultText : '',
|
||||
discType,
|
||||
pdpType: r.pdp ? r.pdp.resultText : '',
|
||||
aiType: r.ai ? r.ai.resultText : '',
|
||||
mbtiTime: r.mbti ? r.mbti.testTime : '',
|
||||
discTime: r.disc ? r.disc.testTime : '',
|
||||
pdpTime: r.pdp ? r.pdp.testTime : '',
|
||||
aiTime: r.ai ? r.ai.testTime : '',
|
||||
mbtiResultId: r.mbti ? r.mbti.id : null,
|
||||
discResultId: r.disc ? r.disc.id : null,
|
||||
pdpResultId: r.pdp ? r.pdp.id : null,
|
||||
aiResultId: r.ai ? r.ai.id : null,
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
this._loadRecentFromStorage()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 从 /api/distribution/stats 拉取推广中心统计 */
|
||||
_loadPromoStats() {
|
||||
request({
|
||||
url: '/api/distribution/stats',
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
const payload = res && res.data
|
||||
if (payload && payload.code === 200 && payload.data) {
|
||||
const d = payload.data
|
||||
this.setData({
|
||||
promoDistributionEnabled: d.distributionEnabled !== false,
|
||||
promoCenterTitle: d.promoCenterTitle || '推广中心',
|
||||
promoTotalInvite: d.totalInvite || 0,
|
||||
promoTotalEarned: d.totalEarned || '0.00',
|
||||
promoWithdrawable: d.walletBalance || '0.00'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 降级:从 localStorage 读最近记录(兼容离线或 API 失败) */
|
||||
_loadRecentFromStorage() {
|
||||
const mbtiResult = wx.getStorageSync('mbtiResult')
|
||||
const discResult = wx.getStorageSync('discResult')
|
||||
const pdpResult = wx.getStorageSync('pdpResult')
|
||||
const aiResult = wx.getStorageSync('aiResult')
|
||||
|
||||
let testCount = 0
|
||||
if (mbtiResult) testCount++
|
||||
if (discResult) testCount++
|
||||
if (pdpResult) testCount++
|
||||
if (aiResult) testCount++
|
||||
|
||||
const _fmt = (ts) => {
|
||||
if (!ts) return ''
|
||||
const d = new Date(typeof ts === 'number' && ts < 1e12 ? ts * 1000 : ts)
|
||||
if (isNaN(d.getTime())) return ''
|
||||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
this.setData({
|
||||
testCount,
|
||||
hasResults: testCount > 0,
|
||||
mbtiType: mbtiResult ? getTypeOnly(mbtiResult, 'mbti') : '',
|
||||
discType: discResult ? getTypeOnly(discResult, 'disc') : '',
|
||||
pdpType: pdpResult ? getTypeOnly(pdpResult, 'pdp') : '',
|
||||
aiType: aiResult ? (aiResult.mbtiType || aiResult.type || '') : '',
|
||||
mbtiTime: _fmt(mbtiResult && (mbtiResult.createdAt || mbtiResult.timestamp || mbtiResult.testTime)),
|
||||
discTime: _fmt(discResult && (discResult.createdAt || discResult.timestamp || discResult.testTime)),
|
||||
pdpTime: _fmt(pdpResult && (pdpResult.createdAt || pdpResult.timestamp || pdpResult.testTime)),
|
||||
aiTime: _fmt(aiResult && (aiResult.createdAt || aiResult.timestamp || aiResult.testTime)),
|
||||
mbtiResultId: null,
|
||||
discResultId: null,
|
||||
pdpResultId: null,
|
||||
aiResultId: null,
|
||||
})
|
||||
},
|
||||
|
||||
/** 点击登录:先静默登录拿到 token */
|
||||
doLogin() {
|
||||
this.setData({ loginLoading: true, loginFailed: false })
|
||||
app.ensureLogin().then((ok) => {
|
||||
this.setData({ loginLoading: false })
|
||||
if (ok) {
|
||||
this.loadData()
|
||||
} else {
|
||||
wx.showToast({ title: '登录失败,请检查网络或稍后重试', icon: 'none' })
|
||||
this.setData({ loginFailed: true })
|
||||
}
|
||||
}).catch(() => {
|
||||
this.setData({ loginLoading: false, loginFailed: true })
|
||||
wx.showToast({ title: '登录失败', icon: 'none' })
|
||||
})
|
||||
},
|
||||
|
||||
goToIndex() { wx.switchTab({ url: '/pages/index/index' }) },
|
||||
goToCamera() { wx.switchTab({ url: '/pages/index/camera' }) },
|
||||
goToHistory() { wx.navigateTo({ url: '/pages/history/index' }) },
|
||||
goToUserProfile() { wx.navigateTo({ url: '/pages/user-profile/index' }) },
|
||||
goToPurchase() { wx.navigateTo({ url: '/pages/purchase/index?tab=personal' }) },
|
||||
goToPurchasePersonal() { wx.navigateTo({ url: '/pages/purchase/index?tab=personal' }) },
|
||||
goToPurchaseEnterprise() { wx.navigateTo({ url: '/pages/purchase/index?tab=enterprise' }) },
|
||||
goToEnterprise() { wx.navigateTo({ url: '/pages/enterprise/index' }) },
|
||||
goToPromo() { wx.navigateTo({ url: '/pages/promo/index' }) },
|
||||
goToMyResume() { wx.navigateTo({ url: '/pages/enterprise/resume-history' }) },
|
||||
goToSettings() {
|
||||
wx.showToast({ title: '开发中', icon: 'none' })
|
||||
},
|
||||
shareApp() {
|
||||
// 触发分享
|
||||
},
|
||||
viewMBTI() {
|
||||
const id = this.data.mbtiResultId
|
||||
wx.navigateTo({ url: id ? `/pages/result/mbti?id=${id}&type=mbti` : '/pages/result/mbti' })
|
||||
},
|
||||
viewDISC() {
|
||||
const id = this.data.discResultId
|
||||
wx.navigateTo({ url: id ? `/pages/result/disc?id=${id}&type=disc` : '/pages/result/disc' })
|
||||
},
|
||||
viewPDP() {
|
||||
const id = this.data.pdpResultId
|
||||
wx.navigateTo({ url: id ? `/pages/result/pdp?id=${id}&type=pdp` : '/pages/result/pdp' })
|
||||
},
|
||||
viewAI() {
|
||||
const id = this.data.aiResultId
|
||||
wx.navigateTo({ url: id ? `/pages/index/result?id=${id}&type=ai` : '/pages/index/result' })
|
||||
},
|
||||
|
||||
logout() {
|
||||
wx.showModal({
|
||||
title: '确认退出',
|
||||
content: '退出后测试记录仍会保留',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
app.logout()
|
||||
this.setData({ hasLogin: false, userInfo: null, loginFailed: false })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
const { getSharePathByScope } = require('../../utils/share')
|
||||
return {
|
||||
title: '神仙团队AI性格测试 - 发现你的MBTI类型',
|
||||
path: getSharePathByScope('/pages/index/index')
|
||||
}
|
||||
},
|
||||
|
||||
onShareTimeline() {
|
||||
const { buildShareQuery } = require('../../utils/share')
|
||||
return {
|
||||
title: '神仙团队AI性格测试 - 发现你的MBTI类型',
|
||||
query: buildShareQuery()
|
||||
}
|
||||
}
|
||||
})
|
||||
6
miniprogram/pages/profile/index.json
Normal file
6
miniprogram/pages/profile/index.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "",
|
||||
"navigationBarBackgroundColor": "#F9FAFB",
|
||||
"navigationBarTextStyle": "black",
|
||||
"usingComponents": {}
|
||||
}
|
||||
212
miniprogram/pages/profile/index.wxml
Normal file
212
miniprogram/pages/profile/index.wxml
Normal file
@@ -0,0 +1,212 @@
|
||||
<!--pages/profile/index.wxml-->
|
||||
<view class="page">
|
||||
|
||||
<!-- 顶部栏 -->
|
||||
<view class="topbar">
|
||||
<text class="topbar-title">我的</text>
|
||||
<view class="topbar-actions">
|
||||
<!-- <view class="settings-btn" bindtap="goToSettings">
|
||||
<text class="settings-icon">⚙️</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 未登录 -->
|
||||
<view class="user-card" wx:if="{{!hasLogin}}" bindtap="doLogin">
|
||||
<view class="avatar-ring">
|
||||
<view class="avatar-letter-wrap" style="background:{{avatarBgColor}}">
|
||||
<text class="avatar-letter">{{avatarLetter}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-meta">
|
||||
<text class="user-name" wx:if="{{loginLoading}}">登录中...</text>
|
||||
<text class="user-name" wx:elif="{{loginFailed}}">登录失败,点击重试</text>
|
||||
<text class="user-name" wx:else>点击登录</text>
|
||||
<text class="user-sub">登录后查看你的测试结果</text>
|
||||
</view>
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
|
||||
<!-- 已登录:用户卡片(点击进入个人资料) -->
|
||||
<view class="user-card" wx:if="{{hasLogin}}" bindtap="goToUserProfile">
|
||||
<!-- 头像区 -->
|
||||
<view class="avatar-wrap">
|
||||
<view class="avatar-ring">
|
||||
<image wx:if="{{userInfo && (userInfo.avatarUrl || userInfo.avatar)}}"
|
||||
class="avatar-img"
|
||||
src="{{userInfo.avatarUrl || userInfo.avatar}}"
|
||||
mode="aspectFill"/>
|
||||
<view wx:else class="avatar-letter-wrap" style="background:{{avatarBgColor}}">
|
||||
<text class="avatar-letter">{{avatarLetter}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="online-dot"></view>
|
||||
</view>
|
||||
|
||||
<!-- 昵称 + 标签 -->
|
||||
<view class="user-meta">
|
||||
<text class="nickname-text">{{nicknameDisplay || '点击设置昵称'}}</text>
|
||||
<!-- 类型标签 -->
|
||||
<scroll-view scroll-x class="tags-scroll">
|
||||
<view class="tags-row">
|
||||
<view class="tag tag-purple" wx:if="{{mbtiType}}">
|
||||
<text class="tag-text">{{mbtiType}}</text>
|
||||
</view>
|
||||
<view class="tag tag-blue" wx:if="{{discType}}">
|
||||
<text class="tag-text">{{discType}}型</text>
|
||||
</view>
|
||||
<view class="tag tag-orange" wx:if="{{pdpType}}">
|
||||
<text class="tag-text">{{pdpType}}</text>
|
||||
</view>
|
||||
<view class="tag tag-gray" wx:if="{{!mbtiType && !discType && !pdpType}}">
|
||||
<text class="tag-text">暂无测试记录</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<text class="chevron">›</text>
|
||||
</view>
|
||||
|
||||
<!-- 最新测试卡片(横向滚动) -->
|
||||
<view class="section" wx:if="{{hasLogin && hasResults}}">
|
||||
<view class="section-header">
|
||||
<text class="section-title">最新测试</text>
|
||||
<view class="section-link" bindtap="goToHistory">
|
||||
<text class="section-link-text">查看全部</text>
|
||||
</view>
|
||||
</view>
|
||||
<scroll-view scroll-x class="cards-scroll" enhanced show-scrollbar="{{false}}">
|
||||
<view class="cards-row">
|
||||
<!-- MBTI -->
|
||||
<view class="result-card card-purple" wx:if="{{mbtiType}}" bindtap="viewMBTI">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-purple">
|
||||
<text class="card-icon">🧠</text>
|
||||
</view>
|
||||
<text class="card-label">MBTI性格</text>
|
||||
<text class="card-value">{{mbtiType}}</text>
|
||||
<text class="card-time">{{mbtiTime}}</text>
|
||||
</view>
|
||||
<!-- DISC -->
|
||||
<view class="result-card card-blue" wx:if="{{discType}}" bindtap="viewDISC">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-blue">
|
||||
<text class="card-icon">📊</text>
|
||||
</view>
|
||||
<text class="card-label">DISC测评</text>
|
||||
<text class="card-value">{{discType}}型</text>
|
||||
<text class="card-time">{{discTime}}</text>
|
||||
</view>
|
||||
<!-- PDP -->
|
||||
<view class="result-card card-orange" wx:if="{{pdpType}}" bindtap="viewPDP">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-orange">
|
||||
<text class="card-icon">🦁</text>
|
||||
</view>
|
||||
<text class="card-label">PDP行为</text>
|
||||
<text class="card-value">{{pdpType}}</text>
|
||||
<text class="card-time">{{pdpTime}}</text>
|
||||
</view>
|
||||
<!-- AI 面相 -->
|
||||
<view class="result-card card-rose" wx:if="{{aiType}}" bindtap="viewAI">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-rose">
|
||||
<text class="card-icon">👁️</text>
|
||||
</view>
|
||||
<text class="card-label">面相分析</text>
|
||||
<text class="card-value">{{aiType}}</text>
|
||||
<text class="card-time">{{aiTime}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 推广中心(根据管理端开关显示/隐藏,标题可配置) -->
|
||||
<view class="section px-section" wx:if="{{hasLogin && promoDistributionEnabled}}">
|
||||
<view class="promo-card" bindtap="goToPromo">
|
||||
<view class="promo-header">
|
||||
<view class="promo-title-wrap">
|
||||
<text class="promo-icon">📈</text>
|
||||
<text class="promo-title">{{promoCenterTitle}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="promo-stats">
|
||||
<view class="promo-stat-item">
|
||||
<text class="promo-stat-label">邀请好友</text>
|
||||
<text class="promo-stat-value">{{promoTotalInvite}}</text>
|
||||
</view>
|
||||
<view class="promo-stat-divider"></view>
|
||||
<view class="promo-stat-item">
|
||||
<text class="promo-stat-label">累计收益</text>
|
||||
<text class="promo-stat-value">¥{{promoTotalEarned}}</text>
|
||||
</view>
|
||||
<view class="promo-stat-divider"></view>
|
||||
<view class="promo-stat-item">
|
||||
<text class="promo-stat-label">可提现</text>
|
||||
<text class="promo-stat-value promo-highlight">¥{{promoWithdrawable}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务菜单 -->
|
||||
<view class="section px-section">
|
||||
<text class="section-title">深度服务</text>
|
||||
<view class="menu-card">
|
||||
<!-- 专业报告:跳到个人版开通页面 -->
|
||||
<view class="menu-item" bindtap="goToPurchasePersonal">
|
||||
<view class="menu-icon-wrap menu-icon-red">
|
||||
<text class="menu-icon">📄</text>
|
||||
</view>
|
||||
<view class="menu-content">
|
||||
<text class="menu-title">专业报告</text>
|
||||
<text class="menu-sub">解锁完整的深度性格解析</text>
|
||||
</view>
|
||||
<text class="menu-chevron">›</text>
|
||||
</view>
|
||||
<view class="menu-divider"></view>
|
||||
|
||||
<!-- 企业版服务:跳到企业版开通页面 -->
|
||||
<view class="menu-item" bindtap="goToPurchaseEnterprise">
|
||||
<view class="menu-icon-wrap menu-icon-indigo">
|
||||
<text class="menu-icon">🏢</text>
|
||||
</view>
|
||||
<view class="menu-content">
|
||||
<text class="menu-title">企业版服务</text>
|
||||
<text class="menu-sub">团队测评与人才管理方案</text>
|
||||
</view>
|
||||
<text class="menu-chevron">›</text>
|
||||
</view>
|
||||
<view class="menu-divider"></view>
|
||||
|
||||
<!-- 测试历史:仍然进入历史记录页面 -->
|
||||
<view class="menu-item" bindtap="goToHistory">
|
||||
<view class="menu-icon-wrap menu-icon-amber">
|
||||
<text class="menu-icon">🕒</text>
|
||||
</view>
|
||||
<view class="menu-content">
|
||||
<text class="menu-title">测试历史</text>
|
||||
<text class="menu-sub">{{testCount > 0 ? testCount + '条记录' : '查看过往所有测试记录'}}</text>
|
||||
</view>
|
||||
<text class="menu-chevron">›</text>
|
||||
</view>
|
||||
<view class="menu-divider" wx:if="{{hasEnterprise}}"></view>
|
||||
<!-- 我的简历:仅绑定企业的用户可见,展示当前企业下的简历并可设默认 -->
|
||||
<view class="menu-item" wx:if="{{hasEnterprise}}" bindtap="goToMyResume">
|
||||
<view class="menu-icon-wrap menu-icon-indigo">
|
||||
<text class="menu-icon">📋</text>
|
||||
</view>
|
||||
<view class="menu-content">
|
||||
<text class="menu-title">我的简历</text>
|
||||
<text class="menu-sub">查看与设置默认简历</text>
|
||||
</view>
|
||||
<text class="menu-chevron">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
<view class="bottom-safe"></view>
|
||||
</view>
|
||||
534
miniprogram/pages/profile/index.wxss
Normal file
534
miniprogram/pages/profile/index.wxss
Normal file
@@ -0,0 +1,534 @@
|
||||
/* pages/profile/index.wxss */
|
||||
page {
|
||||
background-color: #F9FAFB;
|
||||
}
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background-color: #F9FAFB;
|
||||
padding-bottom: 140rpx;
|
||||
}
|
||||
|
||||
/* ===== 顶部栏 ===== */
|
||||
.topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16rpx 40rpx 24rpx;
|
||||
background: #F9FAFB;
|
||||
}
|
||||
|
||||
.topbar-title {
|
||||
font-size: 48rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
letter-spacing: -0.5rpx;
|
||||
}
|
||||
|
||||
.topbar-actions {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.settings-btn {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.settings-icon {
|
||||
font-size: 44rpx;
|
||||
color: #4B5563;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.icon-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.icon-text {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
/* ===== 用户卡片 ===== */
|
||||
.user-card {
|
||||
margin: 0 32rpx 28rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 28rpx;
|
||||
padding: 36rpx 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.05);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.user-card:active {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 头像区域(已登录,仅展示) */
|
||||
.avatar-wrap {
|
||||
position: relative;
|
||||
width: 144rpx;
|
||||
height: 144rpx;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.avatar-ring {
|
||||
width: 144rpx;
|
||||
height: 144rpx;
|
||||
border-radius: 50%;
|
||||
padding: 4rpx;
|
||||
background: linear-gradient(135deg, #FFDEE9 0%, #B5FFFC 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
box-shadow: inset 0 2rpx 10rpx rgba(0,0,0,0.05);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid #FFFFFF;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.avatar-letter-wrap {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 4rpx solid #FFFFFF;
|
||||
}
|
||||
|
||||
.avatar-letter {
|
||||
font-size: 64rpx;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.online-dot {
|
||||
position: absolute;
|
||||
bottom: 8rpx;
|
||||
right: 8rpx;
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
background: #22C55E;
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid #FFFFFF;
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 用户信息 */
|
||||
.user-meta {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
padding-left: 12rpx;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.user-sub {
|
||||
font-size: 24rpx;
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.nickname-text {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
line-height: 1.3;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 类型标签 */
|
||||
.tags-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tags-row {
|
||||
display: flex;
|
||||
gap: 10rpx;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 10rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tag-text {
|
||||
font-size: 20rpx;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tag-purple {
|
||||
background: #EDE9FE;
|
||||
}
|
||||
.tag-purple .tag-text {
|
||||
color: #7C3AED;
|
||||
}
|
||||
|
||||
.tag-blue {
|
||||
background: #DBEAFE;
|
||||
}
|
||||
.tag-blue .tag-text {
|
||||
color: #2563EB;
|
||||
}
|
||||
|
||||
.tag-orange {
|
||||
background: #FEF3C7;
|
||||
}
|
||||
.tag-orange .tag-text {
|
||||
color: #D97706;
|
||||
}
|
||||
|
||||
.tag-gray {
|
||||
background: #F3F4F6;
|
||||
}
|
||||
.tag-gray .tag-text {
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.chevron {
|
||||
font-size: 48rpx;
|
||||
color: #D1D5DB;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ===== Section ===== */
|
||||
.section {
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.px-section {
|
||||
padding: 0 32rpx;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 32rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #1F2937;
|
||||
display: block;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.section-header .section-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-link-text {
|
||||
font-size: 24rpx;
|
||||
color: #F43F5E;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ===== 测试结果卡片(横向滚动) ===== */
|
||||
.cards-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cards-row {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
padding: 8rpx 32rpx 16rpx;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
width: 280rpx;
|
||||
height: 240rpx;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx 28rpx 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
|
||||
border: 1rpx solid rgba(0,0,0,0.04);
|
||||
background: #FFFFFF;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-deco {
|
||||
position: absolute;
|
||||
top: -24rpx;
|
||||
right: -24rpx;
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.card-purple .card-deco { background: #DDD6FE; }
|
||||
.card-blue .card-deco { background: #BFDBFE; }
|
||||
.card-orange .card-deco { background: #FDE68A; }
|
||||
.card-rose .card-deco { background: #FECDD3; }
|
||||
|
||||
.card-icon-wrap {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card-icon-purple { background: #EDE9FE; }
|
||||
.card-icon-blue { background: #DBEAFE; }
|
||||
.card-icon-orange { background: #FEF3C7; }
|
||||
.card-icon-rose { background: #FFE4E6; }
|
||||
|
||||
.card-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
font-size: 22rpx;
|
||||
color: #9CA3AF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.card-value {
|
||||
font-size: 44rpx;
|
||||
font-weight: 800;
|
||||
letter-spacing: -1rpx;
|
||||
}
|
||||
|
||||
.card-purple .card-value { color: #7C3AED; }
|
||||
.card-blue .card-value { color: #2563EB; }
|
||||
.card-orange .card-value { color: #D97706; }
|
||||
.card-rose .card-value { color: #E11D48; }
|
||||
|
||||
.card-time {
|
||||
font-size: 20rpx;
|
||||
color: #D1D5DB;
|
||||
}
|
||||
|
||||
.card-time {
|
||||
font-size: 20rpx;
|
||||
color: #D1D5DB;
|
||||
}
|
||||
|
||||
/* ===== 推广中心 ===== */
|
||||
.promo-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
padding: 40rpx;
|
||||
box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.05);
|
||||
border: 1rpx solid rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.promo-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.promo-title-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.promo-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.promo-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #1F2937;
|
||||
}
|
||||
|
||||
.promo-stats {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.promo-stat-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.promo-stat-label {
|
||||
font-size: 24rpx;
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.promo-stat-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
font-family: "DIN Alternate", "Courier New", Courier, monospace;
|
||||
}
|
||||
|
||||
.promo-stat-divider {
|
||||
width: 1rpx;
|
||||
height: 60rpx;
|
||||
background: #F3F4F6;
|
||||
}
|
||||
|
||||
.promo-highlight {
|
||||
color: #F43F5E;
|
||||
}
|
||||
|
||||
/* ===== 服务菜单 ===== */
|
||||
.menu-card {
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.05);
|
||||
border: 1rpx solid rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 32rpx 36rpx;
|
||||
gap: 28rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu-item:active {
|
||||
background: #F9FAFB;
|
||||
}
|
||||
|
||||
.menu-divider {
|
||||
height: 1rpx;
|
||||
background: #F3F4F6;
|
||||
margin: 0 36rpx;
|
||||
}
|
||||
|
||||
.menu-icon-wrap {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.menu-icon-red { background: #FEF2F2; }
|
||||
.menu-icon-indigo { background: #EEF2FF; }
|
||||
.menu-icon-amber { background: #FFFBEB; }
|
||||
.menu-icon-purple { background: #EDE9FE; }
|
||||
|
||||
.menu-icon {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.menu-sub {
|
||||
font-size: 24rpx;
|
||||
color: #9CA3AF;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.menu-chevron {
|
||||
font-size: 48rpx;
|
||||
color: #D1D5DB;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ===== 退出登录按钮 ===== */
|
||||
.logout-btn {
|
||||
width: 100%;
|
||||
padding: 32rpx;
|
||||
background: #FFF1F2;
|
||||
border: 1rpx solid #FFE4E6;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.logout-btn:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.logout-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.logout-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #EF4444;
|
||||
}
|
||||
|
||||
.bottom-safe {
|
||||
height: 40rpx;
|
||||
}
|
||||
Reference in New Issue
Block a user