Files
fe-manage/src/components/drawers/router/RouterFaceStu.vue

856 lines
23 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">
<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" v-if="checkPer(permissions,createId)">
<div
class="btn btn1"
style="margin-right: 20px"
@click="qrcodeVisible()"
>
<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 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="exportTaskStu">
<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"
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>
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";
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: "请假",
},
]);
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>
},
]);
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 ChoiceCourse = (n) => {
coursePlanIndex.value = n;
params.value.pid = data.value[n].id;
tableRef.value.fetch();
};
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();
},
});
};
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`);
}
function afterVisibleChange(bool) {
bool && fetchData()
}
function resetStudentPage() {
tableRef.value.reset({ pid: data.value[coursePlanIndex.value]?.id, type: 3 });
}
//二维码
const qrcodeVisible = () => {
qrCode({
title: "【签到】二维码",
name: props.datasource?.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}`,
});
};
</script>
<style lang="scss">
.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;
.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;
.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>