Files
tra-app/src/pages/me/index.vue
T
2025-08-06 22:10:55 +08:00

600 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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 v-else class="img_100" src="/static/images/me/default_user_avatar.png"></image>
</view>
<view class="personal_information_data">
<view class="name_sex_div">
<view class="name">
{{ 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="fl1"></view>
</view>
<view class="work_number_div">工号{{ user_info.loginName }}</view>
<view class="fl1"></view>
</view>
<!-- 显示当前选中的徽章 -->
<view class="dan" @click="changeBadge">
<view class="dan_img">
<image class="img_100" :src="`/static/images/me/${badgeList[currentIndex].png}`"
mode="heightFix" alt="当前徽章"></image>
</view>
<view class="dan_text">{{ badgeList[currentIndex].name }}</view>
</view>
</view>
<!-- 用户学习时间信息 -->
<view class="study_information">
<view class="item">
<view class="top">我的积分</view>
<view class="bottom">
<uv-count-to :startVal="statistics_data['startAccmTaskCnt']"
:endVal="statistics_data['accmTaskCnt']" :useEasing="true"
ref="accmTaskCntRef"></uv-count-to>
</view>
</view>
<view class="item-interval">
</view>
<view class="item">
<view class="top">徽章墙</view>
<view class="bottom">
<uv-count-to :startVal="statistics_data['startAccmTaskCnt']"
:endVal="statistics_data['accmTaskCnt']" :useEasing="true"
ref="accmTaskCntRef"></uv-count-to>
</view>
</view>
</view>
</view>
</view>
<!-- 4个导航 -->
<view class="navigation_div">
<scroll-view class="scroll-view" scroll-x="true" :show-scrollbar="false">
<view class="scroll-view-item">
<view class="item">
<image class="icon" src="@/static/images/me/study.png"></image>
<view class="title">学习记录</view>
</view>
</view>
<view class="scroll-view-item">
<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">
<image class="icon" src="@/static/images/me/examination.png"></image>
<view class="title">考试记录</view>
</view>
</view>
<view class="scroll-view-item">
<view class="item">
<image class="icon" src="@/static/images/me/report.png"></image>
<view class="title">竞赛记录</view>
</view>
</view>
<view class="scroll-view-item">
<view class="item">
<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">
<image class="icon" src="@/static/images/me/report.png"></image>
<view class="title">AI问答记录</view>
</view>
</view>
</scroll-view>
<!-- </scroll-view> -->
</view>
<view class="cell_div">
<uv-cell-group :border="false">
<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" :clickable="false" :border="false"
@click="common.navigateTo('/pages/me/preview')"></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>
<tabbar-shadow></tabbar-shadow>
<avatar-cropper v-if="avatarCropperStatus" mode="ratio" :imageUrl="avatarCropperUrl" :width="500" :height="500"
:delay="150" @cancel="onCancel" @confirm="onConfirm"></avatar-cropper>
</view>
</template>
<script setup>
import {
ref,
computed,
reactive,
nextTick
} from 'vue';
import {
onLoad,
onShow
} 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';
const badgeList = [{
"name": "黑铁",
"png": "badge_1.png",
},
{
"name": "青铜",
"png": "badge_2.png",
},
{
"name": "白银",
"png": "badge_3.png",
},
{
"name": "黄金",
"png": "badge_4.png",
},
{
"name": "铂金",
"png": "badge_5.png",
},
{
"name": "钻石",
"png": "badge_6.png",
},
{
"name": "王者",
"png": "badge_7.png",
}
]
const currentIndex = ref(0);
const changeBadge = () => {
// 计算下一个索引,如果当前是最后一个则回到0
currentIndex.value = (currentIndex.value + 1) % badgeList.length;
};
const socketStore = useSocketStore()
const accmTaskCntRef = ref(null);
const accmPointRef = ref(null);
const stdtTmLenRef = ref(null);
const statistics_data = reactive({
stdtTmLen: 0, // 累计学习时长
accmTaskCnt: 0, // 累计完成任务
accmPoint: 0, // 累计获得积分
startStdtTmLen: 0, // 起始累计学习时长
startAccmTaskCnt: 0, // 起始累计完成任务
startAccmPoint: 0, // 起始累计获得积分
request_time: 0, // 上次请求时间
init: function() {
// 初始化统计数据
console.log('初始化统计数据');
const me_statistics_data = common.getValue('statistics_data'); // 获取缓存
if (me_statistics_data) {
this.stdtTmLen = me_statistics_data.stdtTmLen;
this.accmTaskCnt = me_statistics_data.accmTaskCnt;
this.accmPoint = me_statistics_data.accmPoint;
}
},
get_statistics_data: async function() {
try {
const time_now = Date.now();
let statistics_data = common.getValue('statistics_data') || {};
if (time_now - this.request_time > 300) {
this.request_time = time_now;
// 获取最新数据
const res = await queryTraStdyStatisticsSelf();
if (res && res.body) {
// 合并新数据与缓存数据,确保只更新有效字段
statistics_data = {
...statistics_data,
stdtTmLen: res.body.stdtTmLen || 0,
accmTaskCnt: res.body.accmTaskCnt || 0,
accmPoint: res.body.accmPoint || 0
};
common.setValue('statistics_data', statistics_data);
}
}
// 解构赋值获取最新值
const {
accmPoint = 0, accmTaskCnt = 0, stdtTmLen = 0
} = statistics_data;
// 更新数据并触发动画
if (accmPoint !== this.accmPoint) {
this.startAccmPoint = this.accmPoint;
this.accmPoint = accmPoint;
accmPointRef.value?.start();
}
if (accmTaskCnt !== this.accmTaskCnt) {
this.startAccmTaskCnt = this.accmTaskCnt;
this.accmTaskCnt = accmTaskCnt;
accmTaskCntRef.value?.start();
}
if (stdtTmLen !== this.stdtTmLen) {
this.startStdtTmLen = this.stdtTmLen;
this.stdtTmLen = stdtTmLen;
stdtTmLenRef.value?.start();
}
} catch (error) {
console.error('获取统计数据失败:', error);
// 可以在这里添加更多的错误处理逻辑,比如显示错误提示
}
}
});
const user_info = reactive({
userName: '',
userId: '',
imageAddr: '',
});
const goTest = () => {
console.log('goTest');
common.navigateTo('/pages/test/index');
};
const click_loginout = () => {
common.show('确定退出登录?').then(async (res) => {
console.log(res);
if (!res) return;
try {
common.loading('正在退出登录');
socketStore.closeSocket()
await loginOut();
} catch (e) {}
common.hideLoading();
// 主动退出登录后,再次登录应该进首页
common.reLaunch('/pages/login/login');
});
};
onLoad(() => {
statistics_data['init'](); // 初始化数据
})
onShow(() => {
setMascot()
Object.assign(user_info, getUserInfo());
statistics_data['get_statistics_data'](); // 获取统计数据
});
onPullDownRefresh(() => {
console.log('下拉刷新');
uni.stopPullDownRefresh();
});
// 头像
const avatarCropperStatus = ref(true)
const avatarCropperUrl = ref('');
// 点击修改头像
const click_update_avatar = (value) => {
uni.chooseImage({
count: 1,
sizeType: ["compressed"],
success: (res) => {
avatarCropperUrl.value = res.tempFilePaths[0];
avatarCropperStatus.value = true
},
});
};
// 选择头像取消
const onCancel = (value) => {
avatarCropperStatus.value = false
}
// 选择头像确定
const onConfirm = (file) => {
avatarCropperStatus.value = false
nextTick(async () => {
common.loading('更新头像中')
try {
const data = {
bizScen: 'S3007',
thumbnailFlag: true,
thumbImgSize: 100
}
// 上传图片
const res = await uploadFile(file.tempFilePath, data)
// 更新个人信息
await updateDfSysUserExtandInfoImageAddr({
imageAddr: res.thumbFileId
})
// 更新个人信息
const userData = await queryCurrentUser()
console.log('userData', userData);
common.msg('修改成功')
user_info.imageAddr = userData.imageAddr
user_info.imagePath = ''
} catch {
common.msg('修改失败')
}
common.hideLoading()
})
}
</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: 101;
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 0 20rpx 0;
background-color: #ffffff;
border-radius: 23rpx;
display: flex;
flex-direction: column;
justify-content: space-around;
z-index: 101;
.scroll-view {
white-space: nowrap;
width: 100%;
}
.scroll-view-item {
display: inline-block;
padding: 32rpx 22rpx 36rpx 26rpx;
}
.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;
}
}
.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;
}
}
}
.dan {
display: flex;
flex-direction: column;
font-size: 16rpx;
align-items: center;
.dan_img {
height: 50rpx;
}
.dan_text {}
}
.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%;
background-color: #444;
}
}
.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>