592 lines
17 KiB
Vue
592 lines
17 KiB
Vue
<template>
|
||
<view class="main_div max_page">
|
||
<nav-bar :is_seat="false"></nav-bar>
|
||
<view class="bg_div">
|
||
<image src="/static/images/me/bg.png" class="bg_img"></image>
|
||
<view class="user_data_div">
|
||
<!-- 用户个人信息 -->
|
||
<view class="personal_information">
|
||
<view class="profile" @click="click_update_avatar()">
|
||
<image class="img_100" v-if="user_info.imagePath" :src="user_info.imagePath"></image>
|
||
<image-preview
|
||
v-else-if="user_info.imageAddr"
|
||
class="img_100"
|
||
:src="user_info.imageAddr"
|
||
></image-preview>
|
||
<image class="img_100" :src="defaultAvatar"></image>
|
||
</view>
|
||
<view class="personal_information_data">
|
||
<view class="name_sex_div">
|
||
<view class="name" @click="gotoTestPage">
|
||
{{ user_info.userName }}
|
||
</view>
|
||
<view class="sex">
|
||
<image
|
||
v-if="user_info.gender === 'f'"
|
||
class="img_100"
|
||
src="/static/images/me/wuman.png"
|
||
></image>
|
||
<image
|
||
v-if="user_info.gender === 'm'"
|
||
class="img_100"
|
||
src="/static/images/me/man.png"
|
||
></image>
|
||
</view>
|
||
<!-- 显示当前选中的徽章 -->
|
||
<view class="dan" @click="common.navigateTo('/pages/pointsAndRank/rank')">
|
||
<view class="dan_img">
|
||
<image-preview
|
||
v-if="statistics_data['currentRankAddr']"
|
||
class="img_100"
|
||
:src="statistics_data['currentRankAddr']"
|
||
mode="heightFix"
|
||
alt="当前徽章"
|
||
></image-preview>
|
||
<image v-else src="@/static/images/me/default_rank.png" class="img_100" mode="heightFix"/>
|
||
</view>
|
||
</view>
|
||
<view class="fl1"></view>
|
||
</view>
|
||
<view class="work_number_div">工号:{{ user_info.loginName }}</view>
|
||
<view class="fl1"></view>
|
||
</view>
|
||
<view class="sign-in-div" @click="checkInClick" v-if="user_info.checkInToday === 2">签到</view>
|
||
<view class="already-sign-in-div" v-if="user_info.checkInToday === 1" @click="checkInClick">已签</view>
|
||
</view>
|
||
<!-- 用户学习时间信息 -->
|
||
<view class="study_information">
|
||
<view class="item" @click="common.navigateTo('/pages/pointsAndRank/points')">
|
||
<view class="top">我的积分</view>
|
||
<view class="bottom">
|
||
<uv-count-to
|
||
:startVal="statistics_data['startCurrentPoints']"
|
||
:endVal="statistics_data['currentPoints']"
|
||
:useEasing="true"
|
||
ref="currentPointsRef"
|
||
></uv-count-to>
|
||
</view>
|
||
</view>
|
||
<view class="item-interval"></view>
|
||
<view class="item" @click="common.navigateTo('/pages/pointsAndRank/badge')">
|
||
<view class="top">徽章墙</view>
|
||
<view class="bottom">
|
||
<uv-count-to
|
||
:startVal="statistics_data['startBadgeNum']"
|
||
:endVal="statistics_data['badgeNum']"
|
||
:useEasing="true"
|
||
ref="badgeNumRef"
|
||
></uv-count-to>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 4个导航 -->
|
||
<view class="navigation_div">
|
||
<!-- <uv-scroll-list class="scroll-view"> -->
|
||
<!-- <scroll-view scroll-x="true" :show-scrollbar="false"> -->
|
||
<view class="scroll-view-item">
|
||
<view class="item" @click="common.navigateTo('/pages/courseRecord/index')">
|
||
<image class="icon" src="@/static/images/me/study.png"></image>
|
||
<view class="title">课程记录</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="scroll-view-item" @click="common.navigateTo('/pages/examinationRecord/index')">
|
||
<view class="item">
|
||
<image class="icon" src="@/static/images/me/todo.png"></image>
|
||
<view class="title">考试记录</view>
|
||
</view>
|
||
</view>
|
||
<view class="scroll-view-item">
|
||
<view class="item" @click="common.navigateTo('/pages/traRecord/index')">
|
||
<image class="icon" src="@/static/images/me/report.png"></image>
|
||
<view class="title">AI陪练记录</view>
|
||
</view>
|
||
</view>
|
||
<view class="scroll-view-item">
|
||
<view class="item" @click="common.navigateTo('/pages/competitionRecord/index')">
|
||
<image class="icon" src="@/static/images/me/examination.png"></image>
|
||
<view class="title">竞赛记录</view>
|
||
</view>
|
||
</view>
|
||
<!-- </uv-scroll-list> -->
|
||
</view>
|
||
<view class="cell_div">
|
||
<uv-cell-group :border="false">
|
||
<uv-cell
|
||
title="问答演示"
|
||
v-if="user_info.loginName === 'ADMIN'"
|
||
:isLink="true"
|
||
@click="common.navigateTo('/pages/example/dialog')"
|
||
></uv-cell>
|
||
<uv-cell
|
||
title="消息通知"
|
||
:isLink="true"
|
||
@click="common.navigateTo('/pages/messageNotification/index')"
|
||
></uv-cell>
|
||
<uv-cell title="我的收藏" :isLink="true" @click="common.navigateTo('/pages/favorites/index')"></uv-cell>
|
||
<uv-cell title="排行榜" :isLink="true" @click="common.navigateTo('/pages/charts/index')"></uv-cell>
|
||
<uv-cell
|
||
title="我的预览"
|
||
:isLink="true"
|
||
:clickable="false"
|
||
:border="false"
|
||
@click="common.navigateTo('/pages/preview/index')"
|
||
></uv-cell>
|
||
</uv-cell-group>
|
||
</view>
|
||
<view class="cell_div">
|
||
<uv-cell-group :border="false">
|
||
<uv-cell title="设置" :isLink="true" @click="common.navigateTo('/pages/setting/index')"></uv-cell>
|
||
<uv-cell
|
||
title="退出登录"
|
||
:isLink="true"
|
||
:clickable="false"
|
||
:border="false"
|
||
@click="click_loginout()"
|
||
></uv-cell>
|
||
</uv-cell-group>
|
||
</view>
|
||
<view style="height: 86rpx"></view>
|
||
<tabbar-shadow></tabbar-shadow>
|
||
<avatar-cropper
|
||
v-if="avatarCropperStatus"
|
||
mode="ratio"
|
||
:imageUrl="avatarCropperUrl"
|
||
:width="500"
|
||
:height="500"
|
||
:delay="150"
|
||
@cancel="avatarOnCancel"
|
||
@confirm="avatarOnConfirm"
|
||
></avatar-cropper>
|
||
<checkIn ref="checkInRef"></checkIn>
|
||
<badge ref="badgeRef"></badge>
|
||
<points ref="pointsRef"></points>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, reactive, nextTick } from 'vue';
|
||
import { onLoad, onShow, onHide, onUnload } from '@dcloudio/uni-app';
|
||
import common from '@/common/common';
|
||
import { getUserInfo } from '@/common/common';
|
||
import { onPullDownRefresh } from '@dcloudio/uni-app';
|
||
import { loginOut } from '@/api/login.js';
|
||
import { queryTraStdyStatisticsSelf } from '@/api/user.js';
|
||
import { useSocketStore } from '@/store/socket.js';
|
||
import { updateDfSysUserExtandInfoImageAddr } from '@/api/user.js';
|
||
import { uploadFile } from '@/api/common';
|
||
import { setMascot } from '@/common/mascot.js';
|
||
import { queryCurrentUser } from '@/api/login.js';
|
||
import { defaultAvatar } from '@/enum.js';
|
||
import useUpdateAvatar from '@/composables/useUpdateAvatar';
|
||
import { queryCurrentStatus, queryCheckIn } from '@/api/pointsAndRank.js';
|
||
import checkIn from '@/components/points-and-badge/check-in';
|
||
import badge from '@/components/points-and-badge/badge';
|
||
import points from '@/components/points-and-badge/points';
|
||
import useCheckIn from '@/composables/useCheckIn';
|
||
import usePointsAndBadge from '@/components/points-and-badge/usePointsAndBadge';
|
||
const { handlePointsBadgePopup, badgeRef, pointsRef } = usePointsAndBadge();
|
||
const { checkInRef, checkInFun, initCheckInStatus } = useCheckIn();
|
||
const { avatarCropperUrl, click_update_avatar, avatarCropperStatus, avatarOnCancel, avatarOnConfirm } = useUpdateAvatar({
|
||
success: (userData) => {
|
||
user_info.imageAddr = userData.imageAddr;
|
||
user_info.imagePath = '';
|
||
}
|
||
});
|
||
const currentIndex = ref(0);
|
||
const socketStore = useSocketStore();
|
||
const badgeNumRef = ref(null);
|
||
const currentPointsRef = ref(null);
|
||
const gotoTestPage = () => {
|
||
common.navigateTo('/pages/test/index');
|
||
};
|
||
const user_info = reactive({
|
||
userName: '',
|
||
userId: '',
|
||
imageAddr: '',
|
||
loginName: '',
|
||
checkInToday: 0, // 今日是否签到 0未知, 1签到,2未签到
|
||
checkInDays: 0
|
||
});
|
||
// 签到打开
|
||
const checkInClick = () => {
|
||
checkInRef.value.open(
|
||
user_info.checkInToday === 1,
|
||
user_info.checkInDays,
|
||
checkInFun,
|
||
(status, days, pointsList = [], badgesList = []) => {
|
||
user_info.checkInToday = status ? 1 : 2;
|
||
user_info.checkInDays = days;
|
||
statistics_data['get_statistics_data'](true); // 强制获取统计数据
|
||
|
||
handlePointsBadgePopup(pointsList, badgesList);
|
||
}
|
||
);
|
||
};
|
||
|
||
const click_loginout = () => {
|
||
common.show('确定退出登录?').then(async (res) => {
|
||
if (!res) return;
|
||
try {
|
||
common.loading('正在退出登录');
|
||
socketStore.closeSocket();
|
||
await loginOut();
|
||
} catch (e) {}
|
||
common.hideLoading();
|
||
// 主动退出登录后,再次登录应该进首页
|
||
common.reLaunch('/pages/login/login');
|
||
});
|
||
};
|
||
|
||
const statistics_data = reactive({
|
||
badgeNum: 0, // 徽章数
|
||
currentPoints: 0, // 当前积分
|
||
currentRankAddr: '', // 当前段位名称
|
||
currentRankName: '', // 当前段位名称
|
||
startBadgeNum: 0, // 起始 徽章数
|
||
startCurrentPoints: 0, // 起始 积分
|
||
request_time: 0, // 上次请求时间
|
||
init: function () {
|
||
// 初始化统计数据
|
||
const points_data = common.getValue('points_data');
|
||
if (points_data) {
|
||
this.badgeNum = points_data.badgeNum; // 徽章数
|
||
this.currentPoints = points_data.currentPoints; // 当前积分
|
||
this.currentRankAddr = points_data.currentRankAddr;
|
||
this.currentRankName = points_data.currentRankName;
|
||
}
|
||
},
|
||
get_statistics_data: async function (status = false) {
|
||
try {
|
||
const time_now = Date.now();
|
||
let points_data = common.getValue('points_data') || {};
|
||
if (time_now - this.request_time > 300 || status) {
|
||
this.request_time = time_now;
|
||
// 获取最新数据
|
||
const res = await queryCurrentStatus();
|
||
if (res && res.body) {
|
||
// 合并新数据与缓存数据,确保只更新有效字段
|
||
points_data = {
|
||
badgeNum: res.body['badgeNum'], // 徽章数
|
||
currentPoints: res.body['currentPoints'], // 当前积分
|
||
currentRankAddr: res.body['ossAddr'], // 当前段位图片
|
||
currentRankName: res.body['currentRankName'] // 当前段位名称
|
||
};
|
||
common.setValue('points_data', points_data);
|
||
this.currentRankAddr = points_data.currentRankAddr;
|
||
this.currentRankName = points_data.currentRankName;
|
||
}
|
||
}
|
||
// 解构赋值获取最新值
|
||
const { badgeNum = 0, currentPoints = 0, currentRankAddr = '' } = points_data;
|
||
this.currentRankAddr = currentRankAddr; // 赋值段位变化
|
||
// 更新数据并触发动画
|
||
if (badgeNum !== this.badgeNum) {
|
||
this.startBadgeNum = this.badgeNum;
|
||
this.badgeNum = badgeNum;
|
||
badgeNumRef.value.start();
|
||
}
|
||
if (currentPoints !== this.currentPoints) {
|
||
this.startCurrentPoints = this.currentPoints;
|
||
this.currentPoints = currentPoints;
|
||
currentPointsRef.value.start();
|
||
}
|
||
} catch (error) {
|
||
console.error('获取统计数据失败:', error);
|
||
}
|
||
}
|
||
});
|
||
|
||
onLoad(() => {
|
||
statistics_data['init'](); // 初始化数据
|
||
uni.$on('update_me_data', (status) => {
|
||
// 获取了徽章或者积分,把缓存时间重置,以便下次进入me页实时获取
|
||
console.log('获取了徽章或者积分,把缓存时间重置,以便下次进入me页实时获取');
|
||
if (status) {
|
||
statistics_data.request_time = 0;
|
||
}
|
||
});
|
||
});
|
||
onShow(() => {
|
||
setMascot();
|
||
Object.assign(user_info, getUserInfo());
|
||
statistics_data['get_statistics_data'](); // 获取统计数据
|
||
// 获取今日是否签到消息
|
||
initCheckInStatus(({isSigned, checkInDays, popUpStatus}) => {
|
||
user_info.checkInToday = isSigned ? 1 : 2;
|
||
user_info.checkInDays = checkInDays;
|
||
});
|
||
|
||
|
||
|
||
});
|
||
onHide(() => {
|
||
checkInRef.value && checkInRef.value.close();
|
||
});
|
||
onPullDownRefresh(() => {
|
||
uni.stopPullDownRefresh();
|
||
});
|
||
|
||
onUnload(() => {
|
||
uni.$off('update_me_data');
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
$bg-margin-left: 50rpx;
|
||
|
||
// 下方单元格
|
||
.cell_div {
|
||
margin: 30rpx $bg-margin-left 0 $bg-margin-left;
|
||
background: #ffffff;
|
||
border-radius: 15rpx;
|
||
z-index: 90;
|
||
overflow: hidden;
|
||
|
||
// 为了让线缩进来24单位
|
||
:deep(.uv-line) {
|
||
width: calc(100% - 48rpx) !important;
|
||
margin: 0 24rpx !important;
|
||
}
|
||
|
||
:deep(.uv-cell__body) {
|
||
padding: 12px 15px;
|
||
}
|
||
}
|
||
|
||
// 快捷导航栏
|
||
.navigation_div {
|
||
margin: 30rpx $bg-margin-left 0 $bg-margin-left;
|
||
padding: 24rpx 10rpx;
|
||
background-color: #ffffff;
|
||
border-radius: 23rpx;
|
||
display: flex;
|
||
justify-content: space-around;
|
||
z-index: 90;
|
||
.scroll-view {
|
||
white-space: nowrap;
|
||
width: 100%;
|
||
}
|
||
|
||
.scroll-view-item {
|
||
display: inline-block;
|
||
padding: 0 22rpx 0 26rpx;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
// background-color: red;
|
||
.icon {
|
||
width: 88rpx;
|
||
height: 86rpx;
|
||
}
|
||
|
||
.title {
|
||
margin-top: 10rpx;
|
||
font-family: PingFangSC, PingFang SC;
|
||
font-weight: 400;
|
||
font-size: 25rpx;
|
||
color: #323e57;
|
||
line-height: 35rpx;
|
||
text-align: left;
|
||
font-style: normal;
|
||
}
|
||
}
|
||
}
|
||
|
||
.bg_div {
|
||
display: flex;
|
||
flex-direction: column;
|
||
position: relative;
|
||
padding: 0 $bg-margin-left;
|
||
z-index: 50;
|
||
|
||
.bg_img {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
z-index: -1;
|
||
height: 858rpx;
|
||
}
|
||
|
||
// 个人信息
|
||
.user_data_div {
|
||
margin-top: 158rpx;
|
||
background: linear-gradient(180deg, rgba(241, 245, 255, 0.5) 0%, #f6f9fe 50%, #ffffff 100%);
|
||
border-radius: 14rpx;
|
||
border: 2rpx solid #ffffff;
|
||
box-shadow: #f6f8ff 0 0 2rpx;
|
||
|
||
.personal_information {
|
||
padding: 20rpx 26rpx 0 26rpx;
|
||
display: flex;
|
||
|
||
// 个人头像框框
|
||
.profile {
|
||
border-radius: 100%;
|
||
border: 2px solid #ffffff;
|
||
width: 108rpx;
|
||
height: 108rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
// 头像右边的信息
|
||
.personal_information_data {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin-left: 26rpx;
|
||
overflow: hidden;
|
||
|
||
// 上边的姓名性别盒子
|
||
.name_sex_div {
|
||
display: flex;
|
||
margin-top: 6rpx;
|
||
justify-content: space-between;
|
||
|
||
.name {
|
||
font-family: PingFangSC;
|
||
font-weight: 600;
|
||
font-size: 28rpx;
|
||
color: #000000;
|
||
line-height: 40rpx;
|
||
}
|
||
|
||
.sex {
|
||
margin-left: 10rpx;
|
||
width: 30rpx;
|
||
height: 30rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
.dan {
|
||
font-size: 16rpx;
|
||
padding-left: 20rpx;
|
||
.dan_img {
|
||
height: 44rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.work_number_div {
|
||
font-family: PingFangSC;
|
||
font-weight: 400;
|
||
font-size: 25rpx;
|
||
color: #666666;
|
||
line-height: 38rpx;
|
||
text-align: left;
|
||
font-style: normal;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
}
|
||
}
|
||
.sign-in-div {
|
||
margin-top: 10rpx;
|
||
width: 102rpx;
|
||
height: 48rpx;
|
||
font-weight: 500;
|
||
font-size: 26rpx;
|
||
line-height: 46rpx;
|
||
text-align: center;
|
||
color: #ffffff;
|
||
background-image: url('@/static/images/me/sign-in-button-bg.png');
|
||
background-size: 100%; /* 图片自适应元素大小(避免拉伸变形) */
|
||
background-repeat: no-repeat; /* 禁止重复平铺 */
|
||
background-position: center; /* 图片居中显示 */
|
||
}
|
||
.already-sign-in-div {
|
||
margin-top: 10rpx;
|
||
width: 102rpx;
|
||
height: 48rpx;
|
||
font-weight: 500;
|
||
font-size: 26rpx;
|
||
line-height: 46rpx;
|
||
text-align: center;
|
||
color: #ffffff;
|
||
background-image: url('@/static/images/me/sign-in-button-bg2.png');
|
||
background-size: 100%; /* 图片自适应元素大小(避免拉伸变形) */
|
||
background-repeat: no-repeat; /* 禁止重复平铺 */
|
||
background-position: center; /* 图片居中显示 */
|
||
}
|
||
}
|
||
|
||
.study_information {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
z-index: 20;
|
||
margin: 30rpx 50rpx;
|
||
|
||
.item-interval {
|
||
position: relative;
|
||
&:before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 20%;
|
||
transform: translateX(-50%);
|
||
width: 1rpx;
|
||
height: 60%;
|
||
width: 4rpx;
|
||
height: 54rpx;
|
||
background: linear-gradient(
|
||
to bottom,
|
||
rgba(239, 246, 255, 0) 0%,
|
||
rgba(229, 239, 255, 1) 30%,
|
||
rgba(229, 239, 255, 1) 70%,
|
||
rgba(239, 246, 255, 0) 100%
|
||
);
|
||
}
|
||
}
|
||
|
||
.item {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
|
||
.top {
|
||
font-family: PingFangSC, PingFang SC;
|
||
font-weight: 400;
|
||
font-size: 28rpx;
|
||
color: #666666;
|
||
line-height: 35rpx;
|
||
text-align: center;
|
||
font-style: normal;
|
||
}
|
||
|
||
.bottom {
|
||
margin-top: 5%;
|
||
font-weight: bold;
|
||
font-size: 50rpx;
|
||
color: #000000;
|
||
line-height: 58rpx;
|
||
text-align: left;
|
||
font-style: normal;
|
||
text-align: center;
|
||
|
||
// 数字滚动组件
|
||
:deep(.uv-count-num) {
|
||
font-weight: bold !important;
|
||
font-size: 50rpx !important;
|
||
color: #000000 !important;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.main_div {
|
||
display: flex;
|
||
flex-direction: column;
|
||
background-color: #f6f8ff;
|
||
}
|
||
</style>
|