This commit is contained in:
Pengxiansen
2025-02-16 17:36:15 +08:00
parent 8daf1d5374
commit ab45b0aaa6
10 changed files with 836 additions and 986 deletions

View File

@@ -60,6 +60,9 @@ function getUserInfo() {
}
</script>
<style lang="scss">
body::-webkit-scrollbar {
display: none;
}
#app {
// font-family: MicrosoftYaHei, Microsoft YaHei, Avenir, Helvetica, Arial,
// sans-serif;

View File

@@ -142,3 +142,5 @@ export const PROFESSIONAL_STUDENT_DETAIL = id => `${baseUrl}/professional/studen
export const PROFESSIONAL_STUDENT_TASKLIST = `${baseUrl}/professional/student/studentTaskList`
//记录当前学习任务
export const PROFESSIONAL_STUDENT_LEARN = `${baseUrl}/professional/student/learnCourse`
//专业力必修任务列表
export const PROFESSIONAL_STUDENT_LIST = `${baseUrl}/professional/student/studentGrowthList`

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +1,17 @@
<template>
<div
style="
background: #0078fc;
height: 150px;
width: 100%;
position: absolute;
top: 0;
z-index: -9999;
"
></div>
<div class="growth-roadmap pathdetails" style="padding: 30px">
<div class="pdname">
<div>{{ data?.growthName }}</div>
<div
v-if="data.description"
style="font-size: 16px; font-weight: 600; margin-top: 10px"
>
简介{{ data.description }}
</div>
</div>
<div class="container">
<div class="growth-list">
<img
style="width: 100%; height: 290px"
src="@/assets/image/growth/bg.png"
/>
<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-text">我的必修</div>
<div class="tabs-line"></div>
</div>
<div
@@ -35,168 +19,182 @@
:class="queryParams.type == 2 ? 'active' : ''"
@click="tabClick(2)"
>
<div class="tabs-text">选修</div>
<div class="tabs-text">我的选修</div>
<div class="tabs-line"></div>
</div>
</div>
<div
class="path-container"
v-loading="loading"
:style="stageProcessList.length ? '' : 'height:400px'"
<!-- <div style="display: flex">
<el-select
style="width: 230px"
:teleported="false"
v-model="queryParams.completionStatus"
placeholder="搜索学习状态"
>
<el-option label="全部" value="3"> </el-option>
<el-option label="未开始" value="0"> </el-option>
<el-option label="已完成" value="1"> </el-option>
<el-option label="进行中" value="2"> </el-option>
</el-select>
<el-input
style="margin-left: 9px; width: 230px"
v-model="queryParams.taskName"
placeholder="搜索名称"
clearable
/>
<el-button
icon="Search"
style="margin-left: 20px"
type="primary"
@click="getList"
>搜索</el-button
>
<el-button icon="Refresh" style="margin-left: 20px" @click="refresh"
>清除</el-button
>
</div> -->
</div>
<div class="growth-path-container">
<div class="growth-name-type">
<div class="growth-name">
<div>{{ selectGrowth?.growthName }}</div>
<el-dropdown :teleported="false" trigger="click" ref="dropdownRef">
<div style="margin-left: 20px; cursor: pointer">
<el-icon color="#000" size="16"><CaretBottom /></el-icon>
</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>
<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 class="path-container">
<div
class="path-item"
@click="toFinish(item)"
v-for="(item, index) of stageProcessList"
:key="item.id"
>
<div class="item-content">
<!-- 单数在左 -->
<div class="item-progress-left">
<template v-if="index % 2 === 1">
<div
class="progress"
:style="{
width: item.completionStatus === '2' ? '360px' : '224px',
background:
item.completionStatus === '1' ? '#27A741' : '#ededed',
}"
>
<template v-if="item.completionStatus === '2'">
<div
:style="{
width: `${item.progress}%`,
}"
class="progress-content"
>
<div class="rocket">
<img
style="width: 100%; height: 100%"
src="@/assets/image/growth/rocket.png"
/>
</div>
</div>
</template>
<div
:style="{
width:
item.completionStatus === '2' ? '360px' : '224px',
color:
item.completionStatus === '0' ||
item.completionStatus === '10'
? '#333'
: '#fff',
}"
class="progress-text"
>
{{
`${item.taskName}${
item.completionStatus === "10" ? "(未解锁)" : ""
}`
}}
</div>
</div>
<div class="horizontal-line"></div>
</template>
</div>
<div class="icon">
<template v-if="item.lastStudy">
<img
style="width: 100%; height: 100%"
src="@/assets/image/growth/growth-icon4.png"
/>
</template>
<template v-else-if="item.completionStatus === '1'">
<img
style="width: 100%; height: 100%"
src="@/assets/image/growth/growth-icon1.png"
/>
</template>
<template v-else-if="item.completionStatus === '2'">
<img
style="width: 100%; height: 100%"
src="@/assets/image/growth/growth-icon2.png"
/>
</template>
<template
v-else-if="
item.completionStatus === '0' ||
item.completionStatus === '10'
:class="
(index + 1) % 2 && index + 1 != 15
? 'path-item-top'
: 'path-item-bottom'
"
@click="toFinish(item)"
:style="point[index]"
v-for="(item, index) of stageProcessList"
>
<img
style="width: 100%; height: 100%"
src="@/assets/image/growth/growth-icon3.png"
/>
</template>
<template v-if="(index + 1) % 2 && index + 1 != 15">
<div>
<div class="item-name">{{ item.taskName }}</div>
<div class="triangle"></div>
</div>
<!-- 双数在右 -->
<div class="item-progress-right">
<template v-if="index % 2 === 0">
<div class="horizontal-line"></div>
<div
class="progress"
:style="{
width: item.completionStatus === '2' ? '360px' : '224px',
background:
item.completionStatus === '1' ? '#27A741' : '#ededed',
}"
>
<template v-if="item.completionStatus === '2'">
<div
:style="{
width: `${item.progress}%`,
}"
class="progress-content"
>
<div class="rocket">
<img
style="width: 100%; height: 100%"
src="@/assets/image/growth/rocket.png"
<div class="f-a-c">
<div class="item-progress">
<el-progress
:percentage="parseInt(item.progress)"
:show-text="false"
:stroke-width="6"
:color="stateData(item).progressColor"
/>
</div>
</div>
</template>
<div
class="item-state"
:style="{
width:
item.completionStatus === '2' ? '360px' : '224px',
color:
item.completionStatus === '0' ||
item.completionStatus === '10'
? '#333'
: '#fff',
color: stateData(item).color,
background: stateData(item).bgColor,
}"
class="progress-text"
>
{{
`${item.taskName}${
item.completionStatus === "10" ? "(未解锁)" : ""
}`
}}
{{ stateData(item).text }}
</div>
</div>
<div class="item-link">
<div class="line"></div>
<div class="circle"></div>
</div>
</template>
<template v-else>
<div class="item-link">
<div class="circle"></div>
<div class="line"></div>
</div>
<div>
<div class="triangle"></div>
<div class="item-name">{{ item.taskName }}</div>
</div>
<div class="f-a-c" style="margin-top: 9px">
<div class="item-progress">
<el-progress
:percentage="parseInt(item.progress)"
:show-text="false"
:stroke-width="6"
:color="stateData(item).progressColor"
/>
</div>
<div
class="item-state"
:style="{
color: stateData(item).color,
background: stateData(item).bgColor,
}"
>
{{ stateData(item).text }}
</div>
</div>
</template>
</div>
</div>
<template v-if="index != stageProcessList.length - 1">
<div class="vertical-line">
<!-- 虚线不可删除 -->
<div></div>
</div>
</template>
</div>
</template>
<template v-else>
<el-empty description="暂无数据" />
</template>
</div>
<!-- 弹框提示信息 -->
<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=""
@@ -283,31 +281,141 @@
</el-dialog>
</div>
</template>
<script setup>
import { computed, reactive, onMounted, onUnmounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { request } from "@/api/request";
import { useRequest, growthRequest } from "@/api/growthRequest";
import { useStore } from "vuex";
import { computed, onMounted, ref, watch, reactive } from "vue";
import { ElMessage } from "element-plus";
import { GROWTH, TASK_TYPES } from "@/api/CONST";
import { request } from "@/api/request";
import { growthRequest } from "@/api/growthRequest";
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 point = [
{
top: 0,
right: "234px",
},
{
top: "226px",
right: "128px",
},
{
top: "92px",
right: "455px",
},
{
top: "347px",
right: "341px",
},
{
top: "155px",
right: "673px",
},
{
top: "443px",
right: "561px",
},
{
top: "200px",
right: "893px",
},
{
top: "512px",
right: "780px",
},
{
top: "232px",
right: "1111px",
},
{
top: "565px",
right: "1000px",
},
{
top: "254px",
right: "1331px",
},
{
top: "602px",
right: "1220px",
},
{
top: "267px",
right: "1551px",
},
{
top: "630px",
right: "1440px",
},
{
top: "650px",
right: "1660px",
},
];
const router = useRouter();
const { commit, dispatch, state } = useStore();
const userInfo = computed(() => state.userInfo);
const data = computed(() => state.growthInfo);
const {
query: { routerId },
} = useRoute();
const errorData = computed(() => state.projectError);
const lastLearned = computed(() =>
stageProcessList.value.find((item) => item.lastStudy)
);
const stateData = computed(() => {
return (item) => {
if (item.completionStatus === "10") {
return {
text: "未解锁",
color: "#666666",
bgColor: "rgba(102, 102, 102, 0.2)",
progressColor: "#AEB3B8",
};
} else if (item.completionStatus === "0") {
return {
text: "未开始",
color: "#666666",
bgColor: "rgba(102, 102, 102, 0.2)",
progressColor: "#AEB3B8",
};
} else if (item.completionStatus === "1") {
return {
text: "已完成",
color: "#0077EC",
bgColor: "rgba(0, 119, 236, 0.2)",
progressColor: "#0077EC",
};
} else if (item.completionStatus === "2") {
return {
text: "进行中",
color: "#F2903D",
bgColor: "rgba(242, 144, 61, 0.2)",
progressColor: "#F2903D",
};
}
};
});
// 样式类型
const templateClick = () => {
router.push({
path: "/growthList",
query: {
routerId: routerId,
},
});
};
// 查询条件
const queryParams = reactive({
type: 1,
@@ -323,28 +431,71 @@ const tabClick = (type, status) => {
}
getList();
};
// 学习任务列表
const stageProcessList = ref([]);
// 清除
const refresh = () => {
queryParams.taskName = "";
queryParams.completionStatus = "";
getList();
};
// 是否加载数据中
const loading = ref(false);
const getList = () => {
loading.value = true;
growthRequest(PROFESSIONAL_STUDENT_TASKLIST, {
growthId: routerId,
let params = {
growthId: selectGrowth.id ? selectGrowth.id : routerId,
...queryParams,
}).then((res) => {
};
// 3为查询全部状态需给后端传空
if (params.completionStatus == 3) {
params.completionStatus = "";
}
loading.value = true;
growthRequest(PROFESSIONAL_STUDENT_TASKLIST, params).then((res) => {
console.log(res);
stageProcessList.value = res.data;
loading.value = false;
});
};
// 学习任务列表
const stageProcessList = ref([]);
onMounted(() => {
// 专业力必修列表
const growthList = ref([]);
// 当前浏览的专业力必修任务
const selectGrowth = ref({});
// 切换专业力必修
const dropdownRef = ref(null);
const select = (item) => {
selectGrowth.value = item;
dropdownRef.value.handleClose();
getList();
};
onMounted(() => {
dispatch("getGrowthInfo", { routerId });
getList();
growthRequest(PROFESSIONAL_STUDENT_LIST).then((res) => {
growthList.value = res.data;
});
});
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([]);
@@ -354,7 +505,7 @@ const dialogVisibleTip = ref("该任务无法学习,请联系管理员进行
async function toFinish(d) {
if (d.completionStatus == 10) {
ElMessage.warning("当前未解锁");
ElMessage.warning(`请先完成“${d.superTaskName}”的学习任务`);
return;
}
//更新学员当前任务
@@ -406,7 +557,7 @@ async function toFinish(d) {
chapterId: 0,
externalId: d.courseId,
externalName: d.taskName,
targetId: data.value.id,
targetId: selectGrowth.value.id,
studentNo: userInfo.value.userNo,
})
.then((res) => {
@@ -420,9 +571,9 @@ async function toFinish(d) {
query: {
id: d.id,
type: GROWTH,
infoId: data.value.id,
infoId: selectGrowth.value.id,
courseId: d.courseId ? d.courseId : d.info.id,
pName: data.value.growthName,
pName: selectGrowth.value.growthName,
sName: d.taskName,
chapterOrStageId: 0,
exname: d.info.examinationTestName, // 考试名称
@@ -537,10 +688,9 @@ async function toFinish(d) {
d.courseType == 7 ||
(d.courseType == 5 && d.examType == 2)
) {
console.log(d, data.value);
await request(STUDY_RECORD, {
studentId: userInfo.value.id,
targetId: data.value.id,
targetId: selectGrowth.value.id,
logo: GROWTH,
type: GROWTH,
stageOrChapterId: 0,
@@ -561,9 +711,9 @@ async function toFinish(d) {
id: d.id,
type: GROWTH,
projectId: d.courseId,
infoId: data.value.id,
infoId: selectGrowth.value.id,
courseId: d.courseId ? d.courseId : d.info.id,
pName: data.value.growthName,
pName: selectGrowth.value.growthName,
sName: d.taskName,
chapterOrStageId: 0,
},
@@ -596,154 +746,227 @@ function toOffcoursePlanPage(id) {
);
}
</script>
<style lang="scss">
.growth-roadmap {
box-sizing: border-box;
.pdname {
font-size: 20px;
font-weight: 800;
color: #ffffff;
margin-top: 17px;
}
.path-item {
display: flex;
flex-direction: column;
align-items: center;
.item-content {
width: 100%;
position: relative;
display: flex;
justify-content: center;
.item-progress-left,
.item-progress-right {
flex: 1;
<!-- 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;
}
.item-progress-left {
.progress {
:deep(.el-progress-bar__outer) {
background-color: #aeb3b8;
}
}
.progress {
position: relative;
height: 30px;
border-radius: 20px;
.progress-content {
position: relative;
border-radius: 20px;
height: 30px;
background: linear-gradient(90deg, #fecd49 0%, #e13915 100%);
.rocket {
position: absolute;
width: 36px;
height: 36px;
top: -3px;
}
}
.progress-text {
position: absolute;
top: 0;
height: 100%;
padding: 0 24px;
box-sizing: border-box;
.nav {
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
font-size: 16px;
white-space: nowrap; /* 防止文本换行 */
overflow: hidden; /* 隐藏溢出的内容 */
text-overflow: ellipsis; /* 显示省略符号来代表被修剪的文本 */
}
}
.item-progress-right {
.progress {
.progress-content {
.rocket {
right: -18px;
}
}
}
}
.item-progress-left {
justify-content: end;
.progress {
display: flex;
justify-content: end;
.progress-content {
.rocket {
left: -18px;
transform: scaleX(-1);
}
}
.progress-text {
display: flex;
justify-content: end;
}
}
}
.horizontal-line {
height: 1px;
width: 100px;
border-top: 1px dashed #c5c6ca;
}
.icon {
width: 30px;
height: 30px;
}
.item-text {
margin-left: 10px;
}
}
.vertical-line {
display: flex;
justify-content: center;
width: 20px;
height: 64px;
div {
height: 100%;
border-left: 1px dashed #c5c6ca;
width: 1px;
}
}
}
.container {
height: 80px;
background: #ffffff;
border-radius: 8px;
margin-top: 24px;
padding-bottom: 30px;
padding: 0 80px 0 62px;
}
.growth-name-type {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32px 80px 14px 80px;
}
.growth-name {
display: flex;
align-items: center;
font-weight: 700;
font-size: 20px;
color: #000000;
}
.growth-path-container {
background-image: url(@/assets/image/growth/path-bg.png);
background-size: 100%;
background-repeat: no-repeat;
width: 1920px;
height: 1122px;
}
.path-container {
padding: 48px 0;
position: relative;
width: 100%;
height: 659px;
margin-top: 53px;
background-image: url(@/assets/image/growth/path.png);
.path-item-bottom {
position: absolute;
width: 210px;
.item-progress {
flex: 1;
}
.item-state {
width: 64px;
height: 24px;
border-radius: 6px;
text-align: center;
line-height: 24px;
margin-left: 5px;
}
.item-name {
display: inline-block;
max-width: 210px;
min-width: 80px;
background: linear-gradient(268deg, #3c65f5 0%, #4395f9 100%);
border-radius: 7px;
font-size: 16px;
color: #ffffff;
padding: 14px 10px 14px 15px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.triangle {
width: 0px;
height: 0px;
border: 7px solid transparent;
border-bottom-color: #4391f8;
margin-left: 32px;
padding-top: 10px;
}
.item-link {
position: relative;
.line {
width: 2px;
height: 138px;
margin-left: 39px;
background: linear-gradient(180deg, #ffffff 0%, #0077ec 65%);
border-image: linear-gradient(180deg, #ffffff, #0077ec) 10 10;
}
.circle {
width: 14px;
height: 14px;
background: #ffffff;
border-radius: 50%;
border: 3px solid #0077ec;
position: absolute;
top: 0;
left: 30px;
}
}
}
.path-item-top {
position: absolute;
width: 210px;
.item-progress {
flex: 1;
}
.item-state {
width: 64px;
height: 24px;
border-radius: 6px;
text-align: center;
line-height: 24px;
margin-left: 5px;
}
.item-name {
max-width: 210px;
min-width: 80px;
display: inline-block;
background: linear-gradient(268deg, #3c65f5 0%, #4395f9 100%);
border-radius: 7px;
font-size: 16px;
color: #ffffff;
padding: 14px 10px 14px 15px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.triangle {
width: 0px;
height: 0px;
border: 7px solid transparent;
border-top-color: #4391f8;
margin-left: 32px;
}
.item-link {
position: relative;
.line {
margin-left: 39px;
width: 2px;
height: 210px;
background: linear-gradient(0deg, #ffffff 0%, #0077ec 65%);
border-image: linear-gradient(0deg, #ffffff, #0077ec) 10 10;
}
.circle {
width: 14px;
height: 14px;
background: #ffffff;
border-radius: 50%;
border: 3px solid #0077ec;
position: absolute;
bottom: 0;
left: 30px;
}
}
}
}
.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 {
padding-top: 24px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
.tabs-item {
width: 135px;
display: flex;
align-items: center;
flex-direction: column;
font-size: 18px;
font-size: 20px;
color: #666666;
cursor: pointer;
font-weight: 700;
padding: 0 18px;
}
.tabs-line {
width: 135px;
height: 1px;
margin-top: 2px;
width: 96px;
height: 3px;
margin-top: 7px;
}
.active {
color: #2478ff;
color: #0077ec;
font-size: 24px;
.tabs-line {
background-color: #61a4f9;
background-color: #1379f7;
}
}
}
.el-dialog__body {
width: 80%;
}
.el-dialog__header {
display: none;
}
}
</style>

View File

@@ -37,9 +37,13 @@ export default defineConfig(({ command, mode }) =>
},
server: {
proxy: {
"/professional": {
// target: 'http://192.168.16.195:32002',
target: 'http://192.168.150.97:32002',
// "/professional": {
// // target: 'http://192.168.16.195:32002',
// target: 'http://192.168.150.97:32002',
// changeOrigin: true,
// },
'/growth': {
target: 'https://u-pre.boe.com',
changeOrigin: true,
},
'/file/upload': {