Files
fe-manage/src/components/project/ConfirmLecturer.vue

810 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 class="icon" :class="activeList.includes(index)?'active':'not'" @click.stop="setList(index)"></div>
</div>
</div>
<div class="box">
<div class="top">
<div class="item">
<a-input @pressEnter="searchData(true,indexList)" style="border-radius: 8px;width:240px;height: 40px;" v-model:value="searchConditions[indexList].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="searchConditions[indexList].dateValue" />
</div>
<div class="item">
<a-button type="primary" @click="searchData(true,indexList)" style="margin-right:20px;border-radius:8px;width: 100px;height: 40px;">搜索</a-button>
<a-button type="primary" @click="resetData(indexList,true)" 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[indexList]:expenseList[indexList]"
:pagination="false"
:scroll="{ x: 'max-content',y:expenseList[indexList]?.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 = () => {
modalVisible.value = false;
emit('example',true)
const idList = []
activeList.value?.map(item=>{
idList.push(...timesList.value[item])
})
const ids = idList?.map(item=>item.id)
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) => {
indexList.value = i
resetData(i,false)
}
const pageHeight = computed(() => {
return window.innerHeight - 450
});
const searchConditions = ref([{ nameUserNo: '', dateValue: [] }])
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;
//todo1 , 只有一个处理 初始化 处理数据 ,进行 激活处理
if(forData.value.length == 1){
activeList.value.push(0);
}
forData.value.forEach((item,i) => {
searchConditions.value.push({ nameUserNo: '', dateValue: [] });
expenseList.value.push(forData.value[i]?.expenseList || [])
timesList.value.push(forData.value[i]?.expenseList || [])
});
}
loadingData.value = false
resetData(indexList.value,true)
}).catch((err)=>{
message.error('获取数据失败,请重新尝试')
loadingData.value = false
})
}else{
searchConditions.value = [{ nameUserNo: '', dateValue: [] }]
timesList.value = []
indexList.value = 0
activeList.value = []
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){
timesList.value[indexList.value] = timesList.value[indexList.value].filter(item=>item.id !== e.id)
expenseList.value[indexList.value] = expenseList.value[indexList.value].filter(item=>item.id !== e.id)
}else{
expenseList.value[indexList.value] = expenseList.value[indexList.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 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,index) => {
searchTrue.value = val
loadingData.value = true
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
loadingData.value = false;
const { nameUserNo, dateValue } = searchConditions.value[index];
if(!nameUserNo&&!dateValue.length){
searchList.value = [];
timesList.value[indexList.value] = expenseList.value[indexList.value]
forData.value[indexList.value].summaryTotal = expenseList.value[indexList.value].reduce((sum, item) => sum + (Number(item.expense) || 0), 0);
return
}
const timesLists = expenseList.value[indexList.value].filter(item => {
const teachingDateTimestamp = new Date(item.teachingDate).getTime();
if(dateValue==null||!dateValue.length){
return expenseList.value[indexList.value]
}
const startDateTimestamp = new Date(dateValue[0]).getTime();
const endDateTimestamp = new Date(dateValue[1]).getTime();
const isDateInRange = teachingDateTimestamp >= startDateTimestamp && teachingDateTimestamp <= endDateTimestamp;
return isDateInRange;
});
const filteredList = timesLists.filter(item => {
if(!nameUserNo){
return
}
const isNameMatch = (item.name + item.userNo).includes(nameUserNo);
return isNameMatch
});
searchList.value = filteredList;
timesList.value[indexList.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 = (index,val) => {
val && (searchConditions.value[index] = { nameUserNo: '', dateValue: [] });
searchData(true,index);
}
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,
ellipsis:true,
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(2)+'分' : '-'}
</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 activeList = ref([])
const setList = (item) => {
const index = activeList.value.findIndex(listItem => listItem === item);
if (index > -1) {
activeList.value.splice(index, 1);
} else {
activeList.value.push(item);
}
}
const qureyDrawer = () => {
const idList = []
activeList.value?.map(item=>{
idList.push(...timesList.value[item])
})
const ids = idList?.map(item=>item.id)
if(!ids.length){
return message.error('暂无可提交的数据')
}
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 = () => {
const idList = []
activeList.value?.map(item=>{
idList.push(...timesList.value[item])
})
const ids = idList?.map(item=>item.id)
if(!ids.length){
message.error('暂无可提交的数据')
return
}
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;
padding-top: 20px;
.list{
display: flex;
align-items: center;
// background-color: #f5f5f5;
border: 1px solid #1e98d7;
min-width: 286px;
padding: 20px 10px;
// color: #ffffff;
border-radius:6px;
margin-right: 20px;
margin-bottom: 20px;
position: relative;
.icon{
position: absolute;
right: 3px;
top: 3px;
width:24px;
height:24px;
cursor: pointer;
// background: url('@/assets/icon.png') no-repeat;
// background-size: 100% 100%;
}
.active{
background: url('@/assets/icon.png') no-repeat;
background-size: 100% 100%;
}
.not{
background: url('@/assets/iconnot.png') no-repeat;
background-size: 100% 100%;
}
.left{
width: 35%;
min-width: 86px;
text-align:right;
margin-right:20px;
// color: rgba(116, 120, 141, 0.603921568627451);
color: #333333;
.text{
margin-top:20px;
}
}
.right{
color: #333333;
.org{
max-width: 144px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.text{
margin-top:20px;
}
}
}
.active{
// background-color: #4ea6ff;
background-color: rgba(174, 220, 246, 0.64);
.left{
color: #333333;
}
.right{
color: #333333;
}
}
}
.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>