This commit is contained in:
Pengxiansen
2025-02-24 10:28:32 +08:00
parent 348977347f
commit c0e9f9e3b7
9 changed files with 457 additions and 177 deletions

View File

@@ -1,30 +1,32 @@
<template>
<div id="container" v-if="!isLogin">
<nav-top/>
<div style="display: flex">
<nav-left/>
<div style="flex: 1; display: flex; flex-direction: column; width: 0">
<open-pages/>
<bread-crumb/>
<main>
<a-config-provider :locale="zhCN">
<router-view v-slot="{ Component }">
<keep-alive :include="isInclude">
<component :is="Component"/>
</keep-alive>
</router-view>
</a-config-provider>
</main>
<a-config-provider :locale="locale">
<div id="container" v-if="!isLogin">
<nav-top />
<div style="display: flex">
<nav-left />
<div style="flex: 1; display: flex; flex-direction: column; width: 0">
<open-pages />
<bread-crumb />
<main>
<a-config-provider :locale="zhCN">
<router-view v-slot="{ Component }">
<keep-alive :include="isInclude">
<component :is="Component" />
</keep-alive>
</router-view>
</a-config-provider>
</main>
</div>
</div>
</div>
</div>
<div id="container" v-if="isLogin">
<router-view/>
</div>
<div id="container" v-if="isLogin">
<router-view />
</div>
</a-config-provider>
</template>
<script setup>
import {ref, onMounted,computed} from "vue";
import {useStore, createStore} from "vuex";
import { ref, onMounted, computed } from "vue";
import { useStore, createStore } from "vuex";
import NavLeft from "@/components/NavLeft";
import NavTop from "@/components/NavTop";
import OpenPages from "@/components/OpenPages";
@@ -32,44 +34,45 @@ import BreadCrumb from "@/components/BreadCrumb";
import zhCN from "ant-design-vue/es/locale/zh_CN";
import * as api1 from "@/api/index1";
import * as api2 from "@/api/index";
import {request} from "@/api/request";
import {USER_PERMISSION, VALIDATE_TOKEN} from "@/api/apis";
import { request } from "@/api/request";
import { USER_PERMISSION, VALIDATE_TOKEN } from "@/api/apis";
import dayjs from "dayjs";
import "dayjs/locale/zh-cn";
import locale from "ant-design-vue/es/date-picker/locale/zh_CN";
dayjs.locale("zh-cn");
const store = useStore();
const isLogin = ref(false);
const isInclude = computed(()=>store.state.isInclude)
const isInclude = computed(() => store.state.isInclude);
console.log("版本3.3.2------------");
// 监听关闭浏览器
let time1 = ref(0);
let time2 = ref(0);
onMounted(() => {
window.addEventListener('beforeunload', e => beforeunloadHandler(e));
window.addEventListener('unload', e => unloadHandler(e));
})
window.addEventListener("beforeunload", (e) => beforeunloadHandler(e));
window.addEventListener("unload", (e) => unloadHandler(e));
});
//添加监听事件
function beforeunloadHandler() {
time1.value = new Date().getTime();
}
function unloadHandler() {
time2.value = new Date().getTime() - time1.value;
if (time2.value <= 5) {
store.replaceState(createStore({state: {openpages: null}}).state);
store.replaceState(createStore({ state: { openpages: null } }).state);
localStorage.removeItem("openpages");
}
}
</script>
<style lang="scss">
#app {
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
Microsoft YaHei, Arial, sans-serif;
Microsoft YaHei, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
@@ -84,7 +87,9 @@ function unloadHandler() {
}
}
.ant-table-tbody > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) > td {
.ant-table-tbody
> tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)
> td {
background: #f6f9fd;
}
}
@@ -168,4 +173,4 @@ function unloadHandler() {
// }
// }
}
</style>
</style>

View File

@@ -18,8 +18,8 @@ import { boeRequest } from "@/api/request";
// "application/x-www-form-urlencoded";
axios.defaults.withCredentials = true;
const http = axios.create({
baseURL: '/growth',
// baseURL: process.env.VUE_APP_BASE_API_GROWTH,
// baseURL: '/growth',
baseURL: process.env.VUE_APP_BASE_API_GROWTH,
timeout: 1000 * 15,
// headers: { "Content-Type": "multipart/form-data" },
headers: { "Content-Type": "application/json" },

View File

@@ -0,0 +1,211 @@
<template>
<a-modal
:visible="true"
:footer="null"
:title="null"
:centere="true"
:closable="false"
style="margin-top: 400px"
:zIndex="9999"
@cancel="closeDialog"
>
<div class="delete">
<div class="del_header"></div>
<div class="del_main">
<div class="header">
<div class="del-icons">
<img :src="types[type]" alt="" />
</div>
<span>提示</span>
<div class="close_exit" @click="closeDialog"></div>
</div>
<div class="body">
<div>
<span>{{ content }}</span>
</div>
</div>
<div class="del_btnbox" v-if="isCloseBtn">
<div class="del_btn btn2" @click="closeDialog" v-if="cancel">
<div class="btnText">取消</div>
</div>
<div class="del_btn btn2" @click="handleConfirm">
<div class="btnText">确定</div>
</div>
</div>
<div class="del_btnbox" v-else>
<div class="del_btn btn2" @click="closeDialog">
<div class="btnText">关闭</div>
</div>
</div>
</div>
</div>
</a-modal>
</template>
<script setup>
import notide from "@/assets/images/coursewareManage/notice.png";
import infoPng from "@/assets/images/coursewareManage/QR.png";
import { defineProps, onMounted, ref } from "vue";
const props = defineProps({
close: {
type: Function,
default: () => ({}),
},
dialogClose: {
type: Function,
default: () => ({}),
},
ok: {
type: Function,
default: () => ({}),
},
content: String,
title: {
type: String,
default: "提示",
},
cancel: {
type: Boolean,
default: true,
},
duration: {
type: Number,
default: 0,
},
type: {
type: Number,
default: 1,
},
isCloseBtn: {
type: Boolean,
default: true,
},
});
const types = {
1: infoPng,
2: notide,
};
const type = ref(1);
onMounted(() => {
props.duration && setTimeout(() => props.close(props.dialogClose), props.duration);
});
const closeDialog = () => {
props.close()
props.dialogClose()
};
function handleConfirm() {
props.ok();
props.dialogClose()
}
</script>
<style lang="scss" scoped>
.delete {
min-width: 424px;
background: #ffffff;
box-shadow: 0px 1px 35px 0px rgba(118, 136, 166, 0.21);
border-radius: 4px;
position: absolute;
left: 50%;
top: 10%;
transform: translate(-50%, -50%);
.del_header {
position: absolute;
width: calc(100%);
height: 40px;
background: linear-gradient(
rgba(78, 166, 255, 0.2) 0%,
rgba(78, 166, 255, 0) 100%
);
}
.del_main {
width: 100%;
position: relative;
.header {
display: flex;
align-items: center;
padding-top: 20px;
padding-left: 26px;
font-size: 16px;
.del-icons {
width: 16px;
height: 16px;
position: relative;
margin-right: 10px;
img {
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
background-size: 100% 100%;
}
}
.close_exit {
position: absolute;
right: 42px;
cursor: pointer;
width: 20px;
height: 20px;
background-image: url(@/assets/images/coursewareManage/close.png);
background-size: 100% 100%;
}
}
.body {
width: 100%;
padding: 0 30px;
margin: 34px auto 56px auto;
display: flex;
justify-content: center;
align-items: center;
}
.del_btnbox {
display: flex;
margin: 30px auto;
justify-content: center;
.del_btn {
width: 100px;
height: 40px;
background: rgba(64, 158, 255, 0);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 14px;
flex-shrink: 0;
cursor: pointer;
.btnText {
font-size: 14px;
font-weight: 400;
line-height: 40px;
}
}
.btn1 {
border: 1px solid rgba(64, 158, 255, 1);
color: #4ea6ff;
}
.btn2 {
background-color: #4ea6ff;
color: #ffffff;
}
}
}
}
</style>
<style lang="scss">
.ant-modal-body {
padding: 0 !important;
}
</style>

View File

@@ -551,7 +551,7 @@ const batchSign = () => {
ids: courseSelectRows.value?.map((t) => t.studentId),
taskId: taskId.value,
taskType: props.datasource.taskType,
type: 3,
type: 4,
})
.then((res) => {
message.success("签到成功");
@@ -626,11 +626,11 @@ function submitCall(flag) {
// 导出数据
function exportTaskStu() {
window.open(
`${process.env.VUE_APP_BASE_API}/admin/student/exportTaskStudent?type=3&pid=${offcoursePlanId.value}&thirdType=2`
`${process.env.VUE_APP_BASE_API}/admin/student/exportTaskStudent?type=4&pid=${offcoursePlanId.value}&thirdType=2`
);
}
const exportAssessment = () =>
const exportAssessment = () => {
window.open(
`${
process.env.VUE_APP_BASE_API
@@ -640,13 +640,17 @@ const exportAssessment = () =>
data.value[coursePlanIndex.value].id
}&taskType=11`
);
};
function afterVisibleChange(bool) {
bool && fetchData();
}
function resetStudentPage() {
tableRef.value.reset({ pid: data.value[coursePlanIndex.value]?.id, type: 3 });
tableRef.value.reset({
pid: data.value[coursePlanIndex.value]?.id,
type: 18,
});
}
//二维码

View File

@@ -171,7 +171,6 @@ export default {
title: "类型",
dataIndex: "courseType",
key: "courseType",
width: 120,
align: "center",
ellipsis: true,
slots: { customRender: "courseType" },
@@ -189,19 +188,24 @@ export default {
key: "taskType",
align: "center",
ellipsis: true,
width: 120,
customRender: ({ record: { taskType } }) =>
({
1: "必修",
2: "选修",
}[taskType]),
},
{
title: "学习进度",
dataIndex: "progress",
key: "progress",
align: "center",
ellipsis: true,
},
{
title: "开始时间",
dataIndex: "startTime",
key: "startTime",
align: "center",
width: 200,
ellipsis: true,
},
{
@@ -209,14 +213,12 @@ export default {
dataIndex: "finishTime",
key: "finishTime",
align: "center",
width: 200,
},
{
title: "任务状态",
dataIndex: "completionStatus",
key: "completionStatus",
align: "center",
width: 120,
customRender: ({ record: { completionStatus } }) =>
({
2: "进行中",
@@ -233,10 +235,10 @@ export default {
// slots: { customRender: "action" },
// },
]);
const formData = ref()
const formData = ref();
const openDrawer = (row) => {
state.seevisible = true;
formData.value = row
formData.value = row;
getData();
};
const getData = () => {

14
src/utils/gowthDialog.js Normal file
View File

@@ -0,0 +1,14 @@
import { createApp } from 'vue'
import CommonAlert from "@/components/growthpath/GrowthCommonAlert";
import Antd from "ant-design-vue";
function mountContent (option = {}) {
const dom = document.createElement('div')
document.body.appendChild(dom)
const app = createApp(CommonAlert, {
dialogClose: () => { app.unmount(dom); document.body.removeChild(dom) },
...option
})
app.use(Antd).mount(dom)
}
export default mountContent

View File

@@ -16,7 +16,7 @@
allowClear
></a-select>
</div>
<div class="select">
<!-- <div class="select">
<a-select
v-model:value="dataSourceValue"
style="width: 181px; height: 40px"
@@ -27,7 +27,7 @@
]"
allowClear
></a-select>
</div>
</div> -->
<div class="grow_btn_left">
<div class="btn1" @click="searchList">
<div class="search"></div>
@@ -562,7 +562,13 @@ export default {
align: "center",
ellipsis: true,
},
{
title: "学习人数",
dataIndex: "learnNum",
key: "learnNum",
align: "center",
ellipsis: true,
},
{
title: "状态",
dataIndex: "isPublished",
@@ -573,6 +579,7 @@ export default {
return record.isPublished ? "已发布" : "未发布";
},
},
// {
// title: "归属人",
// dataIndex: "createName",
@@ -581,16 +588,16 @@ export default {
// align: "center",
// ellipsis: true,
// },
{
title: "数据来源",
dataIndex: "dataSource",
key: "dataSource",
align: "center",
ellipsis: true,
customRender: ({ record }) => {
return record.dataSource == 1 ? "AMED" : "手动添加";
},
},
// {
// title: "数据来源",
// dataIndex: "dataSource",
// key: "dataSource",
// align: "center",
// ellipsis: true,
// customRender: ({ record }) => {
// return record.dataSource == 1 ? "AMED" : "手动添加";
// },
// },
{
title: "操作",
align: "right",

View File

@@ -119,6 +119,7 @@
<div class="nub2">总完成率</div>
</div>
</div>
<div class="onerow">
<div class="taskmain">任务信息</div>
</div>
@@ -137,64 +138,67 @@
</div>
</template>
</div>
<!-- 条形统计 -->
<div class="proright">
<div class="proright1">
<div class="textpro">
任务总完成率
<!-- <span style="margin-left: 16px; color: #409eff">{{
</div>
<div class="onerow">
<div class="taskmain">完成率统计</div>
</div>
<!-- 条形统计 -->
<div class="proright">
<div class="proright1">
<div class="textpro">
任务总完成率
<!-- <span style="margin-left: 16px; color: #409eff">{{
overviewData3?.totalTaskCompletionNum
}}</span> -->
</div>
<div style="margin-top: 12px; display: flex">
<a-progress
:showInfo="true"
:percent="
fixDoublePer(
overviewData3?.totalTaskCompletionRate || 0
)
"
style="width: 369px"
/>
</div>
</div>
<div class="proright1">
<div class="textpro">
必修任务完成率
<!-- <span style="margin-left: 16px; color: #409eff">{{
<div style="margin-top: 12px; display: flex">
<a-progress
:showInfo="true"
:percent="
fixDoublePer(
overviewData3?.totalTaskCompletionRate || 0
)
"
style="width: 369px"
/>
</div>
</div>
<div class="proright1">
<div class="textpro">
必修任务完成率
<!-- <span style="margin-left: 16px; color: #409eff">{{
overviewData3?.compulsoryTaskCompletionNum
}}</span> -->
</div>
<div style="margin-top: 12px; display: flex">
<a-progress
:showInfo="true"
:percent="
fixDoublePer(
overviewData3?.compulsoryTaskCompletionRate || 0
)
"
style="width: 369px"
/>
</div>
</div>
<div class="proright1">
<div class="textpro">
选修任务完成率
<!-- <span style="margin-left: 16px; color: #409eff">{{
<div style="margin-top: 12px; display: flex">
<a-progress
:showInfo="true"
:percent="
fixDoublePer(
overviewData3?.compulsoryTaskCompletionRate || 0
)
"
style="width: 369px"
/>
</div>
</div>
<div class="proright1">
<div class="textpro">
选修任务完成率
<!-- <span style="margin-left: 16px; color: #409eff">{{
overviewData3?.electiveTaskCompletionNum
}}</span> -->
</div>
<div style="margin-top: 12px; display: flex">
<a-progress
:showInfo="true"
:percent="
fixDoublePer(
overviewData3?.electiveTaskCompletionRate || 0
)
"
style="width: 369px"
/>
</div>
</div>
<div style="margin-top: 12px; display: flex">
<a-progress
:showInfo="true"
:percent="
fixDoublePer(
overviewData3?.electiveTaskCompletionRate || 0
)
"
style="width: 369px"
/>
</div>
</div>
</div>
@@ -288,6 +292,7 @@
forceFallback="true"
group="task"
animation="500"
@change="draggableOnEnd"
>
<template #item="{ element, index }">
<div class="course">
@@ -340,10 +345,7 @@
</div>
<div
class="first"
v-if="
checkMenu('growthPreviousLevelTask') &&
courseType == 1
"
v-if="checkMenu('growthPreviousLevelTask')"
>
<template v-if="element.superiorTaskName">
<div style="margin-right: 8px">
@@ -736,7 +738,7 @@ import CommonStudent from "@/components/student/CommonStudent";
import GrowthOpenCourse from "@/components/growthpath/GrowthOpenCourse.vue";
import qrCode from "@/utils/qrCode";
import { fixDoublePer, checkGrowthPer } from "@/utils/utils";
import dialog from "@/utils/dialog";
import dialog from "@/utils/gowthDialog";
import { TASK_TYPE } from "@/utils/constGrown";
import Draggable from "vuedraggable";
import TwoDimensionalCode from "@/components/TwoDimensionalCode.vue";
@@ -833,16 +835,15 @@ export default {
faceData: {},
});
watch(
() => state.listTaskData,
(old, val) => {
if (old.length == val.length) {
const listIds = old.map((item) => item.id).join(",");
listIds && toSortTask(listIds);
}
},
{ deep: true }
);
watch();
// () => state.listTaskData,
// (old, val) => {
// if (old.length == val.length) {
// const listIds = old.map((item) => item.id).join(",");
// listIds && toSortTask(listIds);
// }
// },
// { deep: true }
// 新增选修任务
const addTask = (url) => {
router.push({
@@ -850,12 +851,22 @@ export default {
query: { growId: state.routerId, pre: state.preId },
});
};
// 列表拖动结束
const draggableOnEnd = () => {
// 开启加载弹窗getListTask请求完成后会关闭弹框
state.spinning = true;
const listIds = state.listTaskData.map((item) => item.id).join(",");
toSortTask(listIds).then((res) => {
getListTask();
});
};
// 撤回
const resize = () => {
dialog({
content: "确定撤回?",
contentTwo: "撤回后学员进度保留,发布后可继续学习",
ok: () => {
state.spinning = true;
withdrawal(route.query.id)
.then((res) => {
if (res.data.code == 200) {
@@ -865,6 +876,9 @@ export default {
})
.catch((err) => {
message.error(err.data.msg);
})
.finally(() => {
state.spinning = false;
});
},
});
@@ -996,27 +1010,41 @@ export default {
message.warning("不可绑定");
return;
}
state.spinning = true;
// 关闭弹窗
state.setSuperiorsVisible = false;
// 保存需解锁的上级任务
saveSuperiorTask({
id: element.id,
superiorTaskId: row.id,
}).then((res) => {
message.success("操作成功");
element.superiorTaskName = row.taskName;
state.setSuperiorsVisible = false;
});
})
.then((res) => {
message.success("操作成功");
element.superiorTaskName = row.taskName;
getListTask();
})
.finally(() => {
state.spinning = false;
});
} else {
state.spinning = true;
// 删除需解锁的上级任务
delSuperiorTask(element.id).then((res) => {
message.success("操作成功");
element.superiorTaskName = null;
});
delSuperiorTask(element.id)
.then((res) => {
message.success("操作成功");
element.superiorTaskName = null;
getListTask();
})
.finally(() => {
state.spinning = false;
});
}
};
const releaseLearnPath = () => {
dialog({
content: "确定发布当前任务?",
ok: () => {
state.spinning = true;
published({
growId: route.query.id,
})
@@ -1028,6 +1056,9 @@ export default {
})
.catch((err) => {
message.error(err.data.msg);
})
.finally(() => {
state.spinning = false;
});
},
});
@@ -1354,10 +1385,20 @@ export default {
coursePlanRef.value.openDrawer(ele);
};
// 是否自动学习开关
const sortSwitchChange = () => {
openOrCloseSortSwitch(state.routerId).then((res) => {
message.success("操作成功");
getListTask();
const sortSwitchChange = (val) => {
dialog({
content: `请确认是否${val ? "启用" : "停用"}按顺序学习?`,
ok: () => {
// 开启加载弹窗getListTask请求完成后会关闭弹框
state.spinning = true;
openOrCloseSortSwitch(state.routerId).then((res) => {
message.success("操作成功");
getListTask();
});
},
close: () => {
state.basicData.sortSwitch = !state.basicData.sortSwitch;
},
});
};
return {
@@ -1386,6 +1427,7 @@ export default {
addTask,
qrcodeAssement,
handleMenuClickpg,
draggableOnEnd,
handleMenuClick,
qrcodeVisible,
handlerSuperiors,
@@ -1671,7 +1713,6 @@ export default {
//margin-right: 38px;
.taskmain {
width: 72px;
height: 24px;
font-size: 18px;
background-color: rgba(255, 255, 255, 0);
@@ -1853,40 +1894,37 @@ export default {
margin-top: 25px;
}
}
}
.proright {
// width: 600px;
margin: 35px 50px;
.proright {
// width: 600px;
margin-top: 55px;
margin-left: 50px;
.ant-progress-bg {
height: 24px !important;
background: #4ea6ff;
}
.ant-progress-bg {
height: 24px !important;
background: #4ea6ff;
.ant-progress-inner {
height: 24px;
}
.proright1 {
// display: flex;
justify-content: right;
.textpro {
color: #6d7584;
font-size: 14px;
margin-right: 20px;
}
.ant-progress-inner {
height: 24px;
}
.proright1 {
// display: flex;
justify-content: right;
.textpro {
color: #6d7584;
font-size: 14px;
margin-right: 20px;
}
.ant-progress-text {
color: #4ea6ff;
font-size: 14px;
font-weight: 700;
}
.ant-progress-text {
color: #4ea6ff;
font-size: 14px;
font-weight: 700;
}
}
}
.onerow {
//width: 100%;
display: flex;
@@ -1897,7 +1935,6 @@ export default {
//margin-right: 38px;
.taskmain {
width: 72px;
height: 24px;
font-size: 18px;
background-color: rgba(255, 255, 255, 0);

View File

@@ -18,16 +18,16 @@ module.exports = defineConfig({
overlay: false,// 解决代码抛出异常
},
proxy: {
// "/professional": {
// target: 'http://192.168.68.211:32002',
// // target: 'http://192.168.50.195:32002',
// // target: 'http://192.168.86.195:32002',
// changeOrigin: true,
// },
"/growth": {
target: 'https:' + process.env.VUE_APP_BOE_API_URL,
"/professional": {
target: 'http://192.168.66.211:32002',
// target: 'http://192.168.50.195:32002',
// target: 'http://192.168.86.195:32002',
changeOrigin: true,
},
// "/growth": {
// target: 'https:' + process.env.VUE_APP_BOE_API_URL,
// changeOrigin: true,
// },
"/manageApi": {
target: 'https:' + process.env.VUE_APP_PROXY_URL,
changeOrigin: true, //表示是否改变原域名