This commit is contained in:
Pengxiansen
2025-02-13 17:42:24 +08:00
parent 7392ed6690
commit 8ccd72bde2
14 changed files with 1590 additions and 39 deletions

6
package-lock.json generated
View File

@@ -15015,7 +15015,6 @@
"integrity": "sha512-yl+5qhpjd8e1G4cMXfORkkBlvtPCIgmRf3IYCWYDKIQ7m+PPa5iTm4feiNmCMD6yGqQWMhhK/7M3oWGL9boKwg==",
"dev": true,
"requires": {
"@babel/core": "^7.12.16",
"@babel/helper-compilation-targets": "^7.12.16",
"@babel/helper-module-imports": "^7.12.13",
"@babel/plugin-proposal-class-properties": "^7.12.13",
@@ -15028,7 +15027,6 @@
"@vue/babel-plugin-jsx": "^1.0.3",
"@vue/babel-preset-jsx": "^1.1.2",
"babel-plugin-dynamic-import-node": "^2.3.3",
"core-js": "^3.8.3",
"core-js-compat": "^3.8.3",
"semver": "^7.3.4"
},
@@ -15888,9 +15886,7 @@
"resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"dev": true,
"requires": {
"ajv": "^8.0.0"
}
"requires": {}
},
"ansi-escapes": {
"version": "3.2.0",

View File

@@ -1,6 +1,7 @@
export const PROJECT = 1;
export const ROUTER = 2;
export const COURSE = 3;
export const GROWTH = 4;
export const TASK_TYPES = {
typeName: {

View File

@@ -132,3 +132,13 @@ export const EditVoteInvolvedAndBrowse = `/vote/editVoteInvolvedAndBrowse post`
export const ROUTER_DETAIL_CHAPTER_LIST = `/stu/router/chapterPcList`
/**专业力必修模块 */
let baseUrl = "/growth"
// let baseUrl = ""
//查询专业力必修详情
export const PROFESSIONAL_STUDENT_DETAIL = id => `${baseUrl}/professional/student/studentGrowthDetail/${id}`
//查询专业力必修详情
export const PROFESSIONAL_STUDENT_TASKLIST = `${baseUrl}/professional/student/studentTaskList`
//记录当前学习任务
export const PROFESSIONAL_STUDENT_LEARN = `${baseUrl}/professional/student/learnCourse`

View File

@@ -1,24 +1,25 @@
import {useRoute, useRouter} from "vue-router/dist/vue-router";
import {useStore} from "vuex";
import {TASK_TYPES} from "@/api/CONST";
import {computed, watchEffect} from "vue";
import { useRoute, useRouter } from "vue-router/dist/vue-router";
import { useStore } from "vuex";
import { TASK_TYPES } from "@/api/CONST";
import { computed, watchEffect } from "vue";
export function useTaskPage() {
const router = useRouter()
const {query: {id: taskId, type, infoId}} = useRoute()
const {state, dispatch} = useStore()
const info = computed(() => type == 1 ? state.projectInfo : state.routerInfo)
const taskList = computed(() => type == 1 ? info.value.stageProcessList.flatMap(t => t.taskProcessList.map(s => ({
...s,
stageId: t.id,
stageName: t.name
}))) : info.value.taskBoList)
const { query: { id: taskId, type, infoId } } = useRoute()
const { state, dispatch } = useStore()
const info = computed(() => type == 1 ? state.projectInfo : type == 2 ? state.routerInfo : state.growthInfo)
const taskList = computed(() => {
return type == 1 ? info.value.stageProcessList.flatMap(t => t.taskProcessList.map(s => ({
...s,
stageId: t.id,
stageName: t.name
}))) : type == 2 ? info.value.taskBoList : []
})
const index = computed(() => taskList.value?.findIndex(t => t.id == taskId))
const hasPrev = computed(() => index.value - 1 > 0)
const hasNext = computed(() => taskList.value.length > index.value)
const hasNext = computed(() => index.value !== -1 && taskList.value?.length > index.value)
type == 1 ? dispatch('getProjectInfo', {projectId: infoId}) : dispatch('getRouterInfo', {routerId: infoId})
type == 1 ? dispatch('getProjectInfo', { projectId: infoId }) : type == 2 ? dispatch('getRouterInfo', { routerId: infoId }) : dispatch('getGrowthInfo', { routerId: infoId })
function nextPage() {
toPage(taskList.value[index.value + 1])
@@ -49,5 +50,5 @@ export function useTaskPage() {
}
}
return {hasPrev, hasNext, nextPage, prevPage}
return { hasPrev, hasNext, nextPage, prevPage }
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -15,9 +15,13 @@ import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import "@/assets/scss/common.scss"
import "@/assets/scss/iconfont.css"
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
app.use(store).use(router).mount('#app')
app.use(ElementPlus, {
locale: zhCn,
})
})
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}

View File

@@ -1,14 +1,15 @@
import {createStore} from "vuex";
import {PROJECT_PROCESS, ROUTER_PROCESS} from "@/api/api";
import {request} from "@/api/request";
import {TASK_TYPES} from "@/api/CONST";
import { createStore } from "vuex";
import { PROJECT_PROCESS, ROUTER_PROCESS, PROFESSIONAL_STUDENT_DETAIL } from "@/api/api";
import { request } from "@/api/request";
import { TASK_TYPES } from "@/api/CONST";
export default createStore({
state: {
userInfo: {},
projectInfo: {},
projectError: {},
routerInfo: {}
routerInfo: {},
growthInfo: {}
},
getters: {},
mutations: {
@@ -18,7 +19,11 @@ export default createStore({
SET_PROJECT_INFO(state, info) {
state.projectInfo = info;
},
SET_PROJECT_ERROR(state,error){
SET_GROWTH_INFO(state, info) {
state.growthInfo = info;
},
SET_PROJECT_ERROR(state, error) {
state.projectError = error;
},
INIT_PROJECT_INFO(state) {
@@ -31,7 +36,7 @@ export default createStore({
}
if (state.projectInfo.unlockMode === 1) {
state.projectInfo.stageProcessList.forEach((t) => {
if(t.studyModel == 1){
if (t.studyModel == 1) {
state.projectInfo.stageProcessList?.forEach((t1) => {
t1.statusName = "已完成";
const stageState = t1.taskProcessList?.some((s) => {
@@ -44,7 +49,7 @@ export default createStore({
return stageState;
});
}
else{
else {
t.statusName = "进行中";
t.taskProcessList?.forEach((s) => s.statusName = (s.status === 1) ? "已完成" : s.status === 2 ? (s.statusName = "进行中") : TASK_TYPES.toName[s.type]);
t.taskProcessList?.every((s) => s.status === 1) && (t.statusName = "已完成");
@@ -52,31 +57,31 @@ export default createStore({
});
return;
}
state.projectInfo.stageProcessList.forEach((item,i)=>{
if(item.studyModel == 1){
state.projectInfo.stageProcessList.forEach((item, i) => {
if (item.studyModel == 1) {
state.projectInfo.stageProcessList?.some((t) => {
t.statusName = "已完成";
const stageState = t.taskProcessList?.some((s) => {
s.unlock = true;
s.statusName = "已完成";
s.status !== 1 && (s.statusName = s.status==2? '进行中' : TASK_TYPES.toName[s.type])
s.status !== 1 && (s.statusName = s.status == 2 ? '进行中' : TASK_TYPES.toName[s.type])
return state.projectInfo.unlockMode === 2 ? s.status !== 1 : (s.status !== 1 && s.flag)
});
stageState && (t.statusName = "进行中");
return stageState;
});
}else{
if(i == 0){
} else {
if (i == 0) {
item.statusName = "进行中";
item.taskProcessList?.forEach((s) => s.statusName = (s.status === 1) ? "已完成" : s.status === 2 ? (s.statusName = "进行中") : TASK_TYPES.toName[s.type]);
item.taskProcessList?.every((s) => s.status === 1) && (item.statusName = "已完成");
}
if(i > 0){
if(state.projectInfo.stageProcessList[i-1].statusName == "已完成"){
if (i > 0) {
if (state.projectInfo.stageProcessList[i - 1].statusName == "已完成") {
item.statusName = "进行中";
item.taskProcessList?.forEach((s) => s.statusName = (s.status === 1) ? "已完成" : s.status === 2 ? (s.statusName = "进行中") : TASK_TYPES.toName[s.type]);
item.taskProcessList?.every((s) => s.status === 1) && (item.statusName = "已完成");
}else{
} else {
item.statusName = "未解锁";
}
}
@@ -117,6 +122,11 @@ export default createStore({
content.commit("INIT_PROJECT_INFO");
});
},
getGrowthInfo(content, { routerId }) {
request(PROFESSIONAL_STUDENT_DETAIL(routerId)).then(res => {
content.commit("SET_GROWTH_INFO", res.data);
})
},
getRouterInfo(content, { routerId, chapterId }) {
request(ROUTER_PROCESS, chapterId ? { routerId, type: 2, chapterId } : { routerId, type: 2 }).then(res => {
content.commit("SET_ROUTER_INFO", res.data);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,390 @@
<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="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
class="path-container"
v-loading="loading"
:style="stageProcessList.length ? '' : 'height:400px'"
>
<template v-if="stageProcessList && stageProcessList.length">
<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' ? '#333' : '#fff',
}"
class="progress-text"
>
{{ item.taskName }}
</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'">
<img
style="width: 100%; height: 100%"
src="@/assets/image/growth/growth-icon3.png"
/>
</template>
</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>
</div>
</template>
<div
:style="{
width:
item.completionStatus === '2' ? '360px' : '224px',
color: item.completionStatus === '0' ? '#333' : '#fff',
}"
class="progress-text"
>
{{ item.taskName }}
</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>
</div>
</div>
</template>
<script setup>
import { computed, reactive, onMounted, onUnmounted, ref } from "vue";
import { useRoute } from "vue-router";
import { useRequest, request } from "@/api/request";
import { useStore } from "vuex";
import {
PROFESSIONAL_STUDENT_TASKLIST,
PROFESSIONAL_STUDENT_LEARN,
} from "@/api/api";
const { commit, dispatch, state } = useStore();
const userInfo = computed(() => state.userInfo);
const data = computed(() => state.growthInfo);
const {
query: { routerId },
} = useRoute();
// 查询条件
const queryParams = reactive({
type: 1,
});
const tabClick = (type, status) => {
// 选修/必修
if (type) {
queryParams.type = type;
}
// 任务状态
if (status) {
queryParams.completionStatus = status;
}
getList();
};
// 是否加载数据中
const loading = ref(false);
const getList = () => {
loading.value = true;
request(PROFESSIONAL_STUDENT_TASKLIST, {
growthId: routerId,
...queryParams,
}).then((res) => {
console.log(res);
stageProcessList.value = res.data;
loading.value = false;
});
};
// 学习任务列表
const stageProcessList = ref([]);
onMounted(() => {
getList();
dispatch("getGrowthInfo", { routerId });
});
function toFinish(item) {
request(PROFESSIONAL_STUDENT_LEARN, {
growthId: routerId,
taskId: item.taskId,
});
}
</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;
display: flex;
align-items: center;
}
.item-progress-left {
.progress {
}
}
.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;
display: flex;
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 {
background: #ffffff;
border-radius: 8px;
margin-top: 24px;
padding-bottom: 30px;
}
.path-container {
padding: 64px 0;
}
.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-weight: 700;
}
.tabs-line {
width: 135px;
height: 1px;
margin-top: 2px;
}
.active {
color: #2478ff;
.tabs-line {
background-color: #61a4f9;
}
}
}
}
</style>

View File

@@ -37,6 +37,11 @@ export default defineConfig(({ command, mode }) =>
},
server: {
proxy: {
"/professional": {
// target: 'http://192.168.16.195:32002',
target: 'http://192.168.150.97:32002',
changeOrigin: true,
},
'/file/upload': {
target: loadEnv(mode, process.cwd()).VITE_PROXY_URL,
changeOrigin: true,