Files
fe-manage/src/components/project/ConfirmLecturer.vue
2024-12-31 15:49:48 +08:00

800 lines
22 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="visible"
:closable="false"
class="largeDrawerInside"
placement="right"
width="76%"
:zIndex="1001"
>
<div class="drawerMains">
<div class="headers" style="margin-top:-24px;">
<div class="headerTitle">{{ name }}</div>
<img
style="width: 29px; height: 29px; cursor: pointer"
src="@/assets/images/basicinfo/close.png"
@click="closeDrawer"
/>
</div>
<div class="content">
<div class="head">
<div class="list" :class="index==indexList?'active':''" @click="clickItem(item,index)" v-for="(item,index) in forData">
<div class="left">
<div>培训发生组织</div>
<div class="text">汇总金额</div>
</div>
<div class="right">
<div class="org" :title="item?.trainOrgName">{{item?.trainOrgName||'-'}}</div>
<div class="text org" :title="Number(item?.summaryTotal).toFixed(2)">{{item?.summaryTotal?Number(item?.summaryTotal).toFixed(2)+'元':'-'}}</div>
</div>
</div>
</div>
<div class="box">
<div class="top">
<div class="item">
<a-input @pressEnter="searchData(true)" style="border-radius: 8px;width:240px;height: 40px;" v-model:value="nameUserNo" placeholder="请输入工号/讲师名称进行搜索" allowClear />
</div>
<div class="item">
<a-range-picker format="YYYY-MM-DD" valueFormat="YYYY-MM-DD" style="border-radius: 8px;width:360px;height: 40px;" v-model:value="dateValue" />
</div>
<div class="item">
<a-button type="primary" @click="searchData(true)" style="margin-right:20px;border-radius:8px;width: 100px;height: 40px;">搜索</a-button>
<a-button type="primary" @click="resetData()" style="border-radius:8px;width: 100px;height: 40px;">重置</a-button>
</div>
</div>
<div class="table">
<a-table
ref="drawerContent"
:columns="columns"
:data-source="searchTrue?timesList:expenseList"
:pagination="false"
:scroll="{ x: 'max-content',y:expenseList.length? pageHeight : null }"
:loading="loadingData"
:custom-row="customRow"
>
<template #action="{ record,index }">
<div class="action">
<div @click="removeId(record,index)" class="btn" style="color: #40a9ff;cursor: pointer;">移除</div>
</div>
</template>
</a-table>
</div>
</div>
</div>
<div class="btnn">
<button class="btn1" @click="config">提交审批</button>
<button class="btn1" @click="qureyDrawer">保存至审批中心</button>
<button class="btn2" @click="closeDrawer">取消</button>
</div>
</div>
</a-drawer>
<div>
<a-modal
:visible="modalVisible"
:footer="null"
:title="null"
:centere="true"
:closable="false"
style="margin-top: 400px"
:zIndex="1001"
@cancel="close"
>
<div class="delete">
<div class="del_header"></div>
<div class="del_main">
<div class="header">
<div class="del-icons">
<img src="@/assets/images/coursewareManage/QR.png" alt=""/>
</div>
<span>操作确认</span>
<div class="close_exit" @click="close"></div>
</div>
<!-- <div class="title">本月可提交审批次数 <span style="color:#4ea6ff"> {{ numTime }} / 10 </span></div> -->
<div class="body">
<div>
<span>请仔细核对讲师费信息确认无误后将自动进入(BPM系统)审批流程</span>
</div>
</div>
<div class="del_btnbox">
<div class="del_btn btn2" @click="close">
<div class="btnText">取消</div>
</div>
<div class="del_btn btn2" @click="handleConfirm">
<div class="btnText">确定</div>
</div>
</div>
</div>
</div>
</a-modal>
</div>
</template>
<script setup lang="jsx">
import {computed, defineEmits,defineProps, ref, watch} from "vue";
import {message} from "ant-design-vue";
import * as api from '@/api/Lecturer'
import dialog from "@/utils/dialog";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
name:{
type: String,
default: ""
},
ids:{
type: String,
default: ''
}
});
const loadingData = ref(false)
const modalVisible = ref(false)
const numTime = ref(0)
const close = () => {
modalVisible.value = false;
}
const handleConfirm = () => {
// if(numTime.value >= 10){
// message.error('提交审批次数已达上限')
// return
// }
// numTime.value+=1
// localStorage.setItem('numTime',numTime.value)
const ids = forData.value.flatMap(item => item.expenseList.map(item => item.id));
// let ids = []
// if(searchTrue.value){
// ids = timesList.value.map(item=>item.id)
// }else{
// ids = expenseList.value.map(item=>item.id)
// }
if(!ids.length){
return message.error('暂无可提交的数据')
}
modalVisible.value = false;
emit('example',true)
api.teacherExpenseConfirm({ids:ids?.join(',')}).then(res=>{
console.log(res,'resssss')
api.submitApproval({ids:res.data?.data?.join(',')}).then(res=>{
console.log(res,'resssss')
message.success('提交成功')
close()
closeDrawer()
emit('visibleFalse',false)
emit('example',false)
}).catch(err=>{
message.destroy()
message.error(err.data.msg)
close()
closeDrawer()
emit('visibleFalse',false)
emit('example',false)
})
}).catch(err=>{
message.destroy()
message.error(err.data.msg)
close()
closeDrawer()
emit('visibleFalse',false)
})
}
const forData = ref()
const indexList = ref(0)
const expenseList = ref([])
const searchList = ref([])
const timesList = ref([])
const clickItem = (item,i) => {
expenseList.value = item.expenseList
indexList.value = i
resetData()
}
const pageHeight = computed(() => {
return window.innerHeight - 450
});
watch(()=>props.visible,(val)=>{
if(val){
loadingData.value = true
forData.value = []
expenseList.value = []
// numTime.value = Number(localStorage.getItem('numTime')||0)
api.getListByAffiliation(
{
name: '',
ids: props.ids,
beginTime: '',
endTime: '',
}
).then(res=>{
if(res.data.code === 200){
forData.value = res.data.data
expenseList.value = res.data.data[indexList.value]?.expenseList || []
}
loadingData.value = false
resetData()
}).catch(()=>{
message.error('获取数据失败,请重新尝试')
loadingData.value = false
})
}else{
nameUserNo.value = null
dateValue.value = null
indexList.value = 0
drawerContent.value?.$el.querySelector('.ant-table-body')?.scrollTo({top:0,behavior: 'smooth'})
}
})
const removeId = (e,i) =>{
dialog({
content: "是否确认移除",
ok: () =>{
forData.value?.forEach(item=>{
item.expenseList?.some((i,l)=>{
if(i.id == e.id){
return item.expenseList.splice(l,1)
}
})
})
if(searchTrue.value){
// searchList.value = searchList.value.filter(item=>item.id !== e.id)
timesList.value = timesList.value.filter(item=>item.id !== e.id)
expenseList.value = expenseList.value.filter(item=>item.id !== e.id)
}else{
expenseList.value = expenseList.value.filter(item=>item.id !== e.id)
}
forData.value[indexList.value].summaryTotal = (forData.value[indexList.value]?.summaryTotal - e.expense).toFixed(2)
if(!forData.value[indexList.value].expenseList.length){
forData.value.splice(indexList.value,1)
forData.value.length > 0 && clickItem(forData.value[0],0)
}
}
})
}
const nameUserNo = ref(null)
const dateValue = ref(null)
const searchTrue = ref(false)
const drawerContent = ref(null)
const customRow = (record) => {
return {
style:{
backgroundColor: searchTrue.value && searchList.value.some(item => item.id === record.id) ? '#a6dff9' : '',
}
};
};
let timeout = null
const searchData = (val) => {
searchTrue.value = val
loadingData.value = true
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
loadingData.value = false;
if(!nameUserNo.value&&!dateValue.value){
searchList.value = [];
timesList.value = expenseList.value
forData.value[indexList.value].summaryTotal = expenseList.value.reduce((sum, item) => sum + (Number(item.expense) || 0), 0);
return
}
//搜索 数组expenseList.value 参数名字或者工号nameUserNo.value 日期dateValue.value
// const filteredList = expenseList.value.filter(item => {
// const isNameMatch = (item.name + item.userNo).includes(nameUserNo.value);
// const teachingDateTimestamp = new Date(item.teachingDate).getTime();
// if(dateValue.value==null){
// return isNameMatch
// }
// const startDateTimestamp = new Date(dateValue.value[0]).getTime();
// const endDateTimestamp = new Date(dateValue.value[1]).getTime();
// const isDateInRange = teachingDateTimestamp >= startDateTimestamp && teachingDateTimestamp <= endDateTimestamp;
// if(nameUserNo.value&&startDateTimestamp&&endDateTimestamp){
// return isNameMatch && isDateInRange;
// }
// return isNameMatch || isDateInRange;
// });
const timesLists = expenseList.value.filter(item => {
const teachingDateTimestamp = new Date(item.teachingDate).getTime();
if(dateValue.value==null){
return expenseList.value
}
const startDateTimestamp = new Date(dateValue.value[0]).getTime();
const endDateTimestamp = new Date(dateValue.value[1]).getTime();
const isDateInRange = teachingDateTimestamp >= startDateTimestamp && teachingDateTimestamp <= endDateTimestamp;
return isDateInRange;
});
const filteredList = timesLists.filter(item => {
const isNameMatch = (item.name + item.userNo).includes(nameUserNo.value);
return isNameMatch
});
searchList.value = filteredList;
timesList.value = timesLists
forData.value[indexList.value].summaryTotal = timesLists.reduce((sum, item) => sum + (Number(item.expense) || 0), 0);
let scrollHeight = null
filteredList.length && (scrollHeight = timesLists.findIndex(item => item.id === filteredList[0].id))
if(scrollHeight||scrollHeight==0){
drawerContent.value?.$el.querySelector('.ant-table-body')?.scrollTo({top:scrollHeight*55,behavior: 'smooth'})
}
}, 500);
}
const resetData = () => {
nameUserNo.value = null
dateValue.value = null
searchData(false)
}
const emit = defineEmits(['update:visible'])
const columns = [
{
title: '讲师名称',
dataIndex: 'name',
key: 'name',
align: 'center',
width:120,
},
{
title: '讲师工号',
dataIndex: 'userNo',
key: 'userNo',
align: 'center',
width:100,
},
{
title: '课程名称',
dataIndex: 'courseName',
key: 'courseName',
align: 'left',
width:160,
customCell :() => {return {style: {maxWidth: '200px',overflow: 'hidden',whiteSpace: 'nowrap',textOverflow:'ellipsis',cursor:'pointer'}}},
},
{
title: '所属组织',
dataIndex: 'orgName',
key: 'orgName',
align: 'left',
ellipsis: true,
width:100,
customCell :() => {return {style: {maxWidth: '200px',overflow: 'hidden',whiteSpace: 'nowrap',textOverflow:'ellipsis',cursor:'pointer'}}},
},
{
title: '讲师体系',
dataIndex: 'tsystemName',
key: 'tsystemName',
align: 'left',
width:100,
customCell :() => {return {style: {maxWidth: '200px',overflow: 'hidden',whiteSpace: 'nowrap',textOverflow:'ellipsis',cursor:'pointer'}}},
},
{
title: '讲师等级',
dataIndex: 'tlevelName',
key: 'tlevelName',
align: 'center',
width:100,
customRender: ({ text })=>{
return text||'-'
}
},
{
title: '发薪地',
dataIndex: 'payrollPlace',
key: 'payrollPlace',
align: 'center',
width:120,
customCell :() => {return {style: {maxWidth: '160px',overflow: 'hidden',whiteSpace: 'nowrap',textOverflow:'ellipsis',cursor:'pointer'}}},
customRender: ({ text })=>{
return text||'-'
}
},
{
title: '课程类型',
dataIndex: 'courseType',
key: 'courseType',
align: 'center',
width:140,
customRender: ({ text,record })=>{
switch (text) {
case 0:
return "在线课"
case 1:
return "面授课"
case 2:
return "课程开发"
case 3:
return "作业员入模培训"
case 4:
return "其他"
default:
return "-"
}
}
},
{
title: '授课/开发课程日期',
dataIndex: 'teachingDate',
key: 'teachingDate',
align: 'center',
width:150,
},
{
title: '授课/开发课程时长',
dataIndex: 'teachingTime',
key: 'teachingTime',
align: 'center',
width:150,
customRender: ({ text,record })=>{
return (text/60).toFixed(2)+'小时'
}
},
{
title: '参训人数',
dataIndex: 'studys',
key: 'studys',
align: 'center',
width:100,
customRender: ({text})=>{
return text ? text+'人' : '0人'
}
},
{
title: '评分',
dataIndex: 'score',
key: 'score',
align: 'center',
width:100,
customRender: (value) => {
return (
<div>
{value.record.score?Number(value.record.score).toFixed(0)==0?'-':Number(value.record.score).toFixed(0)+'分' : '-'}
</div>
)
}
},
{
title: '课酬基准',
dataIndex: 'levelPay',
key: 'levelPay',
align: 'center',
width:120,
customRender: ({text})=>{
return text||text==0 ? text+'元' : '-'
}
},
{
title: '计划费用',
dataIndex: 'expense',
key: 'expense',
align: 'center',
width:120,
customCell :() => {return {style: {maxWidth: '160px',overflow: 'hidden',whiteSpace: 'nowrap',textOverflow:'ellipsis',cursor:'pointer'}}},
customRender: ({text})=>{
return text ? text+'元' : '-'
}
},
// {
// title: '应发费用',
// dataIndex: 'payableExpense',
// key: 'payableExpense',
// align: 'center',
// },
{
title: '操作',
align: 'center',
fixed: 'right',
width:100,
customCell:()=>{return {style:{background:'#fff'}}},
slots: { customRender: "action" },
},
]
const closeDrawer = () => emit("update:visible", false);
const qureyDrawer = () => {
const ids = forData.value.flatMap(item => item.expenseList.map(item => item.id));
// let ids = []
// if(searchTrue.value){
// ids = timesList.value.map(item=>item.id)
// }else{
// ids = expenseList.value.map(item=>item.id)
// }
if(!ids.length){
return message.error('暂无可提交的数据')
}
console.log(ids,'idssssss')
dialog({
content: '是否确认讲师费信息无误?提交后按“培训发生组织”汇总至审批中心,等待验证后“提交”进入审批流程。',
ok: () => {
emit('example',true)
api.teacherExpenseConfirm({ids:ids?.join(',')}).then(res=>{
console.log(res,'resssss')
message.success('提交成功')
closeDrawer()
emit('example',false)
emit('visibleFalse',false)
}).catch(err=>{
message.destroy()
message.error(err.data.msg)
closeDrawer()
emit('example',false)
emit('visibleFalse',false)
})
}
})
}
const config = () => {
if(!expenseList.value.length){
message.error('暂无可提交的数据')
return
}
// let ids = []
// if(searchTrue.value){
// ids = timesList.value.map(item=>item.id)
// }else{
// ids = expenseList.value.map(item=>item.id)
// }
// if(!ids.length){
// return message.error('暂无可提交的数据')
// }
modalVisible.value = true;
}
</script>
<style lang="scss" scoped>
/* 重置表格行的悬停效果 */
::v-deep .ant-table-tbody > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) > td {
background: none;
}
/* 未选中行的鼠标划入样式 */
::v-deep .ant-table-tbody > tr:not(.ant-table-row-selected):hover > td {
background: none;
}
/* 未选中行的鼠标划出样式 */
::v-deep .ant-table-tbody > tr:not(.ant-table-row-selected):not(:hover) > td {
background: none;
}
::v-deep .ant-table-thead > tr > th {
background-color: #eff4fc !important;
text-align: center !important;
}
.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%;
}
}
.title{
padding: 0 30px;
margin-top: 34px;
}
.body {
width: 100%;
padding: 0 50px;
margin: 34px auto 56px auto;
margin-top: 40px;
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;
}
}
}
}
.largeDrawerInside {
.drawerMains {
min-width: 600px;
// margin: 0px 32px 0px 32px;
height:100%;
overflow: auto;
display: flex;
flex-direction: column;
padding:24px;
.headers {
height: 73px;
border-bottom: 1px solid #e8e8e8;
display: flex;
justify-content: space-between;
align-items: center;
// background-color: red;
margin-bottom: 20px;
.headerTitle {
margin: 24px 0;
font-size: 18px;
font-weight: 600;
color: #333333;
line-height: 25px;
// margin-left: 24px;
}
}
.content{
margin: 0 20px;
.head{
display: flex;
align-items: center;
overflow-x: auto;
.list{
display: flex;
align-items: center;
background-color: #f5f5f5;
min-width: 260px;
padding: 20px 10px;
color: #ffffff;
border-radius:6px;
margin-right: 20px;
margin-bottom: 20px;
.left{
width: 35%;
min-width: 86px;
text-align:right;
margin-right:30px;
color: rgba(116, 120, 141, 0.603921568627451);
.text{
margin-top:20px;
}
}
.right{
color: #646C9A;
.org{
max-width: 136px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.text{
margin-top:20px;
}
}
}
.active{
background-color: #4ea6ff;
.left{
color: #ffffff;
}
.right{
color: #ffffff;
}
}
}
.box{
padding-bottom: 80px;
.top{
display: flex;
flex-wrap: wrap;
align-items: center;
// margin-bottom: 20px;
margin-top: 10px;
.item{
margin-right: 20px;
margin-bottom: 20px;
}
}
.table{
::v-deep .ant-table-cell-fix-right {
width: 120px !important;
}
}
}
}
.btnn {
height: 72px;
width: 100%;
position: absolute;
bottom: 0;
left: 0;
display: flex;
align-items: center;
justify-content: end;
box-shadow: 0px 1px 35px 0px rgba(118, 136, 166, 0.16);
background-color: #fff;
z-index: 9;
.btn2 {
width: 100px;
height: 40px;
border: 1px solid #4ea6ff;
border-radius: 8px;
color: #4ea6ff;
background-color: #fff;
cursor: pointer;
margin-right: 15px;
}
.btn1 {
cursor: pointer;
width: 120px;
height: 40px;
background: #4ea6ff;
border-radius: 8px;
border: 0;
margin-right: 15px;
color: #fff;
}
}
}
}
</style>