Files
fe-student/src/views/growth/growthRoadmap.vue
Pengxiansen d430c39270 提交
2025-02-19 15:40:45 +08:00

709 lines
18 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>
<div class="growth-list">
<img
style="width: 100%; height: 290px"
src="@/assets/image/growth/bg.png"
/>
<div class="nav-title">
<el-dropdown :teleported="false" trigger="click" ref="dropdownRef">
<div class="growth-name">
<div>{{ selectGrowth?.growthName }}</div>
<div style="margin-left: 20px; cursor: pointer">
<el-icon color="#fff" size="24"><CaretBottom /></el-icon>
</div>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
:disabled="selectGrowth.id == item.id"
v-for="item of growthList"
>
<div @click="select(item)">{{ item.growthName }}</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<el-affix :offset="0">
<div class="nav">
<div class="tabs">
<div
class="tabs-item"
:class="queryParams.type == 1 ? 'active' : ''"
@click="tabClick(1)"
>
<div class="tabs-text">必修</div>
<div class="tabs-line"></div>
</div>
<div
class="tabs-item"
:class="queryParams.type == 2 ? 'active' : ''"
@click="tabClick(2)"
>
<div class="tabs-text">选修</div>
<div class="tabs-line"></div>
</div>
</div>
</div>
<div class="growth-path-container">
<div class="growth-name-type">
<div class="growth-type">
<div class="type-item-list" @click="templateClick">列表</div>
<div class="type-item-path">路径</div>
</div>
</div>
<template v-if="stageProcessList && stageProcessList.length">
<div
:style="{ transform: 'scale(' + transformSize + ')' }"
style="transform-origin: top left"
>
<template v-if="selectGrowth.template == 1">
<Roadmap2
@toFinish="toFinish"
:stageProcessList="stageProcessList"
></Roadmap2>
</template>
<template v-else>
<Roadmap1
@click="toFinish"
:stageProcessList="stageProcessList"
></Roadmap1>
</template>
</div>
</template>
<template v-else>
<el-empty description="暂无数据" />
</template>
</div>
</el-affix>
<!-- 弹框提示信息 -->
<el-dialog
title=""
top="347px"
v-model="dialogVisible"
:show-close="false"
style="
display: flex;
justify-content: center;
align-items: center;
height: 283px;
padding: 0;
border-radius: 4px;
"
width="502px"
>
<div
style="width: 288px; color: #333333; font-size: 22px; font-weight: 600"
>
{{ dialogVisibleTip }}
</div>
<span slot="footer" style="display: inline-block; margin-top: 60px">
<el-button
@click="dialogVisible = false"
style="width: 140px; height: 40px; margin-right: 22px"
>取消</el-button
>
<el-button
type="primary"
@click="dialogVisible = false"
style="width: 140px; height: 40px"
>确定</el-button
>
</span>
</el-dialog>
<!-- 开课列表弹框 -->
<el-dialog
title=""
top="347px"
v-model="openCourseVisible"
:show-close="false"
style="
display: flex;
justify-content: center;
align-items: center;
min-height: 320px;
padding: 0;
border-radius: 4px;
"
width="502px"
>
<div
style="
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 22px;
"
>
<div
style="
width: 288px;
color: #333333;
font-size: 16px;
font-weight: 600;
"
>
开课列表
</div>
<div
@click="openCourseVisible = false"
style="font-size: 12px; cursor: pointer"
>
X
</div>
</div>
<div style="width: 100%; min-height: 210px; margin-top: 12px">
<div
v-for="(item, key) in openCourseList"
style="
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
background: rgb(247, 251, 253);
height: 40px;
padding: 5px;
border-radius: 5px;
"
>
<div
style="
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 320px;
"
>
{{ item }}
</div>
<div
@click="toOffcoursePlanPage(openCourseIdList[key])"
style="
width: 60px;
height: 30px;
text-align: center;
line-height: 30px;
background: #0078fc;
border-radius: 5px;
color: #fff;
cursor: pointer;
"
>
去上课
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { computed, onMounted, onUnmounted, ref, watch, reactive } from "vue";
import { ElMessage } from "element-plus";
import { request } from "@/api/request";
import { growthRequest } from "@/api/growthRequest";
import Roadmap1 from "./components/roadmap1/index.vue";
import Roadmap2 from "./components/roadmap2/index.vue";
import {
EvaluationToLearn,
QueryEvaluationTaskStatusOne,
STUDY_RECORD,
SubmitExternalExam,
PROFESSIONAL_STUDENT_TASKLIST,
PROFESSIONAL_STUDENT_LIST,
PROFESSIONAL_STUDENT_LEARN,
} from "@/api/api";
import { useRoute, useRouter } from "vue-router";
import { GROWTH, TASK_TYPES } from "@/api/CONST";
import { useStore } from "vuex";
const {
query: { courseId, routerId },
} = useRoute();
const router = useRouter();
const { commit, dispatch, state } = useStore();
const userInfo = computed(() => state.userInfo);
const errorData = computed(() => state.projectError);
// 储存屏幕宽
const windowWidth = ref(0);
const transformSize = ref(1);
const updateWindowWidth = () => {
windowWidth.value = window.innerWidth;
transformSize.value = windowWidth.value / 1920;
};
// 样式类型
const templateClick = () => {
router.push({
path: "/growthList",
query: {
routerId: selectGrowth.value.id,
},
});
};
// 查询条件
const queryParams = reactive({
type: 1,
});
const tabClick = (type, status) => {
// 选修/必修
if (type) {
queryParams.type = type;
}
// 任务状态
if (status) {
queryParams.completionStatus = status;
}
getList();
};
// 学习任务列表
const stageProcessList = ref([]);
// 是否加载数据中
const loading = ref(false);
const getList = () => {
let params = {
growthId: selectGrowth.value.id ? selectGrowth.value.id : routerId,
...queryParams,
};
// 3为查询全部状态需给后端传空
if (params.completionStatus == 3) {
params.completionStatus = "";
}
loading.value = true;
growthRequest(PROFESSIONAL_STUDENT_TASKLIST, params).then((res) => {
if (selectGrowth.value.template === "1") {
stageProcessList.value = res.data;
} else {
let newData = res.data.slice(0, 15).reverse();
// 默认第一个在第一点位
let num = 15 / newData.length;
stageProcessList.value = newData.map((item, index) => {
// 默认第一个在第一点位
if (index === 0) {
item.position = 0;
}
item.position = Number((index * num).toFixed());
return item;
});
}
loading.value = false;
});
};
// 专业力必修列表
const growthList = ref([]);
// 当前浏览的专业力必修任务
const selectGrowth = ref({});
// 切换专业力必修
const dropdownRef = ref(null);
const select = (item) => {
selectGrowth.value = item;
dropdownRef.value.handleClose();
getList();
};
onMounted(() => {
updateWindowWidth(); // 初始化宽度
window.addEventListener("resize", updateWindowWidth);
dispatch("getGrowthInfo", { routerId });
getList();
growthRequest(PROFESSIONAL_STUDENT_LIST).then((res) => {
growthList.value = res.data;
});
});
// 在组件卸载时移除事件监听器
onUnmounted(() => {
window.removeEventListener("resize", updateWindowWidth);
});
watch(
() => state.growthInfo,
(val) => {
selectGrowth.value = val;
}
);
watch(
() => errorData.value,
(val) => {
if (val.data == null) {
ElMessage.error(val.msg);
window.parent.postMessage(
{ type: "navigate", path: "/uc/study/task" },
"*"
);
}
},
{ deep: true }
);
const openCourseVisible = ref(false);
const openCourseList = ref([]);
const openCourseIdList = ref([]);
const dialogVisible = ref(false);
const dialogVisibleTip = ref("该任务无法学习,请联系管理员进行替换!");
async function toFinish(d) {
if (d.completionStatus == 10) {
ElMessage.warning(`请先完成“${d.superTaskName}”的学习任务`);
return;
}
//更新学员当前任务
await growthRequest(PROFESSIONAL_STUDENT_LEARN, {
growthId: routerId,
taskId: d.taskId,
});
if (d.courseType === "2") {
if (!d.targetId) {
return ElMessage.error("还未添加开课,请联系管理员!");
}
if (d.targetId.split(",").length > 1) {
openCourseList.value = d.targetName?.split(",");
openCourseIdList.value = d.targetId?.split(",");
openCourseVisible.value = true;
return;
}
}
// 作业过期判断
if (d.courseType == 4) {
let date1 = new Date(d.info.submitEndTime).getTime();
let date2 = new Date().getTime();
if (date1 < date2) {
dialogVisibleTip.value = "当前作业已结束";
dialogVisible.value = true;
//return
}
}
// 直播结束时间
if (d.courseType == 6) {
let date1 = new Date(d.info.liveEndTime).getTime();
let date2 = new Date().getTime();
if (date1 < date2) {
dialogVisibleTip.value = "当前直播已结束";
dialogVisible.value = true;
//return
}
}
// 考试 停用
if (d.courseType == 5) {
// 此处判断外部考试跳转
if (d.info.examType == 2) {
// 点击即更新状态 进行中
request(SubmitExternalExam, {
type: 1,
taskId: d.id,
chapterId: 0,
externalId: d.courseId,
externalName: d.taskName,
targetId: selectGrowth.value.id,
studentNo: userInfo.value.userNo,
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
router.push({
path: "/externalexamination",
query: {
id: d.id,
type: GROWTH,
infoId: selectGrowth.value.id,
courseId: d.courseId ? d.courseId : d.info.id,
pName: selectGrowth.value.growthName,
sName: d.taskName,
chapterOrStageId: 0,
exname: d.info.examinationTestName, // 考试名称
},
});
return;
}
}
// 其他活动 结束时间
if (d.courseType == 9) {
let date1 = new Date(d.info.activityEndTime).getTime();
let date2 = new Date().getTime();
if (date1 < date2) {
dialogVisibleTip.value = "当前活动已结束";
dialogVisible.value = true;
//return
}
}
// 测评模块 请求接口跳转新的页面 - 新增 暂时未调试 目前无测评数据 2023-02-04
if (d.courseType == 10) {
if (d.completionStatus != 1) {
// 肯定没有完成测评
// 调用接口 跳转页面
request(EvaluationToLearn, {
businessType: "project",
chapterId: 0,
courseId: d.courseId,
quizKid: d.info.evaluationTypeId,
routerOrProjectId: routerId,
studentId: userInfo.value.id,
studentName: userInfo.value.realName,
})
.then((res) => {
console.log(res);
if (res.code == 200) {
let jumpUrl = res.data.quizUrl;
// 此处写跳转url
window.open(jumpUrl, "_top");
}
})
.catch((err) => {
console.log(err);
});
return;
} else {
// 进行中 或者 已完成
// 调用接口 判断当前测评状态 跳转页面
console.log("我是查询测评跳转链接所传递得参数", {
quizTaskId: d.quizTaskId,
});
request(QueryEvaluationTaskStatusOne, {
quizTaskId: d.quizTaskId,
})
.then((res) => {
console.log(res);
if (res.code == 200) {
if (res.data.complete_status == 2) {
ElMessage.error("您已完成测评");
return;
} else {
// 重新查询跳转
// 调用接口 跳转页面
console.log("我是查询测评跳转链接所传递得参数", {
businessType: "project",
chapterId: 0,
courseId: d.courseId,
quizKid: d.info.evaluationTypeId,
routerOrProjectId: routerId,
studentId: userInfo.value.id,
studentName: userInfo.value.realName,
});
request(EvaluationToLearn, {
businessType: "project",
chapterId: 0,
courseId: d.courseId ? d.courseId : d.info.id,
quizKid: d.info.evaluationTypeId,
routerOrProjectId: routerId,
studentId: userInfo.value.id,
studentName: userInfo.value.realName,
})
.then((res) => {
console.log(res);
if (res.code == 200) {
let jumpUrl = res.data.quizUrl;
// 此处写跳转url
window.open(jumpUrl, "_top");
}
})
.catch((err) => {
console.log(err);
});
return;
}
}
})
.catch((err) => {
console.log(err);
});
return;
}
}
if (!TASK_TYPES.path[d.courseType]) {
ElMessage.error("暂时未开放");
return;
}
if (
d.courseType == 3 ||
d.courseType == 7 ||
(d.courseType == 5 && d.examType == 2)
) {
await request(STUDY_RECORD, {
studentId: userInfo.value.id,
targetId: selectGrowth.value.id,
logo: GROWTH,
type: GROWTH,
stageOrChapterId: 0,
taskId: d.id,
taskType: d.courseType,
});
}
if (typeof TASK_TYPES.path[d.courseType] === "string") {
TASK_TYPES.path[d.courseType] &&
TASK_TYPES.path[d.courseType].startsWith("http") &&
window.open(TASK_TYPES.path[d.type] + d.targetId, "_top");
TASK_TYPES.path[d.courseType] &&
TASK_TYPES.path[d.courseType].startsWith("/") &&
router.push({
path: TASK_TYPES.path[d.courseType],
query: {
id: d.id,
type: d.courseType === "11" ? "5" : GROWTH,
projectId: d.courseId,
infoId: d.courseType === "11" ? d.taskId : selectGrowth.value.id,
courseId: d.courseId ? d.courseId : d.info.id,
pName: selectGrowth.value.growthName,
sName: d.taskName,
chapterOrStageId: 0,
},
});
} else if (typeof TASK_TYPES.path[d.courseType] === "function") {
if (d.courseType == 5) {
let params = {
examType: d.info.examType,
};
window.open(
TASK_TYPES.path[d.courseType](params) + d.info.examinationPaperId,
"_top"
);
} else {
let params = {
courseId: d.courseId ? d.courseId : d.info.id,
targetId: d.targetId ? d.targetId : "",
};
TASK_TYPES.path[d.courseType](params);
}
}
}
function toOffcoursePlanPage(id) {
window.open(
`${location.protocol}//${location.host}${
import.meta.env.VITE_BASE_API
}/stu/project/redirectDetail?courseId=${id}`,
"_top"
);
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
.f-a-c {
display: flex;
align-items: center;
}
.growth-list {
background: #f6f6fc;
:deep(.el-popper) {
z-index: 9999 !important;
}
:deep(.el-progress-bar__outer) {
background-color: #aeb3b8;
}
.nav-title {
position: absolute;
top: 75px;
margin-left: 55px;
.growth-name {
display: flex;
align-items: center;
font-weight: 700;
font-size: 24px;
color: #fff;
cursor: pointer;
}
}
.nav {
display: flex;
justify-content: space-between;
align-items: center;
height: 80px;
background: #ffffff;
padding: 0 80px 0 62px;
width: 1920px;
}
.growth-name-type {
display: flex;
align-items: center;
justify-content: flex-end;
padding: 32px 80px 14px 80px;
}
.growth-name {
display: flex;
align-items: center;
font-weight: 700;
font-size: 20px;
color: #000000;
cursor: pointer;
}
.growth-path-container {
background-color: #ffffff;
}
.growth-type {
display: flex;
align-items: baseline;
.type-item-list,
.type-item-path {
display: flex;
justify-content: center;
align-items: center;
width: 110px;
height: 40px;
font-size: 16px;
font-weight: 700;
}
.type-item-list {
background: #ffffff;
border-radius: 6px 0px 0px 6px;
border: 1px solid #e3e5eb;
color: #666666;
cursor: pointer;
}
.type-item-path {
background: #0077ec;
border-radius: 0px 6px 6px 0px;
color: #ffffff;
}
}
.tabs {
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
.tabs-item {
display: flex;
align-items: center;
flex-direction: column;
font-size: 20px;
color: #666666;
cursor: pointer;
font-weight: 700;
padding: 0 18px;
}
.tabs-line {
width: 96px;
height: 3px;
margin-top: 7px;
}
.active {
color: #0077ec;
font-size: 24px;
.tabs-line {
background-color: #1379f7;
}
}
}
.el-dialog__body {
width: 80%;
}
.el-dialog__header {
display: none;
}
}
</style>