Files
fe-manage/src/components/drawers/router/RouterFaceStu.vue
2024-01-30 10:18:47 +08:00

971 lines
27 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>
<a-drawer
:visible="FSvisible"
class="drawerStyle RouterFaceStu"
placement="right"
width="1200"
@after-visible-change="afterVisibleChange"
>
<div class="drawerMain">
<div class="header">
<div class="headerTitle">面授{{ datasource?.name }}</div>
<img
style="width: 29px; height: 29px; cursor: pointer"
src="../../../assets/images/basicinfo/close.png"
@click="closeDrawer"
/>
</div>
<div class="main">
<a-spin v-if="loading" :spinning="loading"/>
<div v-else class="titl">
<div
style="width: 100%;height:220px;display: flex;flex-wrap: nowrap;overflow-x: scroll;">
<div v-for="(item,n) in data" :key="n" style="cursor: pointer;">
<div
@click="ChoiceCourse(n)"
style="width: 360px;height:180px;margin-right: 32px;display: flex;flex-direction: column;padding: 16px;"
:style="{background:n===coursePlanIndex?'rgb(247, 251, 253)':'rgb(250, 250, 250)'}">
<div style="font-size: 16px;">{{ data.length - n }}次开课</div>
<div style="font-size: 14px;margin-top: 12px;">{{ item.name }}</div>
<div style="font-size: 14px;">
<img src="../../../assets/images/courseManage/time.png" style="width:16px;height:16px;">
{{ item.beginTime }} ~ {{ item.endTime }}
</div>
<div style="font-size: 14px;margin-top: 6px;margin-bottom: 6px;">
<img src="../../../assets/images/courseManage/position.png" style="width:16px;height:16px;">
{{ item.address }}
</div>
<div style="font-size: 14px;">
<img src="../../../assets/images/courseManage/persion.png" style="width:16px;height:16px;">
{{ item.teacher }}
</div>
</div>
</div>
</div>
</div>
<div class="search" v-if="data?.length">
<div class="leftchoose">
<div class="namecon" style="margin-right: 30px">
<div class="name">姓名</div>
<a-input
v-model:value="params.studentName"
style="width: 200px; height: 40px; border-radius: 8px"
placeholder="请输入姓名"
/>
</div>
<div class="namecon">
<div class="name">签到状态</div>
<div class="select">
<a-select
v-model:value="params.finishStatus"
style="width: 200px"
placeholder="请选择"
:options="signOptions"
allowClear
></a-select>
</div>
</div>
</div>
<div class="btns">
<div
class="btn btn1"
style="margin-right: 20px"
@click="searchTaskList"
>
<div class="img1"></div>
<div class="wz">搜索</div>
</div>
<div class="btn btn1" @click="resetStudentPage">
<div class="img2"></div>
<div class="wz">重置</div>
</div>
</div>
</div>
<div class="btnss" style="margin-top: 20px;flex-wrap: wrap;" v-if="checkPer(permissions,createId) && data?.length">
<div
class="btn btn1"
style="margin-right: 20px"
@click="qrcodeVisibleSign()"
:class="{ 'notClick': courseSelectRows.length > 0 }"
>
<div class="wz">签到二维码</div>
</div>
<div
class="btn btn1"
style="margin-right: 20px"
v-if="data[coursePlanIndex]?.assessmentId"
@click="qrcodeAssement()"
:class="{ 'notClick': courseSelectRows.length > 0 }"
>
<div class="wz">评估二维码</div>
</div>
<div
class="btn btn1"
style="margin-right: 20px"
@click="qrcodeVisible()"
:class="{ 'notClick': courseSelectRows.length > 0 }"
>
<div class="wz">开课二维码</div>
</div>
<CommonStudent
:type="3"
:isGroup="true"
:id="data[coursePlanIndex]?.id"
:infoId="infoId"
:infoType="type"
@finash="submitCall"
>
<a-button class="cus-btn" style="background: #4ea6ff; color: #fff">
<template #icon><img style="margin-right: 10px" src="../../../assets/images/courseManage/add0.png"/>
</template>
添加学员
</a-button>
</CommonStudent>
<CommonImport @change="change" title="导入学员" :template-url="stuTemplateUrl" :data="{ targetId: offcoursePlanId, type:3 }" :url="`/admin/student/importStudent`" name="uploadFile">
<div class="btn btn1" style="margin-right: 20px;margin-left: 20px">
<div class="img1"></div>
<div class="wz">导入学员</div>
</div>
</CommonImport>
<div class="btn btn1" @click="batchSign" style="margin-right: 20px">
<div class="wz">批量签到</div>
</div>
<div class="btn btn1" @click="batchSignAll" style="margin-right: 20px">
<div class="wz">全部签到</div>
</div>
<div class="btn btn1" @click="exportTaskStu" style="margin-right: 20px" :class="{ 'notClick': courseSelectRows.length > 0 }">
<div class="img2"></div>
<div class="wz">导出签到数据</div>
</div>
<div class="btn btn1" @click="exportAssessment" v-if="data[coursePlanIndex]?.assessmentId" :class="{ 'notClick': courseSelectRows.length > 0 }">
<div class="img2"></div>
<div class="wz">导出评估数据</div>
</div>
</div>
<div class="tableBox" style="margin-top: 30px">
<BaseTable ref="tableRef" :url="STUDENT_LIST" v-model:params="params" :columns="columns" :init="false"
v-model:selectedRows="courseSelectRows" :showSizeChanger="true"
type="checkbox"/>
</div>
</div>
<div class="btnn">
<button class="btn1" @click="closeDrawer">取消</button>
<button class="btn2" @click="closeDrawer">确定</button>
</div>
</div>
</a-drawer>
</template>
<script setup lang="jsx">
import {computed, defineEmits, ref, watch} from "vue";
import * as api from "@/api/index1";
import BaseTable from "@/components/common/BaseTable";
import {message} from "ant-design-vue";
import {checkPer} from "@/utils/utils";
import {COURSE_PLAN_LIST, STUDENT_LIST} from "@/api/apis";
import dialog from "@/utils/dialog";
import qrCode from "@/utils/qrCode";
import {useRequest} from "@/api/request";
import CommonImport from "@/components/common/CommonImport";
import CommonStudent from "@/components/student/CommonStudent";
import dayjs from "dayjs";
import {delStudentList} from "@/api/index1";
const coursePlanIndex = ref(0);
const tableRef = ref();
const stuTemplateUrl = ref(process.env.VUE_APP_FILE_PATH + process.env.VUE_APP_UP_LOAD_STUDENT_TEMPLATE);
const emit = defineEmits([]);
const signOptions = ref([
{
id: 1,
value: 1,
label: "签到",
},
{
id: 2,
value: 0,
label: "请假",
},
{
id: 3,
value: 0,
label: "未签到",
},
]);
const props = defineProps({
type: {
type: Number,
default: 1,
},
createId: {
type: Number,
default: null,
},
permissions: {
type: String,
default: null,
},
FSvisible: {
type: Boolean,
default: false,
},
datasource: {
type: Object,
default: function() {
return {};
},
},
});
const params = ref({ pid: 0, type: 3 });
const courseSelectRows = ref([]);
const infoId = computed(() => props.type === 1 ? props.datasource.projectId : props.datasource.routerId);
const taskId = computed(() => props.type === 1 ? props.datasource.projectTaskId : props.datasource.routerTaskId);
const offcoursePlanId = computed(() => data.value[coursePlanIndex.value]?.id || "");
const inAttendanceTime = computed(() => dayjs(data.value[coursePlanIndex.value]?.endTime).isAfter(dayjs()));
const planParams = computed(() => ({
type: props.type,
offcourseId: props.datasource?.courseId,
taskId: props.datasource?.id
}));
const columns = ref([
{
title: "工号",
dataIndex: "studentUserNo",
key: "studentUserNo",
width: 120,
align: "center",
ellipsis: true,
className: "h",
customRender: (text) => <div class="racona"><span> {text.record.studentUserNo || "-"} </span></div>
},
{
title: "姓名",
dataIndex: "studentName",
key: "studentName",
width: 50,
align: "left",
className: "classify",
scopedSlots: { customRender: "action" },
customRender: (text) => <div class="racona"><span> {text.record.studentName || "-"} </span></div>
},
{
title: "部门",
dataIndex: "studentDepartName",
key: "studentDepartName",
width: 60,
ellipsis: true,
align: "center",
className: "h",
customRender: (text) => <div class="racona"><span> {text.record.studentDepartName || "-"} </span></div>
},
{
title: "岗位",
dataIndex: "studentJobName",
key: "studentJobName",
width: 50,
align: "center",
ellipsis: true,
className: "h",
customRender: (text) => <div class="racona"><span> {text.record.studentJobName || "-"}</span></div>
},
{
title: "签到时间",
dataIndex: "signTime",
key: "signTime",
width: 110,
align: "center",
ellipsis: true,
className: "h",
customRender: (text) => <div class="racona"><span>{text.record.signTime || "-"}</span></div>
},
{
title: "考勤",
dataIndex: "signStatus",
key: "signStatus",
width: 110,
align: "center",
ellipsis: true,
className: "h",
customRender: (text) =>
<span>{text.record.signStatus ? "签到" : text.record.leaveStatus ? "请假" : inAttendanceTime.value ? "-" : "缺勤"}</span>
},
{
title: "签到状态",
dataIndex: "signStatus",
key: "signStatus",
width: 110,
align: "center",
ellipsis: true,
className: "h",
customRender: (text) =>
<span>{(text.record.signStatus || text.record.leaveStatus) ? "正常" : inAttendanceTime.value ? "-" : "异常"}</span>
},
{
title: "考勤情况",
ellipsis: true,
className: "h",
dataIndex: "opacation",
key: "opacation",
width: 130,
align: "center",
customRender: (text) =>
<div class="opa">
<a-radio checked={text.record.signStatus} onClick={() => stuSign(text)}>签到</a-radio>
<a-radio checked={text.record.leaveStatus} onClick={() => stuSign(text)}>请假</a-radio>
</div>
},
{
title: "操作",
ellipsis: true,
className: "h",
dataIndex: "opacation",
key: "opacation",
width: 130,
align: "center",
customRender: (text) =>
<div class="opa">
<a className="opa" style={{color: '#666'}} onClick={()=>removeStu(text.record.id)}>删除</a>
</div>
},
]);
const { data = [], loading, fetchData } = useRequest(COURSE_PLAN_LIST, planParams, false);
watch(() => data.value, () => {
coursePlanIndex.value = 0
params.value.pid = data.value[coursePlanIndex.value]?.id || 0;
tableRef.value.fetch();
});
// 开课签到二维码名字
const openCourseName = ref("");
const ChoiceCourse = (n) => {
openCourseName.value = data.value[n].name;
coursePlanIndex.value = n;
params.value.pid = data.value[n].id;
tableRef.value.fetch();
};
const removeStu=(id)=> dialog({
content: "确定删除该学员吗?",
ok: () => delStudentList({ ids: [id] }).then(() => fetchData())
})
const closeDrawer = () => emit("update:FSvisible", false);
//批量签到
const batchSign = () => {
if (!courseSelectRows.value.length) {
return message.warn("请选择要签到的学员");
}
dialog({
content: "确定批量签到吗?",
ok: async () => {
message.success("批量签到成功");
tableRef.value.toLoading();
await api.attendanceSign({
courseId: offcoursePlanId.value,
ids: courseSelectRows.value?.map(t => t.studentId),
taskId: taskId.value,
taskType: props.datasource.type,
type: 3
});
tableRef.value.fetch();
},
});
};
const batchSignAll = () => {
dialog({
content: "确定全部签到吗?",
ok: async () => {
message.success("全部签到成功");
tableRef.value.toLoading();
await api.attendanceSignAll({
courseId: offcoursePlanId.value
});
tableRef.value.fetch();
},
});
};
function stuSign(text) {
text.record.signStatus = !text.record.signStatus;
text.record.leaveStatus = !text.record.signStatus;
text.record.signStatus && (text.record.signTime = dayjs().format("YYYY-MM-DD HH:mm:ss"));
text.record.signStatus || (text.record.signTime = "");
text.record.signStatus && api.attendanceSign({
courseId: offcoursePlanId.value,
ids: [text.record.studentId],
studentName: text.record.studentName,
taskId: taskId.value,
taskType: props.datasource.type,
type: 3
});
text.record.leaveStatus && api.attendanceLeave({
courseId: offcoursePlanId.value,
ids: [text.record.studentId],
studentName: text.record.studentName,
taskId: taskId.value,
taskType: props.datasource.type,
type: 3
});
}
//搜索学员
const searchTaskList = () => tableRef.value.fetch();
//添加学员
function submitCall(flag) {
tableRef.value.toLoading();
flag && tableRef.value.fetch();
}
// 导出数据
function exportTaskStu() {
window.open(`${process.env.VUE_APP_BASE_API}/admin/student/exportTaskStudent?type=3&pid=${offcoursePlanId.value}&thirdType=2`);
}
const exportAssessment = () =>
window.open(`${process.env.VUE_APP_BASE_API}/admin/assessment/manage/exportCoursePlanAssessmentMessage?type=3&pid=${data.value[coursePlanIndex.value].id}&courseId=${data.value[coursePlanIndex.value].assessmentId}&taskId=${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 });
}
//二维码
const qrcodeVisible = () => {
qrCode({
title: "【开课】二维码",
name: openCourseName.value?openCourseName.value:data.value[0]?.name,
// url: `${location.protocol}//${location.host}${process.env.VUE_APP_BASE_API}/admin/student/studentSign?taskId=${props.datasource.id}&taskType=${props.datasource.type}&type=${props.type}`,
url: `${location.protocol}//${location.host}${process.env.VUE_APP_BASE_API}/stu/project/redirectDetail?courseId=${data.value[coursePlanIndex.value]?.id}`,
});
};
// 签到二维码
const qrcodeVisibleSign = () => {
qrCode({
title: "【签到】二维码",
name: openCourseName.value?openCourseName.value:data.value[0]?.name + '课程签到',
// url: `${location.protocol}//${location.host}${process.env.VUE_APP_BASE_API}/admin/student/studentSign?taskId=${props.datasource.id}&taskType=${props.datasource.type}&type=${props.type}`,
url: `${location.protocol}//${location.host}${process.env.VUE_APP_BASE_API}/admin/student/studentSign?taskId=${data.value[coursePlanIndex.value]?.id}&taskType=${2}&type=${3}`,
});
};
const qrcodeAssement = () =>{
console.log(data,'data')
qrCode({
title: "【评估】二维码",
// name: data.value[coursePlanIndex.value]?.assessmentName + '课程评估',
name: openCourseName.value?openCourseName.value:data.value[0]?.name + '课程评估',
url: `${location.protocol}//${location.host}/student-h5/investigatpage?id=${data.value[coursePlanIndex.value]?.id}&type=3&infoId=${data.value[coursePlanIndex.value]?.id}&courseId=${data.value[coursePlanIndex.value].assessmentId}&chapterOrStageId=0`,
});
const a = {url: `${location.protocol}//${location.host}/student-h5/investigatpage?id=${data.value[coursePlanIndex.value]?.id}&type=3&infoId=${data.value[coursePlanIndex.value]?.id}&courseId=${data.value[coursePlanIndex.value].assessmentId}&chapterOrStageId=0`}
console.log(a,'aaaa')
}
const change = (e) => {
console.log(e)
if(e==="end"){
// 请求刷新数据
tableRef.value.fetch();
}
}
</script>
<style lang="scss">
.notClick{
// cursor: pointer;
// pointer-events: none;
// width: 100px;
// height: 40px;
// border: 1px solid #3b3c3c !important;
// border-radius: 8px;
// color: #fff !important;
// background-color: #3b3c3c !important;
}
.me {
.ant-modal-body {
padding: 0px;
}
}
.CopyModal {
.ant-modal {
width: 424px !important;
height: 258px !important;
.ant-modal-content {
width: 424px !important;
height: 258px !important;
.ant-modal-body {
width: 424px !important;
height: 258px !important;
padding: 0 !important;
.delete {
z-index: 999;
width: 424px;
height: 258px;
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: 68px;
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;
.icon {
width: 16px;
height: 16px;
margin-right: 10px;
background-image: url(@/assets/images/coursewareManage/QR.png);
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%;
margin: 34px auto 56px auto;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
// background-color: red;
position: relative;
.back {
position: absolute;
top: 30px;
font-size: 12px;
font-weight: 400;
color: #666666;
}
}
.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;
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;
margin-right: 14px;
}
.btn2 {
background-color: #4ea6ff;
color: #ffffff;
}
}
}
}
}
}
}
}
.RouterFaceStu {
// // width: 80%;
// .ant-drawer-content-wrapper {
// // max-width: 1000px;
// .ant-drawer-header {
// display: none !important;
// }
// .ant-drawer-body {
// padding: 0;
// }
// }
.drawerMain {
min-width: 600px;
margin: 0px 32px 0px 32px;
overflow-x: auto;
display: flex;
flex-direction: column;
.header {
height: 73px;
border-bottom: 1px solid #e8e8e8;
display: flex;
justify-content: space-between;
align-items: center;
// background-color: red;
margin-bottom: 20px;
.headerTitle {
font-size: 18px;
font-weight: 600;
color: #333333;
line-height: 25px;
// margin-left: 24px;
}
}
.main {
width: 100%;
height: 100%;
overflow: auto;
padding-right: 10px;
padding-bottom: 90px;
.titl {
display: flex;
.endtime {
font-size: 16px;
font-weight: 500;
color: #333333;
}
}
.search {
width: 100%;
display: flex;
flex-wrap: wrap;
margin-top: 20px;
justify-content: space-between;
.leftchoose {
display: flex;
margin-right: 20px;
.namecon {
display: flex;
flex-wrap: nowrap;
margin-bottom: 10px;
.name {
margin-top: 8px;
white-space: nowrap;
}
// .name {
// margin-top: 8px;
// color: rgba(0, 0, 0, 0.85);
// font-size: 14px;
// font-weight: 400;
// }
}
}
.btns {
display: flex;
flex-wrap: nowrap;
.btn {
cursor: pointer;
width: 100px;
height: 40px;
border-radius: 8px;
display: flex;
justify-content: center;
align-items: center;
.img1 {
width: 15px;
height: 17px;
background-image: url(../../../assets/images/courseManage/search0.png);
background-size: 100% 100%;
margin-right: 7px;
}
.img2 {
width: 16px;
height: 18px;
background-image: url(../../../assets/images/courseManage/reset0.png);
background-size: 100% 100%;
margin-right: 7px;
}
}
.btn1 {
background: #4ea6ff;
color: #ffffff;
}
.btn2 {
background: #ffffff;
color: #4ea6ff;
border: 1px solid #4ea6ff;
}
}
}
.btnss {
display: flex;
flex-wrap: nowrap;
.btn {
cursor: pointer;
width: 130px;
height: 40px;
border-radius: 8px;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 10px;
.img1 {
width: 19px;
height: 19px;
background-image: url(../../../assets/images/basicinfo/in.png);
background-size: 100% 100%;
margin-right: 7px;
}
.img2 {
width: 17px;
height: 16px;
background-image: url(../../../assets/images/coursewareManage/export1.png);
background-size: 100% 100%;
margin-right: 7px;
}
}
.btn1 {
background: #4ea6ff;
color: #ffffff;
}
.btn2 {
background: #ffffff;
margin-right: 20px;
color: #4ea6ff;
border: 1px solid #4ea6ff;
}
}
.line {
width: 100%;
height: 40px;
background-color: #e9f6fe;
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
border: 1px solid #c3e6fc;
.inline {
width: 95%;
height: 100%;
display: flex;
justify-content: space-between;
// background-color: #bfa;
.left {
height: 100%;
display: flex;
align-items: center;
.img {
width: 14px;
height: 15px;
background-image: url(../../../assets/images/leveladd/gan.png);
background-size: 100% 100%;
}
.text {
color: #999ba3;
}
.text2 {
color: #4ea6ff;
margin-left: 5px;
margin-right: 5px;
}
.text3 {
color: #999ba3;
margin-left: 20px;
}
}
.right {
font-size: 14px;
font-weight: 400;
color: #387df7;
height: 100%;
display: flex;
align-items: center;
cursor: pointer;
}
}
}
.pad {
width: 96%;
height: 10px;
background-color: #fff;
position: absolute;
}
.tableBox {
// margin-bottom: 80px;
.ant-select-dropdown{
display: inline-block;
}
.ant-select-selection-item{
margin-left: 3px;
}
.ant-pagination-options-size-changer.ant-select{
width: 84px;
}
.classify {
// margin-left: 11px !important;
// padding-left: 9px !important;
padding-left: 0px !important;
}
.ant-checkbox-wrapper {
align-items: center;
margin-top: -2px;
}
.ant-table-selection-column {
padding: 0px !important;
// padding-left: 45px !important;
}
.ant-table-thead > tr > th {
background-color: rgba(239, 244, 252, 1);
}
th.h {
background-color: #eff4fc !important;
}
.ant-table-tbody
> tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)
> td {
background: #f6f9fd;
}
.opa {
background-color: rgba(255, 255, 255, 0);
.ant-checkbox + span {
font-size: 14px;
font-weight: 400;
color: rgba(0, 0, 0, 0.65);
line-height: 22px;
margin-top: 5px;
}
}
}
// .tab {
// .ant-table-thead > tr > th {
// background-color: rgba(239, 244, 252, 1) !important;
// }
// th.h {
// background-color: #eff4fc !important;
// }
// .ant-table-tbody
// > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)
// > td {
// background: #f6f9fd;
// }
// }
}
.btnn {
height: 72px;
width: 100%;
position: absolute;
bottom: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0px 1px 35px 0px rgba(118, 136, 166, 0.16);
background-color: #fff;
.btn1 {
width: 100px;
height: 40px;
border: 1px solid #4ea6ff;
border-radius: 8px;
color: #4ea6ff;
background-color: #fff;
cursor: pointer;
}
.btn2 {
cursor: pointer;
width: 100px;
height: 40px;
background: #4ea6ff;
border-radius: 8px;
border: 0;
margin-left: 15px;
color: #fff;
}
}
}
// .botm {
// width: 100%;
// height: 100%;
// background-color: red;
// // flex-shrink: 1;
// }
}
</style>