Files
fe-manage/src/components/growthpath/GrowthActiveAttendance.vue
Pengxiansen 01ab826dcf 提交
2025-02-21 14:04:17 +08:00

1166 lines
32 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="workAttendanceVisible"
class="largeDrawerStyle"
placement="right"
width="80%"
@after-visible-change="afterVisibleChange"
>
<div class="drawerMain">
<div class="header">
<div class="headerTitle">
{{ datasource.taskType == 6 ? "直播" : "活动" }}考勤
</div>
<img
style="width: 29px; height: 29px; cursor: pointer"
src="../../assets/images/basicinfo/close.png"
@click="closeDrawer"
/>
</div>
<div class="centermain">
<div class="titl">
<div class="endtime">
起止时间{{
datasource.info.liveStartTime || datasource.info.activityStartTime
}}
{{
datasource.info.liveStartTime || datasource.info.activityEndTime
}}
</div>
<div class="endtime" style="margin-left: 64px">
签到时间{{ beginTime }} {{ endTime }}
</div>
<!--<div class="endtime" style="margin-left: 40px">签退时间{{ endTime }}</div>-->
</div>
<div class="search">
<div class="leftchoose">
<div class="namecon" style="margin-right: 30px">
<div class="username">姓名</div>
<a-input
v-model:value="username"
style="width: 270px; height: 40px; border-radius: 8px"
placeholder="请输入姓名"
maxlength="20"
/>
</div>
<div class="namecon">
<div class="name">签到状态</div>
<div class="select">
<a-select
v-model:value="signStatus"
style="width: 160px"
placeholder="请选择"
:options="signStatusOption"
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 btn2" @click="resetTaskList">
<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" @click="signQR">
<div class="wz">签到二维码</div>
</div>
<div class="btn btn1" @click="showqdModal">
<div class="wz">批量签到</div>
</div>
<div class="btn btn1" @click="exportTaskStu">
<div class="img2"></div>
<div class="wz">导出数据</div>
</div>
</div>
<!-- <div class="line">
<div class="inline">
<div class="left">
<div class="img"></div>
<div class="text" style="margin-left: 10px">已选择</div>
<div class="text2">{{ selectedRowKeys.length }}</div>
<div class="text"></div>
<div class="text3">列表选项总计</div>
<div class="text4">{{ tableData.length }}</div>
</div>
<div class="right" @click="clearLine">清空</div>
</div>
</div> -->
<div class="tableBox" style="margin-top: 30px">
<a-table
style="border: 1px solid #f2f6fe"
:columns="tableDataFunc()"
:data-source="tableData"
:loading="tableDataTotalLoading"
rowKey="username"
:scroll="{ x: 1300 }"
:pagination="false"
:row-selection="{
selectedRowKeys: selectedRowKeys,
onChange: onSelectChange,
}"
/>
</div>
<div
class="tableBox"
style="
display: flex;
justify-content: center;
align-items: center;
margin-top: 12px;
"
>
<div class="pa">
<a-pagination
v-if="tableDataTotal > 10"
:showSizeChanger="false"
showQuickJumper="true"
hideOnSinglePage="true"
:pageSize="pageSize"
:current="currentPage"
:total="tableDataTotal"
class="pagination"
@change="changePagination"
/>
</div>
</div>
</div>
<div class="btnn">
<button class="btn1" @click="closeDrawer">取消</button>
<button class="btn2" @click="closeDrawer">确定</button>
</div>
</div>
</a-drawer>
<!-- 批量签到弹窗 -->
<a-modal
v-model:visible="qdModal"
:footer="null"
:closable="closeCopy"
wrapClassName="CopyModal"
centered="true"
>
<div class="delete">
<div class="del_header"></div>
<div class="del_main">
<div class="header">
<div class="icon"></div>
<span>提示</span>
<div class="close_exit" @click="closeqdModal"></div>
</div>
<div class="body">
<span>您确定要批量签到吗</span>
</div>
<div class="del_btnbox">
<div class="del_btn btn1" @click="closeqdModal">
<div class="btnText">取消</div>
</div>
<div class="del_btn btn2" @click="batchSign">
<div class="btnText">确定</div>
</div>
</div>
</div>
</div>
</a-modal>
<!-- 单独签到弹窗 -->
<a-modal
v-model:visible="singleqdModal"
:footer="null"
:closable="closeCopy"
wrapClassName="CopyModal"
centered="true"
>
<div class="delete">
<div class="del_header"></div>
<div class="del_main">
<div class="header">
<div class="icon"></div>
<span>提示</span>
<div class="close_exit" @click="closesingleqdModal"></div>
</div>
<div class="body">
<span>您确定要签到吗</span>
</div>
<div class="del_btnbox">
<div class="del_btn btn1">
<div class="btnText" @click="closesingleqdModal">取消</div>
</div>
<div class="del_btn btn2">
<div class="btnText" @click="closesingleqdModal">确定</div>
</div>
</div>
</div>
</div>
</a-modal>
<!-- 单独签退弹窗 -->
<a-modal
v-model:visible="singleqtModal"
:footer="null"
:closable="closeCopy"
wrapClassName="CopyModal"
centered="true"
>
<div class="delete">
<div class="del_header"></div>
<div class="del_main">
<div class="header">
<div class="icon"></div>
<span>提示</span>
<div class="close_exit" @click="closesingleqtModal"></div>
</div>
<div class="body">
<span>您确定要签退吗</span>
</div>
<div class="del_btnbox">
<div class="del_btn btn1">
<div class="btnText" @click="closesingleqtModal">取消</div>
</div>
<div class="del_btn btn2">
<div class="btnText" @click="closesingleqtModal">确定</div>
</div>
</div>
</div>
</div>
</a-modal>
<!-- 单独请假弹窗 -->
<a-modal
v-model:visible="singleqjModal"
:footer="null"
:closable="closeCopy"
wrapClassName="CopyModal"
centered="true"
>
<div class="delete">
<div class="del_header"></div>
<div class="del_main">
<div class="header">
<div class="icon"></div>
<span>提示</span>
<div class="close_exit" @click="closesingleqjModal"></div>
</div>
<div class="body">
<span>您确定要请假吗</span>
</div>
<div class="del_btnbox">
<div class="del_btn btn1">
<div class="btnText" @click="closesingleqjModal">取消</div>
</div>
</div>
</div>
</div>
</a-modal>
<!-- 二维码弹窗 -->
<two-dimensional-code
v-model:codevisible="codevisible"
:codeInfo="codeInfo"
:index="codeIndex"
:type="codeType == 1 ? '课程二维码' : codeType == 2 ? '签到二维码' : ''"
/>
<!-- 二维码弹窗 -->
</template>
<script lang="jsx">
import { toRefs, reactive } from "vue";
import TwoDimensionalCode from "../../components/TwoDimensionalCode";
import * as api from "../../api/indexTaskManage";
import { toDate } from "../../api/method";
import { message } from "ant-design-vue";
import {
attendanceList,
batchSendMessage,
exportStudentTaskPage,
taskSign,
taskLeave,
taskBatchSign,
} from "@/api/growthpath";
import { checkPer } from "@/utils/utils";
import dayjs from "dayjs";
export default {
name: "ActiveAttendance",
components: {
TwoDimensionalCode,
},
props: {
workAttendanceVisible: {
type: Boolean,
default: false,
},
datasource: {
type: Object,
default: function () {
return {};
},
},
},
setup(props, ctx) {
const state = reactive({
Evisible: false, //录入成绩抽屉
username: null,
showmodal: false, //勾选提示框
closable: false, //modal右上角的关闭按钮
pageSize: 10,
currentPage: 1,
tableDataTotal: 0,
qdModal: false, //批量签到
singleqjModal: false, //单独请假
singleqdModal: false, //单独签到
singleqtModal: false, //单独签退
closeCopy: false,
signStatusOption: [
{
id: 0,
value: "0",
label: "请假",
},
{
id: 1,
value: "1",
label: "签到",
},
{
id: 2,
value: "2",
label: "未签到",
},
],
selectedRowKeys: [],
tableData: [],
options: [],
tableDataTotalLoading: true, // 表格loading加载配置
beginTime: null, //签到开始时间
endTime: null, //签到结束时间
signStatus: undefined,
codeType: null,
codeIndex: null,
codeInfo: null, //二维码内容
codevisible: false, //二维码弹窗
});
const closeDrawer = () => {
state.selectedRowKeys = [];
state.tableDataTotalLoading = true;
state.currentPage = 1;
state.username = null;
state.signStatus = undefined;
state.tableDataTotal = -1;
ctx.emit("update:workAttendanceVisible", false);
};
const showEntryScore = () => {
state.Evisible = true;
};
const onSelectChange = (selectedRowKeys, e) => {
console.log("selectedRowKeys changed: ", selectedRowKeys);
state.selectedRowKeys = selectedRowKeys;
};
// 计算签到时间
const isSignClick = () => {
// 开始时间
let startTime =
props.datasource.info.liveStartTime ||
props.datasource.info.activityStartTime;
// 开始前签到时间
let afterSignIn = props.datasource.info.afterSignIn;
// 结束后签到时间
let beforeSignIn = props.datasource.info.beforeSignIn;
state.beginTime = dayjs(startTime)
.subtract(afterSignIn, "minute")
.format("YYYY-MM-DD HH:mm:ss");
state.endTime = dayjs(startTime)
.add(beforeSignIn, "minute")
.format("YYYY-MM-DD HH:mm:ss");
};
const afterVisibleChange = (bol) => {
if (bol == true) {
console.log("当前是什么类型", props.datasource.type);
console.log("当前是什么类型", props.datasource);
state.tableDataTotalLoading = true;
getTableData();
isSignClick();
}
};
//分页
const changePagination = (page) => {
state.tableDataTotalLoading = true;
state.currentPage = page;
getTableData();
};
const getTableData = () => {
attendanceList({
pageNum: state.currentPage,
pageSize: state.pageSize,
username: state.username,
taskId: props.datasource.id,
taskType: props.datasource.taskType,
})
.then((res) => {
console.log("在线课数据获取", res);
if (res.data.code == 200) {
state.tableDataTotalLoading = false;
state.tableData = res.data.data.records;
state.tableDataTotal = res.data.data.total;
}
})
.catch((err) => {
console.log(err);
state.tableDataTotalLoading = false;
});
};
//批量签到
const batchSign = () => {
let obj = {
workNoList: state.selectedRowKeys,
taskType: props.datasource.taskType,
growthId: props.datasource.growthId,
courseId: props.datasource.id,
};
console.log(obj);
state.tableDataTotalLoading = true;
taskSign(obj)
.then((res) => {
console.log("签到结果", res, obj);
if (res.data.code === 200) {
message.destroy();
message.success("批量签到成功");
state.qdModal = false;
state.selectedRowKeys = [];
getTableData();
}
})
.catch((err) => {
console.log("签到失败", err, obj);
});
};
const tableDataFunc = () => {
const columns = [
{
title: "工号",
dataIndex: "username",
key: "studentUserNo",
width: 50,
align: "center",
className: "h",
customRender: (text) => {
return (
<div class="racona">
<span>
{" "}
{text.record.username ? text.record.username : "-"}
</span>
</div>
);
},
},
{
title: "姓名",
dataIndex: "userNickName",
key: "userNickName",
width: 50,
align: "center",
className: "classify",
scopedSlots: { customRender: "action" }, //引入的插槽
customRender: (text) => {
return (
<div class="racona">
<span>
{" "}
{text.record.userNickName ? text.record.userNickName : "-"}
</span>
</div>
);
},
},
{
title: "标准岗位",
dataIndex: "stdPositionName",
key: "stdPositionName",
width: 150,
align: "center",
className: "h",
ellipsis: true,
customRender: (text) => {
return (
<div class="racona">
<span>
{" "}
{text.record.stdPositionName
? text.record.stdPositionName
: "-"}
</span>
</div>
);
},
},
{
title: "任职资格",
dataIndex: "qualsLevelDesr",
key: "qualsLevelDesr",
width: 80,
align: "center",
className: "h",
ellipsis: true,
customRender: (text) => {
return (
<div class="racona">
<span>
{" "}
{text.record.qualsLevelDesr
? text.record.qualsLevelDesr
: "-"}
</span>
</div>
);
},
},
{
title: "Band职级",
dataIndex: "bandCode",
key: "bandCode",
width: 60,
align: "center",
ellipsis: true,
className: "h",
customRender: (text) => {
return (
<div class="racona">
<span>
{" "}
{text.record.bandCode ? text.record.bandCode : "-"}
</span>
</div>
);
},
},
{
title: "加入方式",
dataIndex: "joinMethod",
key: "joinMethod",
width: 60,
align: "center",
ellipsis: true,
className: "h",
customRender: ({ record: { joinMethod } }) =>
({
1: "系统加入",
2: "手动加入",
}[joinMethod]),
},
{
title: "签到时间",
dataIndex: "signTime",
key: "signTime",
width: 100,
align: "center",
className: "h",
customRender: (text) => {
return (
<div class="racona">
<span>
{" "}
{text.record.signTime ? text.record.signTime : "-"}
</span>
</div>
);
},
},
{
title: "考勤",
dataIndex: "signStatus",
key: "signStatus",
width: 50,
align: "center",
className: "h",
customRender: (text) => {
// console.log("text", text);
return (
<div class="racona">
<span>
{text.record.signStatus == "1"
? "签到"
: text.record.signStatus == "0"
? "请假"
: "未签到"}
</span>
</div>
);
},
},
{
title: "签到状态",
dataIndex: "signStatus",
key: "signStatus",
width: 50,
align: "center",
className: "h",
customRender: (text) => {
return (
<div class="racona">
<span>
{text.record.signStatus === "1"
? "正常"
: text.record.signStatus === "0"
? "请假"
: "异常"}
</span>
</div>
);
},
},
{
title: "考勤情况",
className: "h",
dataIndex: "opacation",
key: "opacation",
width: 100,
align: "center",
customRender: (value) => {
return checkPer(props.permissions, props.createId) ? (
<div
class="opa"
style="display:flex;justify-content:center;align-items:center;"
>
<div
onClick={() => {
console.log("点击签到", value);
let params = {
taskType: props.datasource.taskType,
growthId: props.datasource.growthId,
workNoList: [value.record.username],
courseId: props.datasource.id,
};
state.tableDataTotalLoading = true;
taskSign(params)
.then((res) => {
console.log("res----签到是否成功", res);
message.destroy();
message.info("签到成功");
getTableData();
})
.catch((err) => {
state.tableDataTotalLoading = false;
console.log(err);
});
}}
style="display:flex;justify-content:center;align-items:center;margin-right:12px;cursor:pointer;"
>
{value.record.signStatus === "1" ? (
<div style="width:16px;height:16px;margin-right:6px;border-radius:16px;border:1px solid #ccc;display:flex;justify-content:center;align-items:center;">
<div style="width:8px;height:8px;border-radius:8px;background:#4ea6ff;"></div>
</div>
) : (
<div style="width:16px;height:16px;margin-right:6px;border-radius:16px;border:1px solid #ccc;"></div>
)}
<div>签到</div>
</div>
<div
onClick={() => {
console.log("点击请假", value);
{
/* showsingleqjModal(); */
}
{
/* AttendanceLeave */
}
let params = {
taskType: props.datasource.taskType,
growthId: props.datasource.growthId,
workNoList: [value.record.username],
courseId: props.datasource.id,
};
state.tableDataTotalLoading = true;
taskLeave(params)
.then((res) => {
console.log("res----请假是否成功", res);
message.destroy();
message.info("请假成功");
getTableData();
})
.catch((err) => {
state.tableDataTotalLoading = false;
console.log(err);
});
}}
style="display:flex;justify-content:center;align-items:center;cursor:pointer;"
>
{value.record.signStatus === "0" ? (
<div style="width:16px;height:16px;margin-right:6px;border-radius:16px;border:1px solid #ccc;display:flex;justify-content:center;align-items:center;">
<div style="width:8px;height:8px;border-radius:8px;background:#4ea6ff;"></div>
</div>
) : (
<div style="width:16px;height:16px;margin-right:6px;border-radius:16px;border:1px solid #ccc;"></div>
)}
<div>请假</div>
</div>
</div>
) : (
""
);
},
},
];
return columns;
};
// 清空所选
const clearLine = () => {
state.selectedRowKeys = [];
};
const showqdModal = () => {
if (state.selectedRowKeys.length == 0) {
message.destroy();
message.error("请选择学员");
return;
}
state.qdModal = true;
};
const closeqdModal = () => {
state.selectedRowKeys = [];
state.qdModal = false;
};
const showsingleqdModal = () => {
state.singleqdModal = true;
};
const showsingleqtModal = () => {
state.singleqtModal = true;
};
const showsingleqjModal = () => {
state.singleqjModal = true;
};
const closesingleqdModal = () => {
state.singleqdModal = false;
};
const closesingleqtModal = () => {
state.singleqtModal = false;
};
const closesingleqjModal = () => {
state.singleqjModal = false;
};
const signQR = () => {
state.codevisible = true;
state.codeInfo = {
title: "【签到】二维码",
name: props.datasource?.taskName,
url:
process.env.VUE_APP_BASE_API +
`/admin/student/studentSign?taskId=${props.datasource.id}&taskType=${props.datasource.taskType}&type=4`,
};
console.log("codeInfo", state.codeInfo);
state.codeIndex = 0;
state.codeType = 1;
};
//搜索学员
const searchTaskList = () => {
state.tableDataTotalLoading = true;
state.currentPage = 1;
getTableData();
};
// 重置按钮
function resetTaskList() {
state.selectedRowKeys = [];
state.tableDataTotalLoading = true;
state.currentPage = 1;
state.name = null;
state.signStatus = undefined;
state.tableDataTotal = 0;
getTableData();
}
// 导出数据
function exportTaskStu() {
{
/* debugger */
}
console.log(
"props.datasource此处的taskId 应该是router_task 表中主键",
props.datasource
);
console.log("props.datasource", props.datasource);
if (props.types == 2) {
window.open(
`${process.env.VUE_APP_BASE_API}/admin/student/exportTaskStudent?currentStageId=${props.datasource.chapterId}&type=2&pid=${props.datasource.routerId}&thirdType=1&taskId=${props.datasource.id}&taskType=${props.datasource.type}`
);
} else {
window.open(
`${process.env.VUE_APP_BASE_API}/admin/student/exportTaskStudent?currentStageId=${props.datasource.stageId}&type=1&pid=${props.datasource.projectId}&thirdType=1&taskId=${props.datasource.id}&taskType=${props.datasource.type}`
);
}
// api
// .exportTaskStudent({
// pageNo: state.currentPage,
// pageSize: state.pageSize,
// currentStageId: props.datasource.stageId,
// currentTaskId: props.datasource.projectTaskId,
// type: 1,
// pid: props.datasource.projectId,
// })
// .then((res) => {
// console.log(res);
// })
// .catch((err) => {
// console.log(err);
// });
}
return {
...toRefs(state),
closeDrawer,
onSelectChange,
showEntryScore,
tableDataFunc,
clearLine,
showqdModal,
checkPer,
closeqdModal,
showsingleqdModal,
showsingleqtModal,
showsingleqjModal,
closesingleqdModal,
closesingleqtModal,
closesingleqjModal,
signQR,
afterVisibleChange,
batchSign,
searchTaskList,
resetTaskList,
exportTaskStu,
changePagination,
};
},
};
</script>
<style lang="scss">
.largeDrawerStyle > .ant-drawer-content-wrapper {
width: 1400px !important;
min-width: 1400px !important;
}
.me {
.ant-modal-body {
padding: 0px;
}
}
.largeDrawerStyle {
.ant-drawer-content-wrapper {
.ant-drawer-header {
display: none !important;
}
.ant-drawer-body {
padding: 0;
.drawerMain {
min-width: 600px;
min-height: 960px;
margin: 0px 32px 0px 32px;
display: flex;
flex-direction: column;
height: 100%;
.header {
height: 73px;
border-bottom: 1px solid #e8e8e8;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-shrink: 0;
.headerTitle {
font-size: 18px;
font-weight: 600;
color: #333333;
line-height: 25px;
}
}
.centermain {
flex: 1;
overflow-y: auto;
margin-bottom: 75px;
padding-right: 10px;
.titl {
display: flex;
.endtime {
font-size: 16px;
font-weight: 500;
color: #333333;
white-space: nowrap;
}
}
.search {
width: 100%;
display: flex;
flex-wrap: wrap;
margin-top: 20px;
justify-content: space-between;
.leftchoose {
display: flex;
margin-right: 20px;
flex-wrap: wrap;
.namecon {
display: flex;
flex-wrap: nowrap;
align-items: center;
margin-bottom: 10px;
.name {
margin-top: 8px;
white-space: nowrap;
}
}
}
.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: #4ea6ff;
color: #fff;
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: 15px;
height: 17px;
background-image: url(../../assets/images/courseManage/search0.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;
margin-right: 20px;
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;
.left {
height: 100%;
display: flex;
align-items: center;
.img {
width: 15px;
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;
}
}
}
.tableBox {
.classify {
padding-left: 0px !important;
}
.ant-checkbox-wrapper {
align-items: center;
margin-top: -2px;
}
.ant-table-selection-column {
padding: 0px !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;
}
}
}
.btnn {
height: 72px;
width: 100%;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0px 1px 35px 0px rgba(118, 136, 166, 0.16);
.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;
}
}
}
}
}
}
</style>