Merge branch 'dev-20250930' of http://25.13.9.101:9000/K17_AITS/tra-app into dev-20250930

This commit is contained in:
杨航
2025-08-27 19:11:00 +08:00
38 changed files with 2543 additions and 1608 deletions
+26 -7
View File
@@ -147,6 +147,7 @@
const tabsDiv = ref(null);
const form = ref({});
const crsId = ref('');
const collectId = ref(''); // 记录初始的收藏id
const tabAct = ref(0);
const scrollTop = ref(0);
const tabSwitch = (item) => {
@@ -230,20 +231,23 @@
common.hideLoading();
});
};
const collect_click = async (collectId) => {
// uni.$emit('refresh_comment_data');
const collect_click = async (id) => {
try {
if (!collectId) {
if (!id) {
common.loading('收藏中');
form.value.collectId = await insertTraPersonalCollectionTask({
const result = await insertTraPersonalCollectionTask({
collectTyp: '01',
collectBusId: crsId.value
});
console.log('result.body', result.body);
form.value.collectId = result.body;
common.msg('收藏成功');
} else {
common.loading('取消收藏中');
await deleteTraPersonalCollectionById({
collectId: collectId
collectId: id
});
form.value.collectId = null;
@@ -255,20 +259,35 @@
};
onMounted(async () => {
uni.$on('refresh_comment_data', () => {
console.log('发评论了刷新');
// console.log('发评论了刷新');
commentRef.value?.getData('submit');
tabAct.value = 2;
});
});
onLoad((params) => {
crsId.value = params.crsId;
collectId.value = params.collectId || '';
});
onShow(() => {
getData();
});
onUnload(() => {
uni.$off('refresh_comment_data');
if (form.value.collectId !== collectId.value) {
const pages = getCurrentPages();
if (pages.length >= 2) {
const prevPage = pages[pages.length - 2];
if (prevPage.route === 'pages/favorites/index') {
// 更新
uni.$emit('refresh_favorites_data', {
type: 'course',
state: !!form.value.collectId, // 最终是收藏了还是取消了
old: collectId.value, // 进入时候的收藏id
new: form.value.collectId // 新的收藏id
});
}
}
}
});
</script>
@@ -1,7 +1,7 @@
<template>
<view class="item">
<view class="details_div">
<view class="img_div" v-if="props.mode === 'testingHall'">
<view class="img_div">
<image-preview class="img" :src="item.ossAddr" mode="scaleToFill"></image-preview>
</view>
<view class="right_div">
@@ -58,26 +58,15 @@
<script setup>
import { computed, ref, reactive, nextTick } from 'vue';
import { setPageCache } from '@/common/common';
import common from '@/common/common';
import { queryCrsExamHisByExamId } from '@/api/courseRecord.js';
const status = ref('loadmore'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const props = defineProps({
item: {
type: Object,
default: () => {}
},
mode: {
type: String,
default: 'testingHall',
validator: (value) => {
// 考试: examination
// 考试中心 testingHall
return ['examination', 'testingHall'].includes(value);
}
}
});
import { queryCrsExamHisByExamId } from '@/api/courseRecord.js';
const item = computed(() => props.item);
const data_list = reactive([]);
const total = ref(-1);
@@ -9,12 +9,9 @@
</template>
<script setup>
import itemVue from './examRecordItem.vue';
import { setPageCache } from '@/common/common';
import common from '@/common/common';
import { ref, reactive, onMounted, nextTick } from 'vue';
import { queryCrsExamRecordPaging } from '@/api/courseRecord.js';
import { studyDurationIcon, studyTimeIcon } from '@/common/imgSvg';
const status = ref('loading'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const limit = 15;
const total = ref(-1);
@@ -10,16 +10,16 @@
</view>
<view class="prompt_div">
<view class="prompt_div_left">
<view class="top">{{ item.correctRate || '' }}</view>
<view class="top">{{ item.correctRate || '0' }}%</view>
<view class="bottom">答题正确率</view>
</view>
<view class="prompt_div_line"></view>
<view class="prompt_div_right" @click="show_list_click">
<view class="click_text">
{{ item.exrCnt || 0 }}
{{ item.exrCnt || '0' }}
<uv-icon
class="click_text_icon"
:class="{click_text_icon_action:show_list_state}"
:class="{ click_text_icon_action: show_list_state }"
name="arrow-right"
color="#0066FF"
size="12"
@@ -58,8 +58,8 @@
<script setup>
import { computed, ref, reactive, nextTick } from 'vue';
import { setPageCache } from '@/common/common';
import common from '@/common/common';
import { queryPracticeHis } from '@/api/courseRecord.js';
const status = ref('loadmore'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const props = defineProps({
item: {
@@ -67,11 +67,10 @@
default: () => {}
}
});
import { queryPracticeHis } from '@/api/courseRecord.js';
const item = computed(() => props.item);
const data_list = reactive([]);
const total = ref(-1);
const show_list_state = ref(false);
const list_div_height = computed(() => {
if (!show_list_state.value) {
@@ -97,6 +96,7 @@
});
}
};
// 跳转到详情
const click_list_item = (list_item) => {
common.navigateTo(`/pages/courseRecord/coursePracticeRecordDetail?exrId=${list_item.exrId}`);
@@ -267,10 +267,9 @@
/* 添加过渡动画,包含延迟效果 */
transition: transform 0.2s ease;
}
.click_text_icon_action{
.click_text_icon_action {
transform: rotate(90deg);
}
}
}
}
@@ -9,9 +9,8 @@
</template>
<script setup>
import itemVue from './practiceRecordItem.vue';
import { setPageCache } from '@/common/common';
import common from '@/common/common';
import { ref, reactive, onMounted, nextTick } from 'vue';
import { ref, reactive, onMounted } from 'vue';
import { queryPracticeRecordPaging } from '@/api/courseRecord.js';
const status = ref('loading'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const limit = 15;
@@ -49,7 +48,7 @@
}
});
};
const scrolltolower = (item) => {
getData();
};
@@ -10,7 +10,7 @@
</view>
<view class="prompt_div">
<view class="prompt_div_left">
<view class="top">{{ item.stdyTm || '' }}</view>
<view class="top">{{ formatSeconds(item.stdyTm) || '' }}</view>
<view class="bottom">学习时长</view>
</view>
<view class="prompt_div_line"></view>
@@ -31,7 +31,7 @@
</view>
</view>
</view>
<view class="list_div" :style="{ height: list_div_height}">
<view class="list_div" :style="{ height: list_div_height }">
<view class="list_item" v-for="item_list in data_list" @click="click_list_item(item_list)">
<view class="list_content">
<view class="list_small_item">
@@ -57,9 +57,10 @@
</template>
<script setup>
import { computed, ref, reactive, nextTick } from 'vue';
import { setPageCache } from '@/common/common';
import { computed, ref, reactive } from 'vue';
import { formatSeconds } from '@/common/common';
import common from '@/common/common';
import { queryStdyBatchByCrsId } from '@/api/courseRecord.js';
const status = ref('loadmore'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const props = defineProps({
item: {
@@ -67,8 +68,6 @@
default: () => {}
}
});
import { queryStdyBatchByCrsId } from '@/api/courseRecord.js';
const item = computed(() => props.item);
const data_list = reactive([]);
const total = ref(-1);
@@ -82,7 +81,7 @@
return data_list.length * 136 + 'rpx';
}
});
// 点击获取详情
const show_list_click = () => {
show_list_state.value = !show_list_state.value;
@@ -99,9 +98,12 @@
};
// 跳转到详情
const click_list_item = (list_item) => {
common.navigateTo(`/pages/courseRecord/courseStudyRecordDetail?stdyId=${list_item.stdyBatch}`);
common.navigateTo(
`/pages/courseRecord/courseStudyRecordDetail?stdyId=${list_item.stdyBatch}&crsName=${item.value.crsName}`
);
};
// 计算两个标准时间的时间差值
const formatDuration = (startTm, endTm) => {
// 解析时间字符串为时间戳(毫秒)
@@ -131,7 +133,6 @@
}
.list_div {
overflow: hidden; /* 确保内容不会溢出容器 */
transition: height 0.3s ease-out; /* 高度动画 */
.list_item {
@@ -9,12 +9,9 @@
</template>
<script setup>
import itemVue from './studyRecordItem.vue';
import { setPageCache } from '@/common/common';
import common from '@/common/common';
import { ref, reactive, onMounted, nextTick } from 'vue';
import { queryStdyRecordPaging } from '@/api/courseRecord.js';
import { studyDurationIcon, studyTimeIcon } from '@/common/imgSvg';
const status = ref('loading'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const limit = 15;
const total = ref(-1);
@@ -49,7 +49,7 @@
$bg-margin-left: 30rpx;
.scroll-Y {
height: calc(100vh - var(--status-bar-height) - 70rpx - 10rpx);
height: calc(100vh - var(--status-bar-height) - 70rpx);
}
.content {
position: relative;
+2 -1
View File
@@ -45,6 +45,7 @@
import studyRecordList from './components/studyRecordList.vue';
import practiceRecordList from './components/practiceRecordList.vue';
import examRecordList from './components/examRecordList.vue';
import { reactive, ref, computed, nextTick, onMounted, watch } from 'vue';
import common from '@/common/common.js';
const listItemRefs = ref([]);
@@ -82,7 +83,7 @@
{
immediate: true
}
); // 这里的immediate会在onMounted之后执行
);
});
</script>
+18 -2
View File
@@ -92,8 +92,10 @@
@submit="touchBtnSubmit"
loadingText="问题回答中,请在问题回答结束后重试!"
:isLoading="!answerIsOver"
:isDisabled="false"
:isDisabled="touchIsDisabled"
/>
</view>
</view>
@@ -159,6 +161,7 @@
const feedbackRef = ref(null);
const dialogRef = ref(null);
const popupRef = ref(null);
let examinationStartTime = 0; // 考试开始时间
const mode = ref('examination'); // 考试 examination 练习practice
const paperData = reactive({
@@ -172,12 +175,21 @@
const popup_input_bottom = ref('0px');
const questionNumber = ref(0); // 题号
const systemInfo = uni.getSystemInfoSync();
const topic = computed({
get: () => topicList[questionNumber.value],
set: (val) => (topicList[questionNumber.value] = val)
});
const showTouchBtn = computed(() => {
return topicList[questionNumber.value]['qnsTyp'] === 'ES';
});
const completedList = computed(() => {
return [];
});
// 下面语音组件的禁用状态判断
const touchIsDisabled = computed(() => {
return topic.value?.es_status !== '' && topic.value?.es_status !== undefined;
});
const markList = computed(() => topicList.filter((res) => !!res.mark).map((res) => res.qnsId)); // 标记列表
// 下一题按钮状态
@@ -265,6 +277,9 @@
common.navigateBack();
return;
}
// 记录起始时间
examinationStartTime = new Date().getTime();
paperData.execId = examination.execId;
paperData.examId = examination.examId;
paperData.papersId = examination.papersId;
@@ -395,7 +410,8 @@
common.loading('正在提交');
// 这里通过是否存在examId来判断是考试中心的考试还是课程的考试
let func;
if (paperData.examId) { // 这里是考试中心考试
if (paperData.examId) {
// 这里是考试中心考试
func = commitPaperExam({
papersId: paperData.papersId,
examId: paperData.examId,
@@ -0,0 +1,276 @@
<template>
<view class="item">
<view class="details_div">
<!-- <view class="img_div"> -->
<!-- <image-preview class="img" :src="item.ossAddr" mode="scaleToFill"></image-preview> -->
<!-- </view> -->
<view class="right_div">
<view class="title ellipsis-text">
{{ item.examName }}
</view>
<view class="prompt_div">
<view class="prompt_div_left">
<view class="top">{{ item.highestGrade || 0 }}</view>
<view class="bottom">历史最高分</view>
</view>
<view class="prompt_div_line"></view>
<view class="prompt_div_right" @click="show_list_click">
<view class="click_text">
{{ item.examCnt || 0 }}
<uv-icon
class="click_text_icon"
:class="{click_text_icon_action:show_list_state}"
name="arrow-right"
color="#0066FF"
size="12"
:bold="true"
></uv-icon>
</view>
<view class="bottom">考试次数</view>
</view>
</view>
</view>
</view>
<view class="list_div" :style="{ height: list_div_height }">
<view class="list_item" v-for="item_list in data_list" @click="click_list_item(item_list)">
<view class="list_content">
<view class="list_small_item">
<image src="@/static/images/courseRecord/time.png" class="icon_img"></image>
<view class="prompt">考试时间{{ item_list.examStartTm }}</view>
</view>
<view class="list_small_item">
<image src="@/static/images/courseRecord/duration.png" class="icon_img"></image>
<view class="prompt">考试得分{{ item_list.examGrade }}</view>
</view>
</view>
<view class="fl1"></view>
<uv-icon name="arrow-right" color="#979797" size="17" :bold="true"></uv-icon>
</view>
<uv-load-more
v-if="status === 'loading'"
:status="status"
loadmore-text="轻轻上拉加载"
:height="30"
></uv-load-more>
</view>
</view>
</template>
<script setup>
import { computed, ref, reactive, nextTick } from 'vue';
import { setPageCache } from '@/common/common';
import common from '@/common/common';
const status = ref('loadmore'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const props = defineProps({
item: {
type: Object,
default: () => {}
}
});
import { queryExamHisByExamId } from '@/api/courseRecord.js';
const item = computed(() => props.item);
const data_list = reactive([]);
const total = ref(-1);
const show_list_state = ref(false);
const list_div_height = computed(() => {
if (!show_list_state.value) {
return '0';
} else if (status.value === 'loading') {
return '100rpx';
} else {
return data_list.length * 136 + 'rpx';
}
});
// 点击获取考试详情
const show_list_click = () => {
show_list_state.value = !show_list_state.value;
if (total.value === -1) {
//加载数据
status.value = 'loading';
data_list.length = 0;
queryExamHisByExamId({ examId: item.value.examId }).then((res) => {
data_list.push(...res.body);
total.value = data_list.length;
status.value = 'loadmore';
});
}
};
// 跳转到详情
const click_list_item = (list_item) => {
const examLenTm = 666;
const papersName = item.value.examName;
common.navigateTo(
`/pages/examination/result?execId=${list_item.execId}&examLenTm=${examLenTm}&papersName=${papersName}`
);
};
</script>
<style scoped lang="scss">
.icon_img {
width: 24rpx;
height: 24rpx;
}
.list_div {
overflow: hidden; /* 确保内容不会溢出容器 */
transition: height 0.2s ease-out; /* 高度动画 */
.list_item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 18rpx 32rpx 18rpx 28rpx;
background-color: #f9fbff;
border-radius: 8rpx;
margin: 20rpx 0 0 0;
.list_content {
.list_small_item {
display: flex;
align-items: center;
font-weight: 400;
font-size: 24rpx;
color: #666666;
line-height: 40rpx;
text-align: left;
font-style: normal;
.prompt {
margin-left: 16rpx;
}
}
}
}
}
.item {
margin: 17rpx 20rpx;
background-color: #fff;
border-radius: 16rpx;
padding: 32rpx 22rpx;
.details_div {
display: flex;
.img_div {
position: relative;
width: 226rpx;
height: 168rpx;
overflow: hidden;
border-radius: 22rpx;
.img {
width: 226rpx;
height: 168rpx;
}
.subscript {
position: absolute;
right: 0;
top: 0;
width: 92rpx;
height: 46rpx;
border-radius: 0px 0px 0px 22rpx;
font-family: PingFangSC;
font-weight: 400;
font-size: 22rpx;
line-height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
}
.right_div {
flex: 1;
overflow: hidden;
margin-left: 24rpx;
display: flex;
flex-direction: column;
.title {
font-family: PingFangSC;
color: #333333;
text-align: left;
font-style: normal;
font-weight: 600;
font-size: 28rpx; /* 字体大小30rpx */
overflow: hidden; /* 超出部分隐藏 */
margin-bottom: 8rpx;
min-height: 40rpx;
}
.prompt_div {
font-family: PingFangSC;
font-weight: 400;
font-size: 22rpx;
color: #666;
line-height: 34rpx;
display: flex;
align-items: center;
overflow: hidden;
height: 116rpx;
background-color: #f9fbff;
border-radius: 8rpx;
justify-content: space-around;
.prompt_div_line {
width: 4rpx;
height: 30rpx;
background: linear-gradient(
to bottom,
RGBA(242, 244, 248, 1) 0%,
RGBA(230, 232, 238, 1) 30%,
RGBA(230, 232, 238, 1) 70%,
RGBA(242, 244, 248, 1) 100%
);
}
.prompt_div_left,
.prompt_div_right {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
.top,
.click_text {
font-family: Arial;
font-weight: 700;
font-size: 30rpx;
color: #333333;
line-height: 34rpx;
text-align: center;
font-style: normal;
}
.bottom {
}
.click_text {
position: relative;
.click_text_icon {
position: absolute;
margin-left: 18rpx;
top: calc(50% - 12rpx);
left: calc(100% - 14rpx);
/* 添加过渡动画,包含延迟效果 */
transition: transform 0.2s ease;
}
.click_text_icon_action{
transform: rotate(90deg);
}
}
}
}
.people_progress_div {
display: flex;
font-family: PingFangSC;
font-weight: 400;
font-size: 23rpx;
color: #666;
margin-bottom: 23rpx;
line-height: 23rpx;
flex-wrap: nowrap;
height: 23rpx;
overflow: hidden;
}
}
}
}
</style>
@@ -0,0 +1,69 @@
<template>
<view class="scroll_div">
<scroll-view scroll-y="true" class="scroll-Y" @scrolltolower="scrolltolower" :show-scrollbar="false">
<itemVue :item="item" v-for="(item, index) in data_list"></itemVue>
<list-no-data v-if="total == 0 && status == 'nomore'"></list-no-data>
<uv-load-more v-if="total != 0" :status="status" loadmore-text="轻轻上拉加载" :height="30"></uv-load-more>
</scroll-view>
</view>
</template>
<script setup>
import itemVue from './examRecordItem.vue';
import common from '@/common/common';
import { ref, reactive, onMounted, nextTick } from 'vue';
import { queryExamRecordPaging } from '@/api/courseRecord.js';
const status = ref('loading'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const limit = 15;
const total = ref(-1);
const page = ref(0);
const data_list = reactive([]);
const getData = (from = '') => {
if (from == 'first') {
if (total.value !== -1) {
return;
}
status.value = 'loading';
total.value = -1;
page.value = 1;
data_list.length = 0;
} else {
if (status.value !== 'loadmore' && status.value !== '') return;
status.value = 'loading';
page.value++;
}
queryExamRecordPaging({
page: page.value,
limit: limit
})
.then((res) => {
total.value = res.total;
data_list.push(...res.body);
})
.finally(() => {
if (data_list.length >= total.value) {
status.value = 'nomore';
} else {
status.value = 'loadmore';
}
});
};
const scrolltolower = (item) => {
getData();
};
defineExpose({
getData
});
</script>
<style scoped lang="scss">
.scroll_div {
background-color: #f1f5fa;
}
.scroll-Y {
height: calc(100vh - var(--status-bar-height) - 88rpx - 88rpx - 30rpx);
}
</style>
@@ -1,249 +0,0 @@
<template>
<view class="scroll_div">
<scroll-view scroll-y="true" class="scroll-Y" @scrolltolower="scrolltolower" :show-scrollbar="false">
<view
class="item"
:class="getStatusClass(item.isFinish)"
v-for="(item, index) in data_list"
@click="click_item(item)"
>
<view class="img_div">
<image-preview class="img" :src="item.ossAddr" mode="scaleToFill"></image-preview>
<view class="subscript" v-if="item.isFinish === '00'">未完成</view>
<view class="subscript" v-if="item.isFinish === '01'">已完成</view>
</view>
<view class="right_div">
<view class="title ellipsis-text">
{{ item.taskName }}
</view>
<view class="time_div" v-if="item.limitTyp === '02'">
起止时间
<text class="blod_color">{{ item.startTm?.substr(0, 10) }}{{ item.endTm?.substr(0, 10) }}</text>
</view>
<view class="people_progress_div">
<view class="mr-10">
完成人数
<text class="">{{ item.taskFinishSums ?? 0 }}/{{ item.taskSums ?? 0 }}</text>
</view>
<view>
进度
<text class="">{{ item.finishCrs ?? 0 }}/{{ item.sumCrs ?? 0 }}门课程</text>
</view>
</view>
</view>
</view>
<list-no-data v-if="total == 0 && status == 'nomore'"></list-no-data>
<uv-load-more v-if="total != 0" :status="status" loadmore-text="轻轻上拉加载" :height="30"></uv-load-more>
</scroll-view>
</view>
</template>
<script setup>
const status = ref('loading'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const limit = 15;
const total = ref(-1);
const page = ref(0);
const data_list = reactive([]);
import { setPageCache } from '@/common/common';
import common from '@/common/common';
import { ref, reactive, onMounted, nextTick } from 'vue';
import { queryAllTraTaskInfoByUserId } from '@/api/learningTasks.js';
const props = defineProps({
state: {
type: String,
required: true
}
});
const getData = (from = '', searchText = '') => {
if (from == 'first') {
if (total.value !== -1) {
return;
}
status.value = 'loading';
total.value = -1;
page.value = 1;
data_list.length = 0;
} else {
if (status.value !== 'loadmore' && status.value !== '') return;
status.value = 'loading';
page.value++;
}
setTimeout(() => {
queryAllTraTaskInfoByUserId({
state: props.state,
page: page.value,
limit: limit,
taskName: searchText
})
.then((res) => {
total.value = res.total;
data_list.push(...res.body);
})
.finally(() => {
if (data_list.length >= total.value) {
status.value = 'nomore';
} else {
status.value = 'loadmore';
}
});
}, 200);
};
const getStatusClass = (isFinish) => {
// 00进行中、01已完成、02已超时
if (isFinish === '00') return 'in_progress';
if (isFinish === '01') return 'completed';
return 'expired';
};
// 跳转到详情
const click_item = (item) => {
if(item.isTimeOut === '00') {
common.msg('任务已结束')
return ;
}
setPageCache('learning_tasks_details', item);
common.navigateTo('/pages/learningTasks/details');
};
const scrolltolower = (item) => {
getData();
};
const search = (value) => {
total.value = -1;
console.log('value', value);
getData('first', value);
};
defineExpose({
getData,
search
});
</script>
<style scoped lang="scss">
.scroll_div {
background-color: #f1f5fa;
padding: 22rpx;
}
.scroll-Y {
height: calc(100vh - var(--status-bar-height) - 88rpx - 80rpx - 88rpx - 20rpx - 30rpx);
// height: calc(100vh - var(--status-bar-height) - 54rpx - 88rpx - 20rpx - 44rpx);
background-color: #fff;
border-radius: 16rpx;
}
.scroll-Y {
.item.in_progress {
// 进行中
.subscript {
color: #0066ff;
background-color: #daf0ff;
}
.blod_color {
color: #0066ff;
}
}
.item.completed {
// 已完成
.subscript {
color: #ffffff;
background-color: #33c583 !important;
}
.blod_color {
color: #333333;
}
}
.item.expired {
// 已超时
.subscript {
color: #ffffff;
background-color: #ef5705;
}
.blod_color {
color: #d70c18;
}
}
}
.scroll-Y .item {
height: 170rpx;
margin: 32rpx 22rpx;
display: flex;
.img_div {
position: relative;
width: 224rpx;
height: 168rpx;
overflow: hidden;
border-radius: 22rpx;
.img {
width: 226rpx;
height: 168rpx;
}
.subscript {
position: absolute;
right: 0;
top: 0;
width: 92rpx;
height: 46rpx;
border-radius: 0px 0px 0px 22rpx;
font-family: PingFangSC;
font-weight: 400;
font-size: 22rpx;
line-height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
}
.right_div {
flex: 1;
overflow: hidden;
margin-left: 24rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
.title {
font-family: PingFangSC;
font-size: 30rpx;
font-weight: 600;
color: #333333;
line-height: 44rpx;
}
.time_div {
font-family: PingFangSC;
font-weight: 400;
font-size: 23rpx;
color: #666;
line-height: 23rpx;
flex-wrap: nowrap;
height: 23rpx;
overflow: hidden;
}
.people_progress_div {
display: flex;
font-family: PingFangSC;
font-weight: 400;
font-size: 23rpx;
color: #666;
margin-bottom: 23rpx;
line-height: 23rpx;
flex-wrap: nowrap;
height: 23rpx;
overflow: hidden;
}
}
}
</style>
+14 -51
View File
@@ -5,59 +5,34 @@
<view class="back_div" @click="common.navigateBack()">
<view class="back-icon"></view>
</view>
<view class="title font_pf">学习任务</view>
</view>
<view class="fixed_search_input_div">
<search-input placeholder="请输入任务名" @search="search"></search-input>
<view class="title font_pf">考试记录</view>
</view>
<view class="detail-body">
<view class="body-tab">
<uv-tabs
class="font_pf my_tabs"
:current="tabAct"
:list="tabList"
@change="tabChange"
:scrollable="false"
line-color="#0066FF"
:inactive-style="{ color: '#333333', fontSize: '30rpx', fontWeight: '600' }"
:activeStyle="{ color: '#0066FF', fontSize: '30rpx', fontWeight: '600' }"
:lineColor="`url(${LineBg}) 10% 10%`"
></uv-tabs>
</view>
<view class="uni-margin-wrap no-overflow">
<swiper class="swiper" :current="tabAct" :indicator-dots="false" @change="swiperChange">
<swiper-item>
<listItem :ref="(el) => (listItemRefs[0] = el)" state="01"></listItem>
</swiper-item>
<swiper-item>
<listItem :ref="(el) => (listItemRefs[1] = el)" state="02"></listItem>
</swiper-item>
<swiper-item>
<listItem :ref="(el) => (listItemRefs[2] = el)" state="03"></listItem>
</swiper-item>
</swiper>
</view>
<examRecordList ref="listItemRef"></examRecordList>
</view>
</view>
</template>
<script setup>
import { LineBg } from '@/enum.js';
import listItem from './components/list-item.vue';
import examRecordList from './components/examRecordList.vue';
import { reactive, ref, computed, nextTick, onMounted, watch } from 'vue';
import common from '@/common/common.js';
const listItemRefs = ref([]);
const listItemRef = ref(null);
const taskRef = ref(null);
const tabAct = ref(0);
const tabList = ref([
{
name: '全 部'
name: '学习记录'
},
{
name: '已完成'
name: '练习记录'
},
{
name: '未完成'
name: '考试记录'
}
]);
@@ -67,21 +42,10 @@
const swiperChange = (item) => {
tabAct.value = item.detail.current;
};
const search = (value) => {
listItemRefs.value[tabAct.value]?.search(value);
};
onMounted(() => {
watch(
tabAct,
(newValue, oldValue) => {
typeof listItemRefs.value[newValue]?.getData === 'function' &&
listItemRefs.value[newValue]?.getData('first');
},
{
immediate: true
}
); // 这里的immediate会在onMounted之后执行
listItemRef.value?.getData('first');
});
</script>
@@ -102,12 +66,11 @@
/*
状态栏 var(--status-bar-height)
标题高度88rpx
搜索栏高度80rpx
tab切换栏高度88rpx
下方滑动区域上下边距各10共20rpx
**/
height: calc(100vh - var(--status-bar-height) - 88rpx - 80rpx - 88rpx - 20rpx);
height: calc(100vh - var(--status-bar-height) - 88rpx - 88rpx - 30rpx);
}
.swiper-item {
@@ -1,236 +0,0 @@
<template>
<view class="scroll_div">
<scroll-view scroll-y="true" class="scroll-Y" @scrolltolower="scrolltolower" :show-scrollbar="false">
<view class="item" :class="getStatusClass(index)" v-for="(item,index) in data_list" @click="click_item(item)" >
<view class="img_div">
<image-preview class="img" :src="item?.ossAddr" mode="scaleToFill"></image-preview>
<view class="subscript" v-if="item.isFinish === '00'">未完成</view>
<view class="subscript" v-if="item.isFinish === '01'">已完成</view>
</view>
<view class="right_div">
<view class="title ellipsis-text">
{{item.taskName}}
</view>
<view class="time_div">
起止时间<text class="blod_color">{{item.startTm?.substr(0, 10)}}{{item.endTm?.substr(0, 10)}}</text>
</view>
<view class="people_progress_div">
<view class="mr-10">
完成人数<text class="blod_color">{{item.taskFinishSums ?? 0}}/{{item.taskSums ?? 0}}</text>
</view>
<view>
我的进度<text class="blod_color">{{item.myProcess ?? 0}}%</text>
</view>
</view>
</view>
</view>
<list-no-data v-if="total == 0 && status=='nomore'"></list-no-data>
<uv-load-more v-if="total != 0" :status="status" loadmore-text="轻轻上拉加载" :height="30"></uv-load-more>
</scroll-view>
</view>
</template>
<script setup>
const status = ref('loading') // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const limit = 15
const total = ref(-1)
const page = ref(0)
const data_list = reactive([])
import {
ref,
reactive,
onMounted,
nextTick
} from 'vue'
import {
queryTraPersonalCollectionTaskPaging
} from '@/api/favorites.js';
const getData = (from = '') => {
if (from == 'first') {
if (total.value !== -1) {
return
}
status.value = 'loading'
total.value = -1
page.value = 1
data_list.length = 0
} else {
if (status.value !== 'loadmore' && status.value !== '') return
status.value = 'loading'
page.value++
}
console.log(status.value, '阿斯顿撒');
setTimeout(() => {
queryTraPersonalCollectionTaskPaging({
page: page.value,
limit: limit
}).then(res => {
total.value = res.total
data_list.push(...res.body)
}).finally(() => {
if (data_list.length >= total.value) {
status.value = 'nomore'
} else {
status.value = 'loadmore'
}
})
}, 200)
}
// 跳转到详情
const click_item = (item) => {
// tudo
// if(item.isTimeOut === '00') {
// common.msg('任务已结束')
// return ;
// }
// setPageCache('learning_tasks_details', item);
// common.navigateTo('/pages/learningTasks/details');
};
const getStatusClass = (index) => {
const remainder = index % 3;
if (remainder === 0) return 'in_progress';
if (remainder === 1) return 'completed';
return 'expired';
};
const scrolltolower = (item) => {
getData()
};
defineExpose({
getData
})
</script>
<style scoped lang="scss">
.scroll_div {
background-color: #F1F5FA;
padding: 22rpx;
}
.scroll-Y {
height: calc(100vh - var(--status-bar-height) - 54rpx - 88rpx - 20rpx - 44rpx);
background-color: #fff;
border-radius: 16rpx;
}
.scroll-Y .item.in_progress {
// 进行中
.subscript {
color: #0066FF;
background-color: #DAF0FF;
}
.blod_color {
color: #333333;
}
}
.scroll-Y .item.completed {
// 已完成
.subscript {
color: #FFFFFF;
background-color: #33C583;
}
.blod_color {
color: #333333;
}
}
.scroll-Y .item.expired {
// 已超时
.subscript {
color: #FFFFFF;
background-color: #EF5705;
}
.blod_color {
color: #D70C18;
}
}
.scroll-Y .item {
height: 170rpx;
margin: 32rpx 22rpx;
display: flex;
.img_div {
position: relative;
width: 224rpx;
height: 168rpx;
overflow: hidden;
border-radius: 22rpx;
.img {
width: 226rpx;
height: 168rpx;
}
.subscript {
position: absolute;
right: 0;
top: 0;
width: 92rpx;
height: 46rpx;
border-radius: 0px 0px 0px 22rpx;
font-family: PingFangSC;
font-weight: 400;
font-size: 22rpx;
line-height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
}
.right_div {
flex: 1;
overflow: hidden;
margin-left: 24rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
.title {
font-family: PingFangSC;
font-size: 30rpx;
font-weight: 600;
color: #333333;
line-height: 44rpx;
}
.time_div {
font-family: PingFangSC;
font-weight: 400;
font-size: 23rpx;
color: #666;
line-height: 23rpx;
flex-wrap: nowrap;
height: 23rpx;
overflow: hidden;
}
.people_progress_div {
display: flex;
font-family: PingFangSC;
font-weight: 400;
font-size: 23rpx;
color: #666;
margin-bottom: 23rpx;
line-height: 23rpx;
flex-wrap: nowrap;
height: 23rpx;
overflow: hidden;
}
}
}
</style>
@@ -2,127 +2,129 @@
<view class="scroll_div">
<scroll-view scroll-y="true" class="scroll-Y" @scrolltolower="scrolltolower" :show-scrollbar="false">
<uv-swipe-action :autoClose="true">
<uv-swipe-action-item class="item-swipe-action" :options="options" :threshold="40" :index="index"
:name="index" v-for="(item,index) in data_list" @click="swipeActionClick" >
<uv-swipe-action-item
class="item-swipe-action"
:options="options"
:threshold="40"
:index="index"
:name="index"
v-for="(item, index) in data_list"
@click="swipeActionClick"
>
<view class="item" @touchmove.stop @click="click_item(item)">
<view class="img_div">
<image-preview class="img" :src="item?.ossAddr" mode="scaleToFill"></image-preview>
<view class="subscript">
{{item.evalGrade ?? 0}}
</view>
<view class="subscript">{{ item.evalGrade ?? 0 }}</view>
</view>
<view class="right_div">
<view class="title ellipsis-text">
{{item.crsName}}
{{ item.crsName }}
</view>
<view class="tag_div">
<view class="tag" v-for="tagItem in item?.tagCataList ?? []">
{{tagItem?.tagName}}
{{ tagItem?.tagName }}
</view>
</view>
<view class="time_and_num">
<view class="mr-10">
发布{{item.issuTm?.substr(0, 10)}}
</view>
<view>
已学{{item.accmStdyCnt}}
</view>
<view class="mr-10">发布{{ item.issuTm?.substr(0, 10) }}</view>
<view>已学{{ item.accmStdyCnt }}</view>
</view>
</view>
</view>
</uv-swipe-action-item>
</uv-swipe-action>
<list-no-data v-if="total == 0 && status=='nomore'"></list-no-data>
<list-no-data v-if="total == 0 && status == 'nomore'"></list-no-data>
<uv-load-more v-if="total != 0" :status="status" loadmore-text="轻轻上拉加载" :height="30"></uv-load-more>
</scroll-view>
</view>
</template>
<script setup>
const status = ref('loading') // loadmore - loading - nomore -
const limit = 15
const total = ref(-1)
const page = ref(0)
const data_list = reactive([])
import {
ref,
reactive,
onMounted,
nextTick
} from 'vue'
import {
queryTraPersonalCollectionCrsPaging,
deleteTraPersonalCollectionById
} from '@/api/favorites.js';
const status = ref('loading'); // loadmore - loading - nomore -
const limit = 15;
const total = ref(-1);
const page = ref(0);
const data_list = reactive([]);
import { ref, reactive, onMounted, nextTick } from 'vue';
import { queryTraPersonalCollectionCrsPaging, deleteTraPersonalCollectionById } from '@/api/favorites.js';
import common from '@/common/common';
const options = [{
text: '删除',
style: {
backgroundColor: '#f56c6c'
const options = [
{
text: '删除',
style: {
backgroundColor: '#f56c6c'
}
}
}]
];
const getData = (from = '') => {
console.log('status.value', status.value);
if (from == 'first') {
if (total.value !== -1) {
return
return;
}
status.value = 'loading'
total.value = -1
page.value = 1
data_list.length = 0
status.value = 'loading';
total.value = -1;
page.value = 1;
data_list.length = 0;
} else {
if (status.value !== 'loadmore' && status.value !== '') return
status.value = 'loading'
page.value++
if (status.value !== 'loadmore' && status.value !== '') return;
status.value = 'loading';
page.value++;
}
setTimeout(() => {
queryTraPersonalCollectionCrsPaging({
page: page.value,
limit: limit
}).then(res => {
total.value = res.total
data_list.push(...res.body)
}).finally(() => {
if (data_list.length >= total.value) {
status.value = 'nomore'
} else {
status.value = 'loadmore'
}
queryTraPersonalCollectionCrsPaging({
page: page.value,
limit: limit
})
.then((res) => {
total.value = res.total;
data_list.push(...res.body);
})
}, 200)
}
.finally(() => {
if (data_list.length >= total.value) {
status.value = 'nomore';
} else {
status.value = 'loadmore';
}
console.log('status.value', status.value);
});
};
//
const click_item = (item) => {
common.navigateTo('/pages/courseDetail/index?crsId=' + item.crsId)
common.navigateTo(`/pages/courseDetail/index?crsId=${item.crsId}&collectId=${item.collectId}`);
};
const swipeActionClick = (click_item) => {
const index = click_item['name']
const item = data_list[index]
common.show('提示', '确认删除吗?').then(res => {
const index = click_item['name'];
const item = data_list[index];
common.show('提示', '确认删除吗?').then((res) => {
if (!res) return;
common.loading()
const collectId = '11'
deleteTraPersonalCollectionById({collectId:item.collectId}).then(()=>{
data_list.splice(index, 1); //
total.value -= 1 //
}).finally(() => {
common.hideLoading()
})
})
}
const scrolltolower = (item) => {
getData()
common.loading();
deleteTraPersonalCollectionById({ collectId: item.collectId })
.then(() => {
data_list.splice(index, 1); //
total.value -= 1; //
})
.finally(() => {
common.hideLoading();
});
});
};
const scrolltolower = (item) => {
getData();
};
const refreshData = (params) => {
total.value = -1;
getData('first');
};
defineExpose({
getData
})
getData,
refreshData
});
</script>
<style scoped lang="scss">
.scroll_div {
background-color: #F1F5FA;
background-color: #f1f5fa;
padding: 22rpx;
}
@@ -171,8 +173,8 @@
display: flex;
align-items: center;
justify-content: center;
background-color: #EF5705;
color: #FFFFFF;
background-color: #ef5705;
color: #ffffff;
}
}
@@ -203,16 +205,15 @@
padding: 0 10rpx;
margin-right: 14rpx;
border-radius: 8rpx;
border: 2rpx solid #FFA150;
border: 2rpx solid #ffa150;
font-family: PingFangSC;
font-weight: 500;
font-size: 23rpx;
color: #FFA150;
color: #ffa150;
display: flex;
justify-content: center;
align-items: center;
}
}
.time_and_num {
@@ -229,4 +230,4 @@
}
}
}
</style>
</style>
@@ -0,0 +1,282 @@
<template>
<view class="scroll_div">
<scroll-view scroll-y="true" class="scroll-Y" @scrolltolower="scrolltolower" :show-scrollbar="false">
<uv-swipe-action :autoClose="true">
<uv-swipe-action-item
class="item-swipe-action"
:options="options"
:threshold="40"
:index="index"
:name="index"
v-for="(item, index) in data_list"
@click="swipeActionClick"
>
<view class="item" :class="getStatusClass(index)" @touchmove.stop @click="click_item(item)">
<view class="img_div">
<image-preview class="img" :src="item?.ossAddr" mode="scaleToFill"></image-preview>
<view class="subscript" v-if="item.isFinish === '00'">未完成</view>
<view class="subscript" v-if="item.isFinish === '01'">已完成</view>
</view>
<view class="right_div">
<view class="title ellipsis-text">
{{ item.taskName }}
</view>
<view class="time_div">
起止时间
<text class="blod_color">
{{ item.startTm?.substr(0, 10) }}{{ item.endTm?.substr(0, 10) }}
</text>
</view>
<view class="people_progress_div">
<view class="mr-10">
完成人数
<text class="blod_color">{{ item.taskFinishSums ?? 0 }}/{{ item.taskSums ?? 0 }}</text>
</view>
<view>
我的进度
<text class="blod_color">{{ item.myProcess ?? 0 }}%</text>
</view>
</view>
</view>
</view>
</uv-swipe-action-item>
</uv-swipe-action>
<list-no-data v-if="total == 0 && status == 'nomore'"></list-no-data>
<uv-load-more v-if="total != 0" :status="status" loadmore-text="轻轻上拉加载" :height="30"></uv-load-more>
</scroll-view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue';
import { queryTraPersonalCollectionTaskPaging,deleteTraPersonalCollectionById } from '@/api/favorites.js';
import { setPageCache } from '@/common/common';
import common from '@/common/common';
const status = ref('loading'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const limit = 15;
const total = ref(-1);
const page = ref(0);
const data_list = reactive([]);
const options = [
{
text: '删除',
style: {
backgroundColor: '#f56c6c'
}
}
];
const getData = (from = '') => {
if (from == 'first') {
if (total.value !== -1) {
return;
}
status.value = 'loading';
total.value = -1;
page.value = 1;
data_list.length = 0;
} else {
if (status.value !== 'loadmore' && status.value !== '') return;
status.value = 'loading';
page.value++;
}
setTimeout(() => {
queryTraPersonalCollectionTaskPaging({
page: page.value,
limit: limit
})
.then((res) => {
total.value = res.total;
data_list.push(...res.body);
})
.finally(() => {
if (data_list.length >= total.value) {
status.value = 'nomore';
} else {
status.value = 'loadmore';
}
});
}, 200);
};
const swipeActionClick = (click_item) => {
const index = click_item['name'];
const item = data_list[index];
common.show('提示', '确认删除吗?').then((res) => {
if (!res) return;
common.loading();
const collectId = '11';
deleteTraPersonalCollectionById({ collectId: item.collectionId })
.then(() => {
data_list.splice(index, 1); // 从列表移除
total.value -= 1; // 更新下总数
})
.finally(() => {
common.hideLoading();
});
});
};
// 跳转到详情
const click_item = (item) => {
// tudo
if (item.isTimeOut === '00') {
common.msg('任务已结束');
return;
}
setPageCache('learning_tasks_details', item);
common.navigateTo(`/pages/learningTasks/details?collectId=${item.collectionId}`);
};
const getStatusClass = (index) => {
const remainder = index % 3;
if (remainder === 0) return 'in_progress';
if (remainder === 1) return 'completed';
return 'expired';
};
const scrolltolower = (item) => {
getData();
};
const refreshData = (params) => {
total.value = -1;
getData('first');
console.log('子组件refreshData');
};
defineExpose({
getData,
refreshData
});
</script>
<style scoped lang="scss">
.scroll_div {
background-color: #f1f5fa;
padding: 22rpx;
}
.scroll-Y {
height: calc(100vh - var(--status-bar-height) - 54rpx - 88rpx - 20rpx - 44rpx);
background-color: #fff;
border-radius: 16rpx;
}
.scroll-Y .item.in_progress {
// 进行中
.subscript {
color: #0066ff;
background-color: #daf0ff;
}
.blod_color {
color: #333333;
}
}
.scroll-Y .item.completed {
// 已完成
.subscript {
color: #ffffff;
background-color: #33c583;
}
.blod_color {
color: #333333;
}
}
.scroll-Y .item.expired {
// 已超时
.subscript {
color: #ffffff;
background-color: #ef5705;
}
.blod_color {
color: #d70c18;
}
}
.item-swipe-action {
margin: 32rpx 22rpx;
overflow: hidden;
:deep(.uv-swipe-action-item__right__button__wrapper) {
border-radius: 20rpx !important;
}
}
.scroll-Y .item {
height: 170rpx;
// margin: 32rpx 22rpx;
display: flex;
.img_div {
position: relative;
width: 224rpx;
height: 168rpx;
overflow: hidden;
border-radius: 22rpx;
.img {
width: 226rpx;
height: 168rpx;
}
.subscript {
position: absolute;
right: 0;
top: 0;
width: 92rpx;
height: 46rpx;
border-radius: 0px 0px 0px 22rpx;
font-family: PingFangSC;
font-weight: 400;
font-size: 22rpx;
line-height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
}
.right_div {
flex: 1;
overflow: hidden;
margin-left: 24rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
.title {
font-family: PingFangSC;
font-size: 30rpx;
font-weight: 600;
color: #333333;
line-height: 44rpx;
}
.time_div {
font-family: Arial;
font-weight: 400;
font-size: 23rpx;
color: #666;
line-height: 23rpx;
flex-wrap: nowrap;
height: 23rpx;
overflow: hidden;
}
.people_progress_div {
display: flex;
font-family: PingFangSC;
font-weight: 400;
font-size: 23rpx;
color: #666;
margin-bottom: 23rpx;
line-height: 23rpx;
flex-wrap: nowrap;
height: 23rpx;
overflow: hidden;
}
}
}
</style>
+58 -36
View File
@@ -9,14 +9,26 @@
</view>
<view class="detail-body">
<view class="body-tab">
<uv-tabs class="font_pf my_tabs" :current="tabAct" :list="tabList" @change="tabChange"
:scrollable="false" line-color="#0066FF"
:inactive-style="{ color: '#333333', fontSize: '30rpx', fontWeight:'600'}"
:activeStyle="{color:'#0066FF',fontSize:'30rpx', fontWeight:'600'}"
:lineColor="`url(${LineBg}) 10% 10%`"></uv-tabs>
<uv-tabs
class="font_pf my_tabs"
:current="tabAct"
:list="tabList"
@change="tabChange"
:scrollable="false"
line-color="#0066FF"
:inactive-style="{ color: '#333333', fontSize: '30rpx', fontWeight: '600' }"
:activeStyle="{ color: '#0066FF', fontSize: '30rpx', fontWeight: '600' }"
:lineColor="`url(${LineBg}) 10% 10%`"
></uv-tabs>
</view>
<view class="uni-margin-wrap no-overflow">
<swiper class="swiper" :current="tabAct" :indicator-dots="false" :disable-touch="true" @change="swiperChange">
<swiper
class="swiper"
:current="tabAct"
:indicator-dots="false"
:disable-touch="true"
@change="swiperChange"
>
<swiper-item>
<FavoritesCourse ref="courseRef"></FavoritesCourse>
</swiper-item>
@@ -30,30 +42,25 @@
</template>
<script setup>
import FavoritesTask from './components/favorites-task.vue'
import FavoritesCourse from './components/favorites-course.vue'
import FavoritesTask from './components/favoritesTask.vue';
import FavoritesCourse from './components/favoritesCourse.vue';
// 我的收藏
import {
reactive,
ref,
computed,
nextTick,
onMounted,
watch
} from 'vue';
import {
LineBg
} from '@/enum.js'
import { reactive, ref, computed, nextTick, onMounted, watch } from 'vue';
import { onShow, onLoad, onUnload } from '@dcloudio/uni-app';
import { LineBg } from '@/enum.js';
import common from '@/common/common';
const statusBarHeight = uni.getSystemInfoSync().statusBarHeight;
const courseRef = ref(null);
const taskRef = ref(null);
const tabAct = ref(0);
const tabList = ref([{
name: '课程收藏'
}, {
name: '任务收藏'
}]);
const tabList = ref([
{
name: '课程收藏'
},
{
name: '任务收藏'
}
]);
const tabChange = (item) => {
tabAct.value = item.index;
@@ -62,23 +69,38 @@
tabAct.value = item.detail.current;
};
onMounted(() => {
watch(tabAct, (newValue, oldValue) => {
if (newValue === 0) {
courseRef.value?.getData('first')
} else if (newValue === 1) {
taskRef.value?.getData('first')
watch(
tabAct,
(newValue, oldValue) => {
if (newValue === 0) {
courseRef.value?.getData('first');
} else if (newValue === 1) {
taskRef.value?.getData('first');
}
},
{
immediate: true
}
}, {
immediate: true
}) // 这里的immediate会在onMounted之后执行
})
); // 这里的immediate会在onMounted之后执行
});
onLoad(async () => {
uni.$on('refresh_favorites_data', (params) => {
if (params.type === 'course') {
courseRef.value?.refreshData(params);
} else if (params.type === 'task') {
taskRef.value?.refreshData(params);
}
});
});
onUnload(() => {
uni.$off('refresh_favorites_data');
});
</script>
<style lang="scss" scoped>
$bg-margin-left: 50rpx;
.my_tabs {
// 解决这个圆角的图片,开穿透
:deep(.uv-tabs__wrapper__nav__line) {
height: 26rpx !important;
@@ -111,7 +133,7 @@
}
.detail-main {
background-color: #F6F8FF;
background-color: #f6f8ff;
overflow: hidden;
}
@@ -164,4 +186,4 @@
}
}
}
</style>
</style>
+65 -41
View File
@@ -79,21 +79,35 @@
{{ item.crsName }}
</view>
<view class="button_div">
<template v-if="item.isFinishStudy === '01' && item.isFinishExam === '01' && item.status !== '02'">
<template
v-if="item.isFinishStudy === '01' && item.isFinishExam === '01' && item.status !== '02'"
>
<image class="img" :src="lockIcon('#999999')"></image>
<view class="button_text error">暂无通过条件可自动解锁</view>
</template>
<template v-if="item.isFinishStudy === '01' && item.isFinishExam === '01' && item.status === '02'">
<template
v-if="item.isFinishStudy === '01' && item.isFinishExam === '01' && item.status === '02'"
>
<image class="img" :src="lockIcon('#0066FF')"></image>
<view class="button_text success">已自动解锁</view>
</template>
<template v-if="item.isFinishStudy !== '01'">
<image class="img" :src="passTheExamIcon(item.isFinishStudy==='02'?'#0066FF':'#999999')"></image>
<view class="button_text" :class="item.isFinishStudy==='02'?'success':'error'">完成练习</view>
<image
class="img"
:src="passTheExamIcon(item.isFinishStudy === '02' ? '#0066FF' : '#999999')"
></image>
<view class="button_text" :class="item.isFinishStudy === '02' ? 'success' : 'error'">
完成练习
</view>
</template>
<template v-if="item.isFinishExam !== '01'">
<image class="img" :src="completeTheExercisesIcon(item.isFinishExam==='02'?'#0066FF':'#999999')"></image>
<view class="button_text" :class="item.isFinishExam==='02'?'success':'error'">通过考试</view>
<image
class="img"
:src="completeTheExercisesIcon(item.isFinishExam === '02' ? '#0066FF' : '#999999')"
></image>
<view class="button_text" :class="item.isFinishExam === '02' ? 'success' : 'error'">
通过考试
</view>
</template>
</view>
</view>
@@ -112,13 +126,14 @@
</template>
<script setup>
import { onLoad } from '@dcloudio/uni-app';
import { onLoad, onUnload } from '@dcloudio/uni-app';
import { ref, reactive, computed, nextTick } from 'vue';
import { queryAllTraTaskCrsInfo } from '@/api/learningTasks.js';
import { passTheExamIcon, completeTheExercisesIcon, lockIcon } from '@/common/imgSvg';
import { insertTraPersonalCollectionTask, deleteTraPersonalCollectionById } from '@/api/favorites.js';
import common from '@/common/common';
import { setPageCache, getPageCache } from '@/common/common';
const collectId = ref(''); // 记录初始的收藏id
const detailData = reactive({
taskId: '',
taskName: '',
@@ -140,10 +155,8 @@
};
const getData = () => {
status.value = 'loading';
queryAllTraTaskCrsInfo({
taskId: detailData.taskId
// taskId: 'TASKINFO02506401613920250820161715000008095'
})
.then((res) => {
data_list.push(...res.body);
@@ -166,12 +179,11 @@
}
common.navigateTo('/pages/courseDetail/index?crsId=' + item.crsId);
};
const collect_click = async (collectId) => {
console.log('学习', collectId);
const collect_click = async (id) => {
console.log('学习', id);
try {
if (!collectId) {
if (!id) {
common.loading('收藏中');
const res = await insertTraPersonalCollectionTask({
collectTyp: '02',
collectBusId: detailData.taskId
@@ -181,7 +193,7 @@
} else {
common.loading('取消收藏中');
await deleteTraPersonalCollectionById({
collectId: collectId
collectId: id
});
detailData.collectionId = null;
@@ -192,14 +204,29 @@
}
};
onLoad((e) => {
collectId.value = e.collectId || '';
const learning_tasks_details = getPageCache('learning_tasks_details');
Object.assign(detailData, {
...learning_tasks_details
});
nextTick(() => {
getData();
});
console.log('detailData', detailData);
getData();
});
onUnload(() => {
if (detailData.collectionId !== collectId.value) {
const pages = getCurrentPages();
if (pages.length >= 2) {
const prevPage = pages[pages.length - 2];
if (prevPage.route === 'pages/favorites/index') {
// 更新
uni.$emit('refresh_favorites_data', {
type: 'task',
state: !!detailData.collectionId, // 最终是收藏了还是取消了
old: collectId.value, // 进入时候的收藏id
new: detailData.collectionId // 新的收藏id
});
}
}
}
});
</script>
@@ -221,59 +248,58 @@
}
.status_div {
color: #adb4c3;
background-color: #F3F5F5;
background-color: #f3f5f5;
}
.plug{
.plug {
border-top: 4rpx solid #e1e8f0;
}
}
//未完成
&.unfinished {
.circle_div {
border: 4rpx solid #0066FF;
border: 4rpx solid #0066ff;
// background-color: #bdc7e6;
}
.line_div {
border-left: 4rpx solid #0066FF;
border-left: 4rpx solid #0066ff;
}
.status_div {
color: #FFFFFF;
background-color: #4C93FF;
color: #ffffff;
background-color: #4c93ff;
}
.plug{
border-top: 4rpx solid #0066FF;
.plug {
border-top: 4rpx solid #0066ff;
}
}
// 未解锁
&.not_unlocked {
.circle_div {
border: 4rpx solid #ADB4C3;
border: 4rpx solid #adb4c3;
}
.line_div {
border-left: 4rpx dashed #ADB4C3;
border-left: 4rpx dashed #adb4c3;
}
.status_div {
color: #6C9CE4;
background-color: #EFF6FF;
color: #6c9ce4;
background-color: #eff6ff;
}
.plug{
border-top: 4rpx solid #ADB4C3;
.plug {
border-top: 4rpx solid #adb4c3;
}
}
// 最后一项
&:last-child .plug{
&:last-child .plug {
width: 80%;
height: 40rpx;
margin-top: -6rpx;
}
.progress_div {
width: 30rpx;
display: flex;
align-items: center;
flex-direction: column;
.circle_div {
width: 20rpx;
height: 20rpx;
@@ -338,18 +364,16 @@
.button_text {
font-weight: 400;
font-size: 24rpx;
text-align: left;
margin-right: 50rpx;
&.success{
color: #0066FF;
&.success {
color: #0066ff;
}
&.error{
&.error {
color: #999999;
}
}
}
}
}
+599 -588
View File
File diff suppressed because it is too large Load Diff
+63 -100
View File
@@ -15,8 +15,10 @@
<text class="grey">/{{ storageTopicList.length }}</text>
</view>
<view class="question">
<view class="grey">剩余时间</view>
<uv-count-down :time="100 * 1000" format="HH:mm:ss"></uv-count-down>
<view class="grey">练习时长</view>
<view class="">
{{ formatSeconds(practiceTime) }}
</view>
</view>
</view>
<view class="progress">
@@ -49,10 +51,6 @@
@subject_click="subject_click"
v-model="item.my_answer"
></Subject>
<!-- <CharactersSubject :item="item" v-if="item.qnsTyp === 'ES'" :mode="mode"
:activeVoiceId="activeVoiceId" :voicePathValue="voicePathValue"
@subject_click="subject_click">
</CharactersSubject> -->
</scroll-view>
</view>
</swiper-item>
@@ -84,10 +82,10 @@
import Subject from '@/components/examination/subject.vue';
import CharactersSubject from '@/components/examination/characters-subject.vue';
import TouchBtn from '@/components/examination/touchBtn.vue';
import { ref, reactive, computed, onMounted, nextTick } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { ref, reactive, computed, onMounted, nextTick, onUnmounted } from 'vue';
import { onLoad, onHide, onShow, onUnload } from '@dcloudio/uni-app';
import common from '@/common/common';
import { getPageCache } from '@/common/common';
import { setPageCache, formatSeconds, getPageCache } from '@/common/common';
import { commonUploadVoiceFile } from '@/api/common.js';
import { optionErrorIcon, examinationSheetIcon } from '@/common/imgSvg';
import { practiceAnswer, practiceCommit, queryCrsPracticeAnswerResult } from '@/api/practice.js';
@@ -95,7 +93,11 @@
const feedbackRef = ref(null);
const dialogRef = ref(null);
const touchBtnRef = ref(null);
const practiceTime = ref(0); // 当前练习时间
let practiceStartTime = 0; // 练习开始时间
let practiceTimeTimer = null; // 练习计时器
let leaveDuration = 0; // 当前总计离开时间
let leaveStartTime = 0; // 离开开始时间
const mode = ref('practice'); // 考试 examination 练习practice
const paperData = reactive({
exrId: '',
@@ -109,20 +111,19 @@
const systemInfo = uni.getSystemInfoSync();
const topic = computed({
get: () => topicList[questionNumber.value],
set: (val) => topicList[questionNumber.value] = val
set: (val) => (topicList[questionNumber.value] = val)
});
const showTouchBtn = computed(() => {
return topic.value?.qnsTyp === 'ES';
});
// 下面语音组件的禁用状态判断
const touchIsDisabled = computed(() => {
return topic.value?.es_status !== '' && topic.value?.es_status !== undefined
})
const touchIsDisabled = computed(() => {
console.log('下面语音组件的禁用状态判断', topic.value?.es_status !== '' && topic.value?.es_status !== undefined);
return topic.value?.es_status !== '' && topic.value?.es_status !== undefined;
});
const progress = computed(() => {
const progress = computed(() => {
// 计算进度百分比(保留两位小数)
const total = storageTopicList.value.length;
if (total === 0) return 0;
@@ -132,8 +133,6 @@
// 弹出框配置
const dialogConfiguration = reactive({});
const activeVoiceId = ref('');
const swiperChange = (item) => {
questionNumber.value = item.detail.current;
};
@@ -280,7 +279,7 @@
touchBtnRef.value?.clearinputText(old_text); // 清除发送框数据
// 处理失败情况
});
// const traQnsInfoVo = body.traQnsInfoVo;
// topic.analyContent = traQnsInfoVo.analyContent; // 答案解析
// console.log('答题结果', traQnsInfoVo);
@@ -294,41 +293,26 @@
})
.catch(() => {
topic.es_status = ''; // 取消转圈
});
};
// 录音组件提交
const touchBtnSubmit = (type, submitObj) => {
const old_text = touchBtnRef.value?.clearinputText(); // 清除发送框数据
if (type === 'voice') {
topic.value.es_status = '00'; // 00转圈
const voicePath = submitObj;
commonUploadVoiceFile(voicePath, '/traask/traAskchat/voiceTrans', {})
.then((res) => {
let _res = JSON.parse(res.data);
console.log('res小行星', _res);
if (_res.rtnCode === '0000') {
topic.value.es_status = '02'
topic.value.es_status = '02';
topic.value.my_answer = {
anserResult: _res.body.transContent,
ossKey: _res.body.fileId,
ossAddr: _res.body.ossFileAddr,
voicePath: voicePath
}
// {
// "chatId":"CHAT025018122068202508232000590000000000000000000000000000000060",
// "fileId":"S3F0250181220722025082320005900000676612",
// "ossFileAddr":"http://25.18.122.66::9786/traoss/show//S3F0250181220722025082320005900000676612",
// "transContent":"一二三四。"
// }
// submitProblemEssayQuestion({
// anserResult: _res.body.transContent,
// ossKey: _res.body.fileId,
// ossAddr: _res.body.ossFileAddr,
// voicePath: voicePath
// });
};
}
})
.catch((err) => {
@@ -372,6 +356,8 @@
my_answer: []
}));
addProblem();
// 记录起始时间
practiceStartTime = new Date().getTime();
// #ifdef APP-PLUS
// 安全区域底部到屏幕底部的高度(即底部安全区域高度)
const safeAreaBottomHeight = systemInfo.screenHeight - systemInfo.safeArea.bottom;
@@ -386,7 +372,7 @@
});
// #endif
});
const result_click = ({ key }) => {
console.log('res', key);
if (key === 'hand_in_paper') {
@@ -401,8 +387,6 @@
// 交卷按钮点击,打开交卷确认框
const hand_in_paper_button_click = () => {
console.log('点击交卷', paperData);
// console.log('点击交卷topicList', topicList);
// return;
// 判断是否完成所有题目
const completeList = topicList
.filter((res) => !!(Array.isArray(res.my_answer) ? res.my_answer.length : res.my_answer))
@@ -461,59 +445,42 @@
};
// 执行交卷
const hand_in_paper = () => {
const answerDtoList = topicList.map((res) => {
let answerItem = {
examId: paperData.examId,
papersId: paperData.papersId,
qnsId: res.qnsId,
ossKey: '',
anserResult: ''
};
if (res.qnsTyp === 'ES') {
// 问答题特殊处理
return {
...answerItem,
anserResult: ''
};
} else {
//其他题
return {
...answerItem,
anserResult: res.my_answer.join(',')
};
}
});
console.log('answerDtoList', answerDtoList);
dialogRef.value.close();
common.loading('正在提交');
commitPaperExam({
examId: paperData.examId,
execId: paperData.execId || '1',
examLenTm: 1200,
answerDtoStr: JSON.stringify(answerDtoList)
})
.then((res) => {
console.log('提交成功', res);
// 判断是否需要查分
if (res.body.flagQuery === 'Y') {
// 需要查分
const examLenTm = 678;
common.redirectTo(
`/pages/examination/result?execId=${res.body.execId}&examLenTm=${examLenTm}&papersName=${paperData.papersName}`
);
} else {
// todo 此处应该有个弹窗交代下结果
common.navigateBack();
}
})
.catch((res) => {
console.log('error', res);
})
.finally(() => {
common.hideLoading().body;
});
};
// 启动定时器
const startTimer = () => {
if (practiceTimeTimer) {
clearInterval(practiceTimeTimer);
}
practiceTimeTimer = setInterval(() => {
const currentTime = Date.now();
const elapsedSeconds = Math.floor((currentTime - practiceStartTime - leaveDuration) / 1000);
practiceTime.value = elapsedSeconds;
}, 1000);
};
// 暂停定时器
const stopTimer = () => {
if (practiceTimeTimer) {
clearInterval(practiceTimeTimer);
practiceTimeTimer = null;
}
};
onShow(() => {
if (leaveStartTime) {
const time = Date.now() - leaveStartTime;
leaveDuration += time;
leaveStartTime = 0; // 重置离开开始时间
}
startTimer(); // 启动/恢复定时器
});
onHide(() => {
leaveStartTime = new Date().getTime();
stopTimer();
});
onUnload(() => {
stopTimer();
});
</script>
<style scoped lang="scss">
@@ -744,17 +711,13 @@
align-items: center;
// background-color: red;
width: 242rpx;
color: red;
.grey {
margin-right: 8rpx;
}
:deep(.uv-count-down__text) {
color: #ffffff !important;
font-weight: 600;
font-size: 26rpx !important;
}
color: #ffffff;
font-weight: 600;
font-size: 26rpx;
}
.grey {
@@ -1,5 +1,5 @@
<template>
<view class="scroll_div">
<view class="scroll_div content">
<scroll-view scroll-y="true" class="scroll-Y" @scrolltolower="scrolltolower">
<view class="item" :class="getStatusClass(index)" v-for="(item,index) in data_list">
<view class="top">
@@ -90,6 +90,7 @@
</script>
<style scoped lang="scss">
.scroll_div {
background-color: #F1F5FA;
padding: 22rpx;
@@ -1,10 +1,10 @@
<template>
<view class="scroll_div">
<scroll-view :scroll-y="true" class="scroll-Y" :show-scrollbar="false" @scrolltolower="scrolltolower">
<!-- <view class="item" :class="getStatusClass(index)" v-for="(item, index) in data_list">
</view> -->
<subject v-for="(item, index) in data_list" :item="item" mode="mistake"></subject>
<scroll-view :scroll-y="true" class="scroll-Y " :show-scrollbar="false" @scrolltolower="scrolltolower">
<view class="content" v-for="(item, index) in data_list">
<subject :item="item" mode="mistake" :isPreview="true" :clearResult="false"></subject>
</view>
<list-no-data v-if="total == 0 && status == 'nomore'"></list-no-data>
<uv-load-more v-if="total != 0" :status="status" loadmore-text="轻轻上拉加载" :height="30"></uv-load-more>
</scroll-view>
</view>
@@ -67,14 +67,17 @@
</script>
<style scoped lang="scss">
.scroll_div {
background-color: #f1f5fa;
padding: 22rpx;
$bg-margin-left: 30rpx;
.content {
position: relative;
margin: 32rpx $bg-margin-left;
border-radius: 16rpx;
padding: 34rpx 30rpx 34rpx 36rpx;
background-color: #fff;
// min-height: calc(100vh - var(--status-bar-height) - 54rpx - 88rpx - 20rpx - 44rpx - 156rpx);
}
.scroll-Y {
height: calc(100vh - var(--status-bar-height) - 54rpx - 88rpx - 20rpx - 44rpx - 156rpx);
// background-color: #fff;
background-color: #f1f5fa;
min-height: calc(100vh - var(--status-bar-height) - 78rpx - 88rpx - 156rpx);
}
</style>
+1 -1
View File
@@ -128,7 +128,7 @@
}
.swiper {
height: calc(100vh - var(--status-bar-height) - 54rpx - 88rpx - 20rpx - 10rpx - 156rpx);
height: calc(100vh - var(--status-bar-height) - 78rpx - 88rpx - 156rpx);
}
.swiper-item {