fix: login state refresh, hero layout, default avatar consistency
This commit is contained in:
@@ -24,8 +24,8 @@
|
||||
|
||||
### Step 4: 完整测试
|
||||
```bash
|
||||
# 构建检查
|
||||
cd /root/opencode-workspace/zhiyin/backend && npx nest build 2>&1
|
||||
# 构建检查(注意内存限制,服务器 OOM 时加 --max-old-space-size)
|
||||
cd /root/opencode-workspace/zhiyin/backend && NODE_OPTIONS="--max-old-space-size=2048" npx nest build 2>&1
|
||||
|
||||
# 单元测试
|
||||
npm test -- --forceExit --detectOpenHandles 2>&1
|
||||
@@ -43,14 +43,14 @@ npm test -- --forceExit --detectOpenHandles 2>&1
|
||||
cd /root/opencode-workspace/zhiyin/backend && npx nest build
|
||||
|
||||
# 同步到生产目录
|
||||
cp -rf dist/* /www/wwwroot/server/yzr-yhl/backend/dist/
|
||||
cp -rf dist/* /www/wwwroot/server/zhiyin/backend/dist/
|
||||
|
||||
# 复制证书(postbuild 替代)
|
||||
cp -r certs /www/wwwroot/server/yzr-yhl/backend/dist/src/certs
|
||||
cp -r certs /www/wwwroot/server/zhiyin/backend/dist/src/certs
|
||||
|
||||
# 重启
|
||||
pm2 restart yhl-backend
|
||||
|
||||
# 验证
|
||||
sleep 3 && curl -s http://localhost:3002/api/share/visit/test?visitorId=v
|
||||
sleep 3 && curl -s http://localhost:3006/api/user/wx-login -X POST -H "Content-Type: application/json" -d '{"code":"test"}'
|
||||
```
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<template>
|
||||
<view class="page fade-in">
|
||||
<view class="hero">
|
||||
<view class="hero-row">
|
||||
<view class="hero-left">
|
||||
<text class="hero-title">{{ greeting }}</text>
|
||||
<text class="hero-sub">试试下面的功能,开启你的求职练习</text>
|
||||
|
||||
</view>
|
||||
<view class="hero-right">
|
||||
<view class="user-card card" v-if="userInfo" @click="goProfile">
|
||||
<image class="avatar" :src="userInfo.avatar || '/static/avatar-default.svg'" mode="aspectFill" />
|
||||
<image class="avatar" :src="userInfo.avatar || '/static/avatar-default.png'" mode="aspectFill" />
|
||||
<view class="user-meta">
|
||||
<text class="user-name">{{ userInfo.nickname || '同学' }}</text>
|
||||
<view class="user-tags">
|
||||
@@ -15,6 +18,16 @@
|
||||
</view>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
<view class="guest-card card" v-else @click="goLogin">
|
||||
<image class="avatar" src="/static/avatar-default.png" mode="aspectFill" />
|
||||
<view class="user-meta">
|
||||
<text class="user-name">立即登录</text>
|
||||
<text class="guest-hint">登录后体验全部功能</text>
|
||||
</view>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 功能入口 -->
|
||||
@@ -129,6 +142,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { api } from '../../config'
|
||||
|
||||
const userInfo = ref(null)
|
||||
@@ -139,8 +153,12 @@ const positionsLoading = ref(true)
|
||||
const dailyQuestion = ref(null)
|
||||
const showAnswer = ref(false)
|
||||
|
||||
const loadUserInfo = () => {
|
||||
try { const s = uni.getStorageSync('userInfo'); if (s) userInfo.value = JSON.parse(s); else userInfo.value = null } catch (e) { userInfo.value = null }
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
try { const s = uni.getStorageSync('userInfo'); if (s) userInfo.value = JSON.parse(s) } catch (e) {}
|
||||
loadUserInfo()
|
||||
const h = new Date().getHours()
|
||||
if (h < 6) greeting.value = '夜深了,早点休息 🌙'
|
||||
else if (h < 12) greeting.value = '早上好 ☀️'
|
||||
@@ -170,9 +188,12 @@ onMounted(async () => {
|
||||
finally { positionsLoading.value = false }
|
||||
})
|
||||
|
||||
onShow(loadUserInfo)
|
||||
|
||||
const refreshDaily = () => { showAnswer.value = false; /* trigger reload */ }
|
||||
|
||||
const goProfile = () => uni.switchTab({ url: '/pages/user/user' })
|
||||
const goLogin = () => uni.navigateTo({ url: '/pages/login/login' })
|
||||
const goInterview = () => uni.navigateTo({ url: '/pages/interview/interview' })
|
||||
const goResume = () => uni.navigateTo({ url: '/pages/resume/resume' })
|
||||
const goProgress = () => uni.navigateTo({ url: '/pages/progress/progress' })
|
||||
@@ -190,16 +211,24 @@ const startInterview = (pos) => uni.navigateTo({ url: `/pages/interview/intervie
|
||||
background: linear-gradient(135deg, var(--color-gradient-start) 0%, var(--color-gradient-mid) 50%, var(--color-gradient-end) 100%);
|
||||
padding: 48rpx 32rpx 72rpx; border-radius: 0 0 48rpx 48rpx;
|
||||
}
|
||||
.hero-row { display: flex; align-items: flex-start; gap: 24rpx; }
|
||||
.hero-left { flex: 1; min-width: 0; padding-top: 8rpx; }
|
||||
.hero-right { flex-shrink: 0; width: 320rpx; }
|
||||
.hero-title { font-size: 40rpx; font-weight: 700; color: #FFF; display: block; line-height: 1.3; }
|
||||
.hero-sub { font-size: 22rpx; color: rgba(255,255,255,0.7); margin-top: 8rpx; display: block; }
|
||||
|
||||
.user-card {
|
||||
.user-card, .guest-card {
|
||||
background: rgba(255,255,255,0.95); backdrop-filter: blur(20rpx);
|
||||
border-radius: var(--radius-xl); padding: 24rpx 28rpx;
|
||||
display: flex; align-items: center; margin-top: 24rpx;
|
||||
border-radius: var(--radius-xl); padding: 20rpx 24rpx;
|
||||
display: flex; align-items: center;
|
||||
box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.1);
|
||||
}
|
||||
.avatar { width: 88rpx; height: 88rpx; border-radius: 50%; margin-right: 20rpx; border: 3rpx solid var(--color-primary-light); flex-shrink: 0; }
|
||||
.guest-card { background: rgba(255,255,255,0.15); backdrop-filter: blur(10rpx); }
|
||||
.guest-card .avatar { border-color: rgba(255,255,255,0.3); }
|
||||
.guest-card .user-name { font-size: 26rpx; color: #FFF; }
|
||||
.guest-hint { font-size: 20rpx; color: rgba(255,255,255,0.6); margin-top: 4rpx; display: block; }
|
||||
.guest-card .arrow { color: rgba(255,255,255,0.4); }
|
||||
.avatar { width: 72rpx; height: 72rpx; border-radius: 50%; margin-right: 16rpx; border: 3rpx solid var(--color-primary-light); flex-shrink: 0; }
|
||||
.user-meta { flex: 1; min-width: 0; }
|
||||
.user-name { font-size: 30rpx; font-weight: 600; color: var(--color-text); }
|
||||
.user-tags { display: flex; gap: 10rpx; margin-top: 10rpx; }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- 个人中心 -->
|
||||
<view class="header" v-if="isLoggedIn">
|
||||
<view class="profile-section">
|
||||
<image class="avatar" :src="userInfo.avatar || '/static/avatar-default.svg'" mode="aspectFill" />
|
||||
<image class="avatar" :src="userInfo.avatar || '/static/avatar-default.png'" mode="aspectFill" />
|
||||
<view class="profile-info">
|
||||
<text class="nickname">{{ userInfo.nickname || '未设置昵称' }}</text>
|
||||
<view class="plan-badge">{{ userInfo.plan || '免费版' }}</view>
|
||||
@@ -33,7 +33,6 @@
|
||||
<view class="guest-avatar"><text class="guest-icon">👤</text></view>
|
||||
<view class="guest-info">
|
||||
<text class="guest-name">未登录 / 点击登录</text>
|
||||
<text class="guest-hint">登录后体验全部功能</text>
|
||||
</view>
|
||||
<text class="header-arrow">›</text>
|
||||
</view>
|
||||
@@ -84,6 +83,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { api } from '../../config'
|
||||
|
||||
const userInfo = ref({})
|
||||
@@ -93,13 +93,16 @@ const token = ref('')
|
||||
|
||||
const isLoggedIn = computed(() => !!token.value)
|
||||
|
||||
onMounted(() => {
|
||||
const refreshState = () => {
|
||||
token.value = uni.getStorageSync('token') || ''
|
||||
if (!token.value) return
|
||||
try { const s = uni.getStorageSync('userInfo'); if (s) userInfo.value = JSON.parse(s) } catch(e) {}
|
||||
loadStats()
|
||||
checkAdmin()
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(refreshState)
|
||||
onShow(refreshState)
|
||||
|
||||
const loadStats = async () => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user