Files
tra-app/src/pages/index/index.vue
T

734 lines
19 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" :backgroundColor="bgOpacity"></nav-bar>
<view class="bg_div">
<!-- 背景图 -->
<image src="@/static/images/index/bg.png" class="bg_img"></image>
<view class="fixed_search_div" v-show="opacity" :style="{ opacity: opacity }">
<view class="fixed_search_input_div" @click="goto_search_page">
<uv-input
shape="circle"
:customStyle="fixedCustomStyles"
placeholderStyle="color:#999999;font-size:26rpx"
placeholder="请输入关键字"
suffixIcon="search"
:suffixIconStyle="suffixIconStyle"
:readonly="true"
></uv-input>
</view>
<view class="message_div" @click="common.navigateTo('/pages/messageNotification/index')">
<image src="@/static/images/index/message_b.png" class="message_img"></image>
<view class="message_text">消息</view>
<view class="message_num message_num_font" :class="messageNumberClass" v-if="no_read_message_num > 0">
{{ messageDisplayText }}
</view>
</view>
</view>
<!-- 文字logo -->
<view class="title_logo">
<image src="@/static/images/index/logo_text.png" class="log_img" mode="heightFix"></image>
<view class="fl1"></view>
</view>
<!-- 搜索框 -->
<view class="search_div">
<uv-input
shape="circle"
@click="goto_search_page"
:customStyle="customStyles"
:placeholderStyle="placeholderStyle"
placeholder="请输入关键字"
suffixIcon="search"
:suffixIconStyle="suffixIconStyle"
:readonly="true"
></uv-input>
<view class="message_div" @click="common.navigateTo('/pages/messageNotification/index')">
<image src="@/static/images/index/message.png" class="message_img"></image>
<view class="message_text">消息</view>
<view class="message_num message_num_font" :class="messageNumberClass" v-if="no_read_message_num > 0">
{{ messageDisplayText }}
</view>
</view>
</view>
<!-- 滚动通知 -->
<view class="notice_div">
<!-- <uv-notice-bar
v-show="notice_text.length"
:text="notice_text"
:bgColor="null"
color="rgba(255,255,255,0.7)"
:speed="40"
direction="column"
></uv-notice-bar> -->
</view>
<!-- 学习时长和飞天兔子为了挡住部分兔子只能多一个盒子 -->
<view class="study_duration_and_profile_div">
<!-- 飞天兔子 -->
<view class="profile_div">
<image :src="mascotInfo?.imgAddr || ''" alt="" class="profile" mode="widthFix"></image>
<!-- <image src="/static/images/index/fttz.png" class="profile"></image> -->
</view>
<!-- 学习时长 -->
<view class="study_duration_div">
<view class="item">
<view class="top">本年学习时长</view>
<view class="bottom">
<uv-count-to
:startVal="statistics_data['startStdtTmLen']"
:endVal="statistics_data['stdtTmLen']"
:useEasing="true"
:decimals="1"
ref="stdtTmLenRef"
></uv-count-to>
h
</view>
</view>
<view class="item">
<view class="top">本年陪练次数</view>
<view class="bottom">
<uv-count-to
:startVal="statistics_data['starAccmPracticeCnt']"
:endVal="statistics_data['accmPracticeCnt']"
:useEasing="true"
ref="accmPracticeCntRef"
></uv-count-to>
<span class="number"></span>
</view>
</view>
<view class="item">
<view class="top">本年考试次数</view>
<view class="bottom">
<uv-count-to
:startVal="statistics_data['startAccmExamCnt']"
:endVal="statistics_data['accmExamCnt']"
:useEasing="true"
ref="accmExamCntRef"
></uv-count-to>
<span class="number"></span>
</view>
</view>
</view>
</view>
<!-- 8个导航 -->
<view class="navigation_div">
<view class="group">
<view class="item" @click="common.switchTab('/pages/course/index')">
<image class="icon" src="@/static/images/index/navigation_course.png"></image>
<view class="title">AI学习</view>
</view>
<view
class="item"
@click="common.navigateTo('/pages/index/dialog', { animationType: 'slide-in-bottom' })"
>
<image class="icon" src="@/static/images/index/navigation_question_answering.png"></image>
<view class="title">AI问答</view>
</view>
<view class="item" @click="common.switchTab('/pages/sparring/index')">
<image class="icon" src="@/static/images/index/navigation_ai.png"></image>
<view class="title">AI陪练</view>
</view>
<view class="item" @click="common.navigateTo('/pages/learningTasks/index')">
<image class="icon" src="@/static/images/index/navigation_learning_tasks.png"></image>
<view class="title">学习任务</view>
</view>
</view>
<view class="group">
<view class="item" @click="common.navigateTo('/pages/testingHall/index')">
<image class="icon" src="@/static/images/index/navigation_examination.png"></image>
<view class="title">考试中心</view>
</view>
<view class="item" @click="common.navigateTo('/pages/competition/index')">
<image class="icon" src="@/static/images/index/navigation_practice.png"></image>
<view class="title">竞赛练习</view>
</view>
<view class="item" @click="common.navigateTo('/pages/wrongQuestionRecord/index')">
<image class="icon" src="@/static/images/index/navigation_wrong_question.png"></image>
<view class="title">错题本</view>
</view>
<view class="item" @click="common.navigateTo('/pages/analytics/study')">
<image class="icon" src="@/static/images/index/navigation_learning_analysis.png"></image>
<view class="title">学习分析</view>
</view>
</view>
</view>
<classScroll :ref="(el) => (classScrollRef[0] = el)" name="hot_class"></classScroll>
<classScroll :ref="(el) => (classScrollRef[1] = el)" name="recommend_class"></classScroll>
<classScroll :ref="(el) => (classScrollRef[2] = el)" name="new_class"></classScroll>
<view class="bottom_line">
<uv-divider text="我是有底线的" lineColor="#9cB0DC" :hairline="true"></uv-divider>
</view>
</view>
<tabbar-shadow></tabbar-shadow>
<checkIn ref="checkInRef"></checkIn>
<holiday ref="holidayRef"></holiday>
<badge ref="badgeRef"></badge>
<points ref="pointsRef"></points>
</view>
</template>
<script setup>
import { computed, nextTick, onBeforeMount, onMounted, reactive, ref } from 'vue';
import { onShow, onPageScroll, onLoad, onHide } from '@dcloudio/uni-app';
import { customStyles, fixedCustomStyles, placeholderStyle, suffixIconStyle } from './index.js';
import { getUserInfo } from '@/common/common';
import common from '@/common/common';
import { setMascot } from '@/common/mascot.js';
import { getStatisticsData } from '@/api/index.js';
import { noticeMessageCount } from '@/api/messageNotification.js';
import classScroll from './components/class_scroll.vue';
import useCheckIn from '@/composables/useCheckIn';
import checkIn from '@/components/points-and-badge/check-in';
import holiday from '@/components/points-and-badge/holiday';
import badge from '@/components/points-and-badge/badge';
import points from '@/components/points-and-badge/points';
import usePointsAndBadge from '@/components/points-and-badge/usePointsAndBadge';
const { initCheckInStatus, checkInRef, checkInFun, startLotteryFun, holidayRef } = useCheckIn();
const { handlePointsBadgePopup, badgeRef, pointsRef } = usePointsAndBadge();
const scrollTop = ref(0);
const no_read_message_num = ref(0);
const classScrollRef = ref([]);
// 处理显示的消息数量文本
const messageDisplayText = computed(() => {
return no_read_message_num.value > 99 ? '99+' : no_read_message_num.value;
});
// 处理消息数量的样式类
const messageNumberClass = computed(() => ({
message_num_big: no_read_message_num.value > 99,
message_num_small: no_read_message_num.value <= 99
}));
const mascotShow = ref(false);
const stdtTmLenRef = ref(null);
const accmPracticeCntRef = ref(null);
const accmExamCntRef = ref(null);
// 计算透明度
const opacity = computed(() => {
if (scrollTop.value <= 130) return 0;
return Math.min(3, (scrollTop.value - 130) / 30);
});
const bgOpacity = computed(() => {
return `rgba(246,248,255,${opacity.value})`;
});
// 首页滑动事件监听
onPageScroll((e) => {
scrollTop.value = e.scrollTop;
});
// 滚动消息
const notice_text = ref(['绿色金融政策解析与商业银行绿色水水水水水水水水水水水水水', '绿色金融政策解析与商业银行红色']);
const mascotInfo = ref({});
const statistics_data = reactive({
stdtTmLen: 0, // 学习
accmPracticeCnt: 0, // 训练
accmExamCnt: 0, // 考试
startStdtTmLen: 0, // 起始学习
startAccmPracticeCnt: 0, // 起始训练
startAccmExamCnt: 0, // 起始考试
request_time: 0, // 上次请求时间
init: function () {
// 初始化统计数据
const index_statistics_data = common.getValue('statistics_data'); // 获取缓存
if (index_statistics_data) {
this.stdtTmLen = index_statistics_data.stdtTmLen;
this.accmPracticeCnt = index_statistics_data.accmPracticeCnt;
this.accmExamCnt = index_statistics_data.accmExamCnt;
}
},
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 getStatisticsData();
if (res && res.body) {
// 合并新数据与缓存数据,确保只更新有效字段
statistics_data = {
...statistics_data,
stdtTmLen: res.body.stdtTmLen || 0,
accmPracticeCnt: res.body.accmPracticeCnt || 0,
accmExamCnt: res.body.accmExamCnt || 0
};
common.setValue('statistics_data', statistics_data);
}
}
// 解构赋值获取最新值
const { accmExamCnt = 0, accmPracticeCnt = 0, stdtTmLen = 0 } = statistics_data;
// 更新数据并触发动画
if (stdtTmLen !== this.stdtTmLen) {
this.startStdtTmLen = this.stdtTmLen;
this.stdtTmLen = stdtTmLen;
stdtTmLenRef.value.start();
}
if (accmPracticeCnt !== this.accmPracticeCnt) {
this.startAccmPracticeCnt = this.accmPracticeCnt;
this.accmPracticeCnt = accmPracticeCnt;
accmPracticeCntRef.value.start();
}
if (accmExamCnt !== this.accmExamCnt) {
this.startAccmExamCnt = this.accmExamCnt;
this.accmExamCnt = accmExamCnt;
accmExamCntRef.value.start();
}
} catch (error) {
console.error('获取统计数据失败:', error);
// 可以在这里添加更多的错误处理逻辑,比如显示错误提示
}
}
});
// 进入搜索页
const goto_search_page = () => {
common.navigateTo('/pages/search/search?from=index');
};
onLoad(() => {
statistics_data['init'](); // 初始化数据
// 检测如果没设置吉祥物,跳转到吉祥物界面
// 判断下如果没有设置吉祥物就跳转到吉祥物页面
const userInfo = getUserInfo();
if (userInfo?.mascotInfo.mascotId === null) {
//说明这人在上次选吉祥物的时候退出了,没有选到吉祥物(概率很低)
common.redirectTo('/pages/mascot/mascot_select_list');
return;
}
});
onShow(() => {
if (!common.isLogin()) {
common.navigateTo('/pages/login/login');
return;
}
initCheckInStatus((checkInInfo, holidayBody) => {
// 先判断是否有节日活动
const handleCheckInComplete = (params) => {
const {isSigned, checkInDays, popUpStatus} = checkInInfo
setTimeout(() => {
if(!isSigned && !popUpStatus) {
checkInRef.value.open(isSigned, checkInDays, checkInFun, (status, days, pointsList = [], badgesList = []) => {
handlePointsBadgePopup(pointsList, badgesList);
});
}
}, 140)
};
holidayRef.value?.open(holidayBody, startLotteryFun, handleCheckInComplete)
});
const userInfo = getUserInfo();
if (userInfo) {
mascotInfo.value = userInfo?.mascotInfo;
}
setMascot();
statistics_data['get_statistics_data'](); // 获取统计数据
nextTick(() => {
classScrollRef.value.forEach((item) => item?.get_data());
});
noticeMessageCount().then((res) => {
no_read_message_num.value = res.body;
});
});
onHide(() => {});
</script>
<style scoped lang="scss">
$bg-margin-left: 30rpx;
// 设置骨架屏左侧图片框的大小,设置成和图片大小一致,否则横向滑动会串行(对不齐)
// :deep(.uv-skeleton__wrapper__avatar) {
// height: 146rpx !important;
// width: 146rpx !important;
// border-radius: 16rpx;
// }
// 底线样式
.bottom_line {
margin: 0 auto;
width: 240rpx;
}
// 热门课程等三个横向滑动模块的样式
.hot_class_div {
margin-left: $bg-margin-left;
.scroll-view {
white-space: nowrap;
width: 100%;
}
.scroll-view-item {
display: inline-block;
margin-right: 30rpx;
background-color: #ffffff;
border-radius: 22rpx;
box-shadow: #eaeaea 0 0 6rpx;
padding: 32rpx 22rpx 36rpx 26rpx;
}
.item_div {
width: 562rpx;
display: block;
}
.item {
display: flex;
.left {
border-radius: 16rpx;
overflow: hidden;
.img {
width: 200rpx;
height: 150rpx;
}
// margin-right: 22rpx;
}
.right {
flex: 1;
// width: calc(100% - 180rpx);
padding-left: 22rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
.title {
width: 100%;
font-weight: 500;
font-size: 30rpx;
color: #333333;
line-height: 40rpx;
text-align: left;
font-style: normal;
}
.note {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 23rpx;
color: #666666;
line-height: 33rpx;
text-align: left;
font-style: normal;
}
.button_div {
display: flex;
justify-content: space-between;
.button {
text-align: center;
line-height: 62rpx;
width: 138rpx;
height: 62rpx;
background: #ebf1ff;
border-radius: 30rpx;
font-weight: 500;
font-size: 30rpx;
color: #0066ff;
font-style: normal;
margin-right: 10rpx;
}
}
}
}
}
.bg_div {
height: 100%;
padding-bottom: 30rpx;
display: flex;
flex-direction: column;
position: relative;
z-index: 0;
overflow-x: hidden;
// 背景图
.bg_img {
position: absolute;
top: 0;
left: 0;
width: calc(100% + 4rpx);
height: 870rpx;
z-index: -1;
}
// 文字图片
.title_logo {
display: flex;
margin-top: 106rpx;
margin-left: $bg-margin-left;
.log_img {
margin-left: 10rpx;
// width: 300rpx;
height: 50rpx;
}
}
// 固定的搜索框
.fixed_search_div {
position: fixed;
background-color: #f6f8ff;
margin-top: var(--status-bar-height);
padding: 0 $bg-margin-left 20rpx $bg-margin-left;
height: 90rpx;
width: 100%;
z-index: 100;
display: flex;
align-items: center;
border-bottom: 4rpx solid #efefef;
.fixed_search_input_div {
flex: 1;
}
.message_div {
position: relative;
margin-left: 33rpx;
.message_text {
color: #333333;
}
}
}
// 搜索框
.search_div {
margin-top: 26rpx;
margin-left: $bg-margin-left;
width: 390rpx;
height: 70rpx;
z-index: 20;
display: flex;
position: relative;
.message_div {
position: absolute;
top: 4rpx;
right: -56rpx;
.message_text {
color: #ffffff;
}
}
}
.message_div {
display: flex;
flex-direction: column;
align-items: center;
min-width: 60rpx;
.message_img {
width: 32rpx;
height: 36rpx;
}
.message_text {
margin-top: 4rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 22rpx;
line-height: 24rpx;
white-space: nowrap;
}
.message_num {
position: absolute;
top: 2rpx;
right: 16rpx;
padding: 1rpx 2rpx 1rpx 2rpx;
background-color: #ff1926;
border-radius: 10rpx;
color: #ffffff;
}
.message_num_big {
right: 0rpx;
}
.message_num_small {
right: 8rpx;
min-width: 24rpx;
}
.message_num_font {
height: 22rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 16rpx;
}
}
//滚动消息
.notice_div {
// margin-top: 10rpx;
margin-left: $bg-margin-left;
width: 400rpx;
// height: 74rpx;
height: 34rpx;
font-weight: 500;
color: rgba(255, 255, 255, 0.7);
text-align: left;
font-style: normal;
display: flex;
align-items: center;
:deep(.uv-notice-bar) {
padding: 9px 2px;
}
:deep(.uv-notice__swiper__item__text) {
font-size: 20rpx !important;
}
:deep(.uv-notice__left-icon) .uv-icon__icon {
font-size: 24rpx !important;
}
// ::v-deep .u-notice-bar {
// padding: 14rpx 0;
// font-size: 22rpx;
// }
// :deep(.u-notice__left-icon) {
// margin-right: 5rpx;
// }
}
// 学习时长
.study_duration_and_profile_div {
margin: 0 $bg-margin-left;
position: relative;
//飞天兔子
.profile_div {
position: absolute;
top: -236rpx;
right: -20rpx;
z-index: -1;
.profile {
width: 278rpx;
// height: 370rpx;
}
}
.study_duration_div {
height: 162rpx;
background: linear-gradient(180deg, rgba(238, 246, 255, 0.65) 0%, #c5dffd 4%, #ffffff 100%);
border-radius: 23rpx;
display: flex;
justify-content: space-around;
z-index: 20;
opacity: 0.9;
.item {
flex: 1;
// background-color: red;
display: flex;
flex-direction: column;
justify-content: center;
.top {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 25rpx;
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;
}
.number {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 30rpx;
color: #000000;
}
}
}
}
}
// 快捷导航栏
.navigation_div {
margin: 16rpx $bg-margin-left 0 $bg-margin-left;
height: 338rpx;
background-color: #ffffff;
border-radius: 23rpx;
display: flex;
flex-direction: column;
justify-content: space-around;
box-shadow: #e6e8ef 0 5rpx 20rpx;
.group {
display: flex;
justify-content: space-around;
padding: 10rpx 10rpx;
// background-color: red;
}
.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;
}
}
}
}
.main_div {
display: flex;
flex-direction: column;
background-color: #f6f8ff;
}
</style>