Merge branch 'zcwy-teacher-manage' into master_1202

This commit is contained in:
joshen
2024-12-02 19:28:11 +08:00
70 changed files with 29855 additions and 38 deletions

View File

@@ -108,6 +108,89 @@
},
];
}
if (
n.indexOf("/lecturerlist") !== -1
) {
state.list = [
{
name: "讲师管理",
href: "",
},
{
name: "讲师列表",
href: "",
},
];
}
if (
n.indexOf("/teachingrecord") !== -1
) {
state.list = [
{
name: "讲师管理",
href: "",
},
{
name: "授课记录",
href: "",
},
];
}
if (
n.indexOf("/lecturerfeemanagement") !== -1
) {
state.list = [
{
name: "讲师管理",
href: "",
},
{
name: "讲师费管理",
href: "",
},
];
}
if (
n.indexOf("/lecturerfeestatistics") !== -1
) {
state.list = [
{
name: "讲师管理",
href: "",
},
{
name: "讲师费统计",
href: "",
},
];
}
if (
n.indexOf("/organization") !== -1
) {
state.list = [
{
name: "讲师管理",
href: "",
},
{
name: "培训发生组织",
href: "",
},
];
}if (
n.indexOf("/lecturerSystem") !== -1
) {
state.list = [
{
name: "讲师管理",
href: "",
},
{
name: "讲师体系管理",
href: "",
},
];
}
if (
n.indexOf("/projectadd") !== -1 ||
n.indexOf("/ProjectAdd") !== -1

View File

@@ -56,6 +56,10 @@ const props = defineProps({
accept: {
type: String,
default: ''
},
size: {
type: Number,
default: 0
}
})
const emit = defineEmits(['update:value', 'change'])
@@ -67,6 +71,12 @@ const handleUploadChange = ({ file, fileList }) => {
fileList.value = [];
return message.error("请上传正确的文件格式");
}
if(props.size && file.size > props.size){
removeUpload()
message.destroy();
message.error("文件大小超过5MB!");
return
}
emit('update:value', fileList)
emit('change', fileList)
}

View File

@@ -415,6 +415,59 @@
<router-link to="/teacheropinion">意见</router-link>
</a-menu-item>
</a-sub-menu>
<a-sub-menu key="sub24" @titleClick="titleClick" v-if="checkMenu('lecturer')">
<template #icon>
<div class="imgBox">
<img style="width: 15px; height: 15px" src="../assets/images/navleft/project.png" />
</div>
</template>
<template #title>讲师管理</template>
<a-menu-item key="sub24-1" v-if="checkMenu('lecturer')">
<span
:class="{
circleActive: selectedKeys[0] === 'sub24-1' ? true : false,
circle: selectedKeys[0] === 'sub24-1' ? false : true,
}"
></span>
<router-link to="/lecturerlist">讲师列表</router-link>
</a-menu-item>
<a-menu-item key="sub24-2">
<span
:class="{
circleActive : selectedKeys[0] === 'sub24-2' ? true : false,
circle: selectedKeys[0] === 'sub24-2' ? false : true,
}"
></span>
<router-link to="/teachingrecord">授课记录</router-link>
</a-menu-item>
<a-menu-item key="sub24-3">
<span
:class="{
circleActive: selectedKeys[0] === 'sub24-3' ? true : false,
circle: selectedKeys[0] === 'sub24-3' ? false : true,
}"
></span>
<router-link to="/lecturerfeemanagement">讲师费管理</router-link>
</a-menu-item>
<a-menu-item key="sub24-4">
<span
:class="{
circleActive: selectedKeys[0] === 'sub24-4' ? true : false,
circle: selectedKeys[0] === 'sub24-4' ? false : true,
}"
></span>
<router-link to="/lecturerfeestatistics">讲师费统计</router-link>
</a-menu-item>
<a-menu-item key="sub24-5">
<span
:class="{
circleActive: selectedKeys[0] === 'sub24-5' ? true : false,
circle: selectedKeys[0] === 'sub24-5' ? false : true,
}"
></span>
<router-link to="/organization">培训发生组织</router-link>
</a-menu-item>
</a-sub-menu>
<a-menu-item key="sub13" @titleClick="titleClick" v-if="checkMenu('articlemanage')">
<div class="imgBox">
<img
@@ -623,8 +676,6 @@
</div>
<router-link to="/dictmanage">字典管理</router-link>
</a-menu-item>
</a-menu>
</div>
<div
@@ -687,6 +738,9 @@
<a-menu-item key="sub22" @titleClick="titleClick">
<router-link to="/tooldown">教师专区</router-link>
</a-menu-item>
<a-menu-item key="sub24" @titleClick="titleClick">
<router-link to="/lecturerlist">讲师管理</router-link>
</a-menu-item>
<a-menu-item key="sub13" @titleClick="titleClick">
<router-link to="/articlemanage">文章</router-link>
</a-menu-item>
@@ -710,7 +764,6 @@
<a-menu-item key="sub20" v-if="checkMenu('dictmanage')">
<router-link to="/dictmanage">字典</router-link>
</a-menu-item>
</a-menu>
</div>
</div>
@@ -752,6 +805,12 @@ export default {
"sub21",
"sub22",
"sub23",
"sub24",
"sub25",
"sub26",
"sub27",
"sub28",
"sub29",
],
openKeys: localStorage.getItem("openKeys")
? JSON.parse(localStorage.getItem("openKeys"))
@@ -968,11 +1027,47 @@ export default {
pagename: "问答管理",
},
{
href: "/evaluationupload",
href: "/insidelecturer",
openKeys: "sub23",
selectedKeys: "sub23-1",
pagename: "测评上传",
},
{
href: "/lecturerlist",
openKeys: "sub24",
selectedKeys: "sub24-1",
pagename: "讲师列表",
},
{
href: "/teachingrecord",
openKeys: "sub24",
selectedKeys: "sub24-2",
pagename: "授课记录",
},
{
href: "/lecturerfeemanagement",
openKeys: "sub24",
selectedKeys: "sub24-3",
pagename: "讲师费管理",
},
{
href: "/lecturerfeestatistics",
openKeys: "sub24",
selectedKeys: "sub24-4",
pagename: "讲师费统计",
},
{
href: "/organization",
openKeys: "sub24",
selectedKeys: "sub24-5",
pagename: "培训发生组织",
},
{
href: "/lecturerSystem",
openKeys: "sub29",
selectedKeys: "sub29",
pagename: "讲师体系管理",
},
{
href: "/download",
openKeys: "sub15",

View File

@@ -13,7 +13,7 @@
/>
</template>
<script setup>
import {defineProps, defineExpose, ref, computed, onMounted, defineEmits, nextTick} from "vue";
import {defineProps, defineExpose, ref, computed, onMounted, defineEmits, nextTick,watch} from "vue";
import {usePage, useRequest, useRowsPageNoInit} from "@/api/request";
import {useResetRef} from "@/utils/useCommon";
@@ -65,7 +65,15 @@
showSizeChanger:{
type:Boolean,
default:false
}
},
selectedRowKeys:{
type: Array,
default: () => []
},
selectedRows:{
type: Array,
default: () => []
},
});
const emit = defineEmits(["update:params", "update:selectedRowKeys", "update:selectedRows"]);
const rowSelectKeys = ref([]);
@@ -74,7 +82,12 @@
const postParam = computed(() => ({ ...params.value, ...props.params }));
const { data, loading, total, fetch: onFetch } = props.request(props.url, postParam);
watch(()=>data.value,(val)=>{
if(val&&props.selectedRowKeys){
rowSelectKeys.value = props.selectedRowKeys
selectsData.value = props.selectedRows
}
})
const rowSelection = computed(() => (props.type ? {
type: props.type,
columnWidth: 20,
@@ -108,6 +121,22 @@
function onSelectChange(e, l) {
rowSelectKeys.value = e;
const array = []
l.includes(undefined)&&(
selectsData.value = [...selectsData.value,...l],
selectsData.value = selectsData.value.filter(t => t),
selectsData.value = Array.from(new Set(selectsData.value.map(item => item.id)))
.map(id => selectsData.value.find(item => item.id === id)),
selectsData.value.map(item=>{
e.some(i=>{
if(item.id === i){
array.push(item)
return true
}
})
}),
l = array
)
selectsData.value = l;
emit("update:selectedRowKeys", e);
emit("update:selectedRows", l);
@@ -160,6 +189,7 @@
const fetch = () => nextTick(onFetch);
defineExpose({ fetch, reset, resetSelected, clear, toLoading, remove , params });
</script>

View File

@@ -22,7 +22,7 @@
<div class="body">
<div><span>{{ content }}</span></div>
</div>
<div class="del_btnbox">
<div class="del_btnbox" v-if="isCloseBtn">
<div class="del_btn btn2" @click="close" v-if="cancel">
<div class="btnText">取消</div>
</div>
@@ -30,6 +30,11 @@
<div class="btnText">确定</div>
</div>
</div>
<div class="del_btnbox" v-else>
<div class="del_btn btn2" @click="close">
<div class="btnText">关闭</div>
</div>
</div>
</div>
</div>
</a-modal>
@@ -64,6 +69,10 @@ const props = defineProps({
type: {
type: Number,
default: 1
},
isCloseBtn: {
type: Boolean,
default: true
}
});
const types = {

View File

@@ -0,0 +1,646 @@
<template>
<div>
<a-modal
:visible="showWork"
:footer="null"
:closable="closableQR"
wrapClassName="codeModal"
style="margin-top: 400px"
:zIndex="9"
@cancel="qr_exit"
>
<div class="QR">
<div class="qr_header"></div>
<div class="qr_main">
<div class="qrm_header">
<span style="title">{{ title }}</span>
<div class="close_exit" @click="closeCodeModal"></div>
</div>
<div class="line"></div>
<div class="contents">
<div class="drawerMain">
<div class="main">
<div class="minatitl">
<div class="up1">请下载</div>
<div class="up2" @click="downTemplate" style="cursor: pointer">
模板
</div>
<div class="up1">按要求填写数据并导入</div>
</div>
<div class="upload">
<div class="text">上传</div>
<div class="right">
<div style="height: 176px; margin-bottom: 20px">
<a-upload-dragger v-model:fileList="fileList" :action="importHomeWork" name="uploadFile"
:headers="headers"
@change="handleChange" :showUploadList="false">
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text">点击或将文件拖拽到此处上传</p>
<p class="ant-upload-hint">支持扩展名.xls/.xlsx</p>
</a-upload-dragger>
</div>
<div class="loadstate">
<div class="loadborder" v-if="uploadpercent < 100 && uploadpercent !== -1">
<div class="content">
<div class="img"></div>
<div class="timebox">
<div class="timetop">
<div class="tit">{{ fileName }}</div>
<div class="stateloading">正在上传</div>
</div>
<a-progress :percent="uploadpercent" />
</div>
<div class="curloading">
</div>
</div>
</div>
<div class="loadborder" v-if="uploadErr">
<div class="content">
<div class="img"></div>
<div class="timebox">
<div class="timetop">
<div class="tit">{{ fileName }}</div>
<div class="statedefeat">上传失败</div>
</div>
<a-progress :percent="uploadpercent" />
</div>
<div class="curloading">
<div style="color: #387df7; margin-left: 20px; cursor: pointer">
下载失败数据
</div>
</div>
<div class="defeat" style="display: flex; align-items: center">
<div style="color: #ff7474">
{{ succNum }}条数据导入成功{{ errNum }}条数据导入失败
</div>
</div>
</div>
</div>
<div class="loadborder" v-if="uploadpercent === 100">
<div class="content">
<div class="img"></div>
<div class="timebox">
<div class="timetop">
<div class="tit">{{ fileName }}</div>
<div class="statesucce">上传成功</div>
</div>
<a-progress :percent="uploadpercent" />
</div>
<div class="curloading">
<div class="cancel" style="margin-left: 20px; cursor: pointer" @click="removeUpload">
删除
</div>
</div>
</div>
<div v-if="errNum">
<div class="downloadErr" @click="downloadEeeorData">
下载失败数据
</div>
</div>
</div>
<div v-if="uploadpercent === 100" class="defeat" style="
display: flex;
align-items: center;
width: 500px;
height: 40px;" :style="{
background: errNum
? 'rgba(255, 116, 116, 0.1)'
: 'rgba(53, 174, 105, 0.1)',
border: errNum ? '1px solid #ff7474' : '1px solid #35AE69',
}">
<img style="width: 14px; height: 14px; margin-left: 16px" :src="
errNum
? require('../../assets/images/err.png')
: require('../../assets/images/success.png')
" />
<div style="margin-left: 8px" :style="{ color: errNum ? '#ff7474' : 'rgba(0,0,0,0.65)' }">
{{ succNum }}条数据导入成功{{ errNum }}条数据导入失败
</div>
</div>
</div>
</div>
</div>
</div>
<div class="btnn">
<button class="btn1" @click="closeCodeModal">取消</button>
<button class="btn2" @click="closeCodeModal">确定</button>
</div>
</div>
</div>
</div>
</div>
</a-modal>
</div>
</template>
<script>
import { reactive, toRefs, watch,ref,computed } from "vue";
import { message } from "ant-design-vue";
import { useStore } from "vuex";
import * as api from "../../api/index1";
import {getCookieForName} from "@/api/method";
export default {
name: "importWork",
components: {
},
props: {
showWork: {
type: Boolean,
default: false,
},
url: {
type: String,
default: null,
},
title:{
type: String,
default: null,
},
template:{
type: String,
default: null,
},
},
setup(props, ctx) {
const state = reactive({
fileType: ["xls", "xlsx"],
importHomeWork:
process.env.VUE_APP_BASE_API + props.url,
uploadpercent: -1,
uploadErr: false, //上传失败
addLoading: false,
fileList: [],
succNum: 0, //成功数据数
errNum: 0, //失败数据数
downloadErrUrl: null, //下载失败数据
fileName: "",
});
const headers = { token: getCookieForName("token") };
const closeCodeModal = () => {
ctx.emit("update:showWork", false);
state.fileList = [];
state.uploadpercent = -1;
state.uploadErr = false; //上传失败
};
//上传文件
const handleChange = (info) => {
if (info) {
var FileExt = info.file.name.replace(/.+\./, "");
if (["xls", "xlsx"].indexOf(FileExt.toLowerCase()) === -1) {
state.fileList = [];
state.uploadpercent = -1;
message.destroy();
message.error("请按模板格式上传文件");
return;
}
}
let isLt1M = info.file.size < 512000000;
console.log(info.file.size, isLt1M)
if (!isLt1M) {
state.fileList = [];
state.uploadpercent = -1;
message.destroy();
message.error("文件大小超出500M,请重新上传");
return;
}
state.addLoading = true;
state.uploadpercent = parseInt(info.file.percent);
console.log("我是文件上传的进度---------->", info.file.percent);
const status = info.file.status;
console.log(info.file,'status')
if (status !== "uploading") {
// console.log(info.file, info.fileList);
}
if (status === "done") {
state.fileName = info.file.name;
if(!info.file.response.data){
message.error(`${info.file.name}上传失败`);
}
let i = 0;
let timeouts = setTimeout(() => {
// clearInterval(timer)
state.addLoading = false;
message.destroy();
message.error(`文件导入超时`);
}, 30000);
// let timer = setInterval(() => {
// let uid = info.file.response.data;
// api
// .getImportStatus(uid)
// .then((res) => {
// console.log("查询导入状态", res);
// if (res.data.code === 200) {
// if (res.data.data.status !== "START") {
// i++;
// if (i === 1) {
// message.destroy();
// message.success(`${info.file.name}上传成功`);
// state.addLoading = false;
// props.searchTaskList && props.searchTaskList();
// }
// state.succNum = res.data.data.successNum;
// state.errNum = res.data.data.failedNum;
// state.downloadErrUrl = res.data.data.url;
// console.log("props.getStudent", props.getStudent);
// clearInterval(timer);
// clearTimeout(timeouts);
// }
// } else {
// state.addLoading = false;
// message.destroy();
// clearTimeout(timeouts);
// }
// })
// .catch((err) => {
// state.addLoading = false;
// clearInterval(timer);
// clearTimeout(timeouts);
// console.log("查询导入状态失败", err);
// });
// }, 500);
} else if (status === "error") {
state.uploadErr = true;
message.error(`${info.file.name}上传失败`);
}
};
//删除
const removeUpload = () => {
state.fileList = [];
state.uploadpercent = -1;
state.uploadErr = false; //上传失败
state.succNum = 0;
state.errNum = 0;
state.downloadErrUrl = null;
state.addLoading = false;
};
// 下载失败数据
const downloadEeeorData = () => {
if (state.downloadErrUrl !== "") {
window.open(process.env.VUE_APP_FILE_PATH + state.downloadErrUrl);
}
};
function downTemplate() {
window.open(`${process.env.VUE_APP_BASE}/template/${props.template}.xlsx`);
}
return {
...toRefs(state),
closeCodeModal,
handleChange,
downTemplate,
headers,
removeUpload,
downloadEeeorData,
};
},
}
</script>
<style lang="scss" scoped>
.codeModal {
.ant-modal {
.ant-modal-content {
width: 479px !important;
.ant-modal-body {
.QR {
z-index: 11;
width: 700px;
background: #ffffff;
box-shadow: 0px 1px 35px 0px rgba(118, 136, 166, 0.21);
position: absolute;
left: 50%;
top: 10%;
transform: translate(-50%, -50%);
.qr_header {
position: absolute;
width: calc(100%);
height: 40px;
// background: linear-gradient(
// rgba(78, 166, 255, 0.2) 0%,
// rgba(78, 166, 255, 0) 100%
// );
}
.qr_main {
width: 100%;
position: relative;
.qrm_header {
display: flex;
align-items: center;
padding-top: 20px;
padding-left: 26px;
font-size: 16px;
.title {
font-size: 16px;
font-weight: 600;
color: #333333;
line-height: 22px;
}
.close_exit {
position: absolute;
right: 42px;
cursor: pointer;
width: 20px;
height: 20px;
background-image: url(@/assets/images/coursewareManage/close.png);
background-size: 100% 100%;
}
}
.line{
height: 1px;
margin-top: 16px;
background-color: #f0f0f0;
}
.contents{
display: flex;
min-height: 500px;
.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;
flex-shrink: 0;
.headerTitle {
font-size: 18px;
font-weight: 600;
color: #333333;
line-height: 25px;
// margin-left: 24px;
}
}
.main {
overflow-y: auto;
.minatitl {
display: flex;
margin-top: 20px;
.up1 {
font-size: 16px;
font-weight: 400;
color: #333333;
}
.up2 {
font-size: 16px;
font-weight: 400;
color: #4ea6ff;
margin-left: 4px;
}
}
.upload {
margin-top: 32px;
display: flex;
.text {
font-size: 14px;
font-weight: 400;
color: #333333;
}
.right {
margin-left: 6px;
.load {
width: 500px;
height: 176px;
background: #f5f9fd;
border-radius: 4px;
// opacity: 0.3;
border: 1px dashed #caddfd;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
.cloud {
margin-top: 52px;
width: 28px;
height: 28px;
background-image: url(../../assets/images/basicinfo/cloud.png);
}
.tip {
font-size: 14px;
font-weight: 400;
color: #4ea6ff;
margin-top: 15px;
cursor: pointer;
}
.tipz {
font-size: 14px;
font-weight: 400;
color: #999999;
margin-top: 10px;
}
}
.loadstate {
width: 500px;
margin-bottom: 100px;
.loadborder {
width: 500px;
height: 173px;
border-radius: 4px;
border: 1px dashed #eaeaea;
margin-bottom: 30px;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
.content {
display: flex;
margin-left: 20px;
position: relative;
.defeat {
width: 400px;
position: absolute;
left: 46px;
top: 38px;
font-size: 14px;
font-weight: 500;
justify-content: space-between;
}
.img {
width: 30px;
height: 34px;
background-image: url(../../assets/images/basicinfo/exl.png);
}
.timebox {
margin-left: 15px;
margin-top: -5px;
.timetop {
display: flex;
width: 262px;
justify-content: space-between;
// margin-bottom: 8px;
.tit {
font-size: 14px;
font-weight: 400;
color: #333333;
}
.stateloading {
font-size: 14px;
font-weight: 400;
color: #4ea6ff;
}
.statedefeat {
font-size: 14px;
font-weight: 400;
color: #ff7474;
}
.statesucce {
font-size: 14px;
font-weight: 400;
color: #35ae69;
}
}
.prog {
width: 262px;
height: 5px;
background: #eaf1fe;
border-radius: 4px;
.inprogloading {
width: 55%;
height: 5px;
border-radius: 4px;
background: #4ea6ff;
}
//下载失败条
.inprogdefeat {
width: 55%;
height: 5px;
border-radius: 4px;
background: #ff7474;
}
//下载成功条
.inprogsucce {
width: 100%;
height: 5px;
border-radius: 4px;
background: #57c887;
}
}
}
.curloading {
margin-left: 15px;
margin-top: 15px;
display: flex;
.cur {
font-size: 14px;
font-weight: 400;
color: #333333;
}
.cancel {
font-size: 14px;
font-weight: 400;
color: #387df7;
}
}
}
.downloadErr {
width: 120px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 2px;
border: 1px solid #387df7;
font-size: 14px;
font-weight: 400;
color: #387df7;
line-height: 20px;
cursor: pointer;
margin-left: 66px;
margin-top: 16px;
position: absolute;
bottom: 28;
}
}
}
}
}
}
.btnn {
height: 72px;
width: 100%;
position: absolute;
background-color: #fff;
bottom: 0;
left: 0;
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>

View File

@@ -0,0 +1,523 @@
<template>
<div class="CommonStudent">
<a-drawer destroyOnClose :visible="visiable" class="drawerStyle ProjCheckship CommonStudent" placement="right" width="40%">
<div class="drawerMain" id="ProjCheckship" style="">
<div class="header">
<div class="headerTitle">
选择{{title}}
</div>
<img style="width: 29px; height: 29px; cursor: pointer" src="../../assets/images/basicinfo/close.png"
@click="closeDrawer"/>
</div>
<div style="display: flex; overflow-x: auto; overflow-y: auto">
<div class="tabs" style="min-width: 800px">
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane :key="1" tab="快速选人">
<div :style="{ height: screenHeight - 235 + 'px' }">
<div class="tab1">
<a-form-item label="姓名">
<a-input v-model:value="nameSearch.keyword" style="width: 270px; height: 40px; border-radius: 8px"
placeholder="请输入姓名"/>
<a-button type="primary" @click="onSearchStu" style="margin-left: 20px; border-radius: 4px">
<template #icon>
<SearchOutlined/>
</template>
搜索
</a-button>
<a-button type="primary" @click="resetStu" style="margin-left: 20px; border-radius: 4px">重置
</a-button>
</a-form-item>
</div>
<div class="chooseLeft" style="display: grid; grid-template-columns: 250px auto">
<div :style="{
height: screenHeight - 180 + 'px',
overflowY: 'auto',
}" style="border: 1px solid #f0f0f0">
<div class="tree" style="margin: 10px 4px 220px 10px">
<a-tree allow-clear tree-default-expand-all :tree-data="treeData" :loading="orgLoading"
:load-data="onLoadData" v-model:selectedKeys="stuTreeSelectKeys"
v-model:expandedKeys="stuTreeExpandedKeys" :fieldNames="{
children: 'treeChildList',
key: 'id',
title: 'name',
value: 'name',
}" @select="stuStuOrgSelect">
</a-tree>
</div>
</div>
<div class="tableBox tabb" style="
margin: 0px 4px 120px 10px;
border: 1px solid #f0f0f0;
">
<BaseTable ref="stuTableRef" :columns="stuColumns" :url="USER_LIST_PAGE" pageKey="pageNo"
v-model:params="nameSearch" :request="useNewRowsPageNoInit"
v-model:selectedRows="stuSelectRows" v-model:selectedRowKeys="stuSelectRowKeys" type="checkbox"></BaseTable>
</div>
</div>
</div>
</a-tab-pane>
</a-tabs>
</div>
<div class="right1" style="min-width: 200px">
<div class="onerow">
<div class="onleft">
<div class="already">已选</div>
</div>
</div>
<div :style="{ 'max-height': screenHeight - 235 + 'px' }" style="overflow-y: auto">
<div class="selecteds">
<div class="person">快速选人</div>
<div v-for="(item, i) in stuSelectRows" :key="i">
<div v-if="i < 11">
<div class="chose">
{{ item.realName }}
<div class="ch" @click="stuTableRef.remove(i)"></div>
</div>
</div>
<div v-else>
<div v-if="person">
<div class="chose">
{{ item.realName }}
<div class="ch" @click="stuTableRef.remove(i)"></div>
</div>
</div>
</div>
</div>
<div v-if="!person && stuSelectRows.length > 10" class="ifsw">
<div @click="person = !person" class="“sw”">查看更多></div>
</div>
<div v-if="person && stuSelectRows.length > 10" class="ifsw">
<div @click="person = !person" class="sw">收起&lt;</div>
</div>
</div>
</div>
</div>
</div>
<div style="display: flex;justify-content: center;" class="btnn" >
<button class="btn2" style="width: 100px; height:38px;background:
#4ea6ff;border: none;margin-right: 15px; border-radius: 8px;color:aliceblue" @click="closeDrawer">取消</button>
<button class="btn2" style="width: 100px;
height:38px;background: #4ea6ff;border: none; border-radius: 8px ;color:aliceblue" @click="submitAuth">确定</button>
</div>
</div>
</a-drawer>
<a-button @click="openDrawer" type="link" style="width:100%">
<slot></slot>
</a-button>
</div>
</template>
<script setup>
import {message} from "ant-design-vue";
import {computed, defineEmits, defineProps, ref, watch,onMounted} from "vue";
import {useNewRowsPageNoInit, request, useRequest, useTotalPage} from "@/api/request";
import {
saveStu,
} from "@/api/index1";
import dialog from "@/utils/dialog";
import BaseTable from "@/components/common/BaseTable";
import {AUDIENCE_LIST, ORG_CHILD_LIST, ORG_LIST, STUDENT_LIST, USER_LIST_PAGE} from "@/api/apis";
import {addTutor,getOnlineLearningList}from '@/api/examineApi.js'
import {boeRequest} from "@/api/request";
const emit = defineEmits({});
const props = defineProps({
arrayList: {
type: Array,
default: () => []
},
type: {
type: Number,
default: 0
},
title: {
type: String,
default: ''
}
});
onMounted(()=>{
// onlineLearningList()
})
const courseList = ref([])
const onlineLearningList = () =>{
boeRequest('/activityApi/examine/getOnlineLearningList post').then(res=>{
courseList.value = res.data.filter(item=>item.status == 0).map(item => item.courseId)
})
}
const stuSelectRows = ref([]);
const stuSelectRowKeys = ref([]);
const stuTableRef = ref();
const teaunm = ref([])
watch(stuSelectRows,(val)=>{
if(val.length == 0){
teaunm.value = []
return
}
teaunm.value = val.map((res,index)=>{
return {
userName:res?.realName,
userNo:res?.userNo,
type: props.type,
userId: res?.id,
label: res?.realName + res?.userNo,
...res
}
})
},{deep:true})
const person = ref(false);
const visiable = ref(false);
const activeKey = ref(1);
const nameSearch = ref({
keyword: "",
departId: '',
});
const stuTreeSelectKeys = ref([]);
const stuTreeExpandedKeys = ref([]);
const audienceName = ref({
keyword: "",
});
const { data: treeData, loading: orgLoading } = useRequest(
ORG_LIST,
{ keyword: "" },
);
const stuColumns = ref([
{
title: "姓名",
dataIndex: "realName",
key: "realName",
width: 80,
align: "center",
className: "h",
ellipsis: true,
},
{
title: "工号",
dataIndex: "userNo",
key: "userNo",
width: 80,
align: "center",
className: "h",
ellipsis: true,
},
{
title: "归属组织",
dataIndex: "orgName",
key: "orgName",
width: 80,
align: "center",
className: "h",
ellipsis: true,
},
{
title: "部门",
dataIndex: "departName",
key: "departName",
width: 80,
align: "center",
className: "h",
ellipsis: true,
},
]);
const screenHeight = ref(document.body.clientHeight);
const closeDrawer = () => {
deleteDepSelect();
visiable.value = false;
nameSearch.value.keyword = "";
};
function onLoadData(treeNode) {
return request(ORG_CHILD_LIST, { keyword: "", orgId: treeNode.id }).then(
(r) => {
treeNode.dataRef.treeChildList = r.data;
treeData.value = [...treeData.value];
}
);
}
const openDrawer = () => {
visiable.value = true;
stuSelectRows.value = props.arrayList
stuSelectRowKeys.value = props.arrayList.map(item=>item.userId)
};
function onSearchStu() {
stuTableRef.value.reset(nameSearch.value);
}
function stuStuOrgSelect(e) {
nameSearch.value.departId = e.join("");
stuTableRef.value.fetch();
}
const resetStu = () => {
nameSearch.value.keyword = "";
stuTableRef.value.reset({ keyword: "", departId: '' });
};
//清空选择部门信息
const deleteDepSelect = () => {
stuSelectRows.value = [];
};
//确定添加授权
const submitAuth = () => {
emit('update:arrayList',teaunm.value)
emit('valueChange',teaunm.value,props.type)
visiable.value = false;
emit("finash", false);
};
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
watch(visiable, () => {
audienceName.value.keyword = "";
nameSearch.value.departId = '';
stuTreeExpandedKeys.value = [];
stuTreeSelectKeys.value = [];
nameSearch.value.keyword = "";
audienceName.value.keyword = "";
if (!visiable.value) {
stuTableRef.value && stuTableRef.value.clear();
stuTableRef.value && stuTableRef.value.reset({ keyword: "", departId: '' });
}
});
</script>
<style lang="scss">
.CommonStudent > .ant-drawer-content-wrapper {
min-width: 1200px !important;
width: 1200px !important;
}
.CommonStudent {
.ant-btn-primary {
background-color: #4ea6ff !important;
}
.cus-select {
height: 40px;
border-radius: 8px;
}
.tableBox .ant-table-row .ant-table-cell {
height: 48px;
font-size: 14px;
font-weight: 400;
color: #4f5156;
line-height: 29px;
padding: 0px;
}
.tableBox .ant-table-thead tr th {
font-size: 14px;
}
.ant-tabs-tabpane {
height: 100%;
}
.ant-tabs {
overflow: visible;
}
.right1 {
border-left: 1px solid #f2f6fe;
margin-left: 20px;
.onerow {
display: flex;
justify-content: space-between;
align-items: center;
margin-right: 40px;
flex-wrap: wrap;
width: 100%;
.onleft {
display: flex;
text-align: center;
.already {
color: rgba(51, 51, 51, 1);
font-size: 16px;
font-weight: 500;
margin-left: 32px;
white-space: nowrap;
// margin-bottom: 20px;
}
.count {
color: #4ea6ff;
font-size: 16px;
margin: 0 6px;
}
.peo {
color: rgba(51, 51, 51, 1);
font-size: 16px;
font-weight: 500;
}
}
.clbox {
margin-right: 50px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
width: 104px;
height: 32px;
border-radius: 4px;
background: #4ea6ff;
.colose {
width: 16px;
height: 16px;
// border-radius: 8px;
// background: #ffffff;
// position: relative;
background-image: url(../../assets/images/basicinfo/ch.png);
background-size: 100%;
margin-right: 4px;
}
.allclear {
color: rgba(255, 255, 255, 1);
font-size: 14px;
}
}
}
.selecteds {
display: flex;
flex-wrap: wrap;
margin-left: 32px;
.person {
width: 100%;
margin-top: 20px;
border-top: 1px solid #f2f6fe;
}
.chose {
width: 64px;
height: 24px;
margin-top: 25px;
margin-right: 25px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 2px;
border: 1px solid rgba(56, 139, 225, 1);
color: rgba(56, 139, 225, 1);
font-size: 12px;
position: relative;
.ch {
position: absolute;
width: 18px;
height: 18px;
background-image: url(../../assets/images/basicinfo/ch.png);
right: -8px;
top: -8px;
}
}
.ifsw {
display: flex;
align-items: end;
justify-content: center;
color: #4ea6ff;
}
.sw {
display: flex;
align-items: center;
justify-content: center;
text-align: justify;
color: #4ea6ff;
margin-top: 23px;
margin-left: 10px;
}
.dept {
width: 100%;
margin-top: 30px;
border-top: 1px solid #f2f6fe;
}
.chose1 {
//width: 90px;
height: 24px;
margin-top: 25px;
margin-right: 25px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 2px;
border: 1px solid rgba(56, 139, 225, 1);
color: rgba(56, 139, 225, 1);
font-size: 12px;
position: relative;
.span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ch1 {
position: absolute;
width: 18px;
height: 18px;
background-image: url(../../assets/images/basicinfo/ch.png);
right: -8px;
top: -8px;
}
}
.group {
width: 100%;
margin-top: 30px;
border-top: 1px solid #f2f6fe;
}
.chose2 {
//width: 120px;
height: 24px;
margin-top: 25px;
margin-right: 25px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 2px;
border: 1px solid rgba(56, 139, 225, 1);
color: rgba(56, 139, 225, 1);
font-size: 12px;
position: relative;
.span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ch2 {
position: absolute;
width: 18px;
height: 18px;
background-image: url(../../assets/images/basicinfo/ch.png);
right: -8px;
top: -8px;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,265 @@
<template>
<div class="twoDimensionalCode">
<!--选择教师专长页面 -->
<a-modal
:visible="showContent"
:footer="null"
:closable="closableQR"
wrapClassName="codeModal"
style="margin-top: 400px"
:zIndex="9999"
@cancel="qr_exit"
>
<div class="QR">
<div class="qr_header"></div>
<div class="qr_main">
<div class="qrm_header">
<span style="title">{{Addtitle}}</span>
<div class="close_exit" @click="closeCodeModal"></div>
</div>
<div class="line"></div>
<div class="content">
<div class="left">
<a-tree
checkable
:tree-data="treeData"
v-model:expandedKeys="expandedKeys"
v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys"
@check="onCheck"
:fieldNames="{
key: 'id',
title: 'name',
value: 'name',
}"
>
<!-- <template #title0010><span style="color: #1890ff">sss</span></template> -->
</a-tree>
</div>
<div class="right">
<div class="headers">
<div>已选择标签<span style="color: #4ea6ff;margin-left:5px;">{{treeAddData?.length}}</span></div>
<div class="header_right" @click="clearTree">清空</div>
</div>
<div class="tags">
<div
class="tag"
v-for="(item, index) in treeAddData"
:key="index"
>
<div>{{ item?.name }}</div>
<div @click="deleteTree(item)" class="tag_delete">+</div>
</div>
</div>
</div>
</div>
</div>
<div class="footer">
<a-button style="margin-right: 20px;" @click="closeCodeModal">取消</a-button>
<a-button type="primary" @click="queryCreate">确定</a-button>
</div>
</div>
</a-modal>
<!--二维码页面 -->
</div>
</template>
<script>
import { reactive, toRefs, watch,ref,computed } from "vue";
import { message } from "ant-design-vue";
import { useStore } from "vuex";
export default {
name: "AddContent",
components: {
},
props: {
showContent: {
type: Boolean,
default: false,
},
Addtitle:{
type:String,
default: true,
},
AddContentList:{
type:Array,
default: ()=>[],
}
},
setup(props, ctx) {
const state = reactive({
qrcodeSize: 800,
codeInfo: {},
courseUrl: "https://www.baidu.com/",
showUrl: false,
domain: location.protocol+'//'+location.host,
treeAddData:[],
});
const store = useStore();
const treeData = computed(() => store.state.content_type)
watch(()=>props.showContent, (val) => {
if(!val){
expandedKeys.value = []
selectedKeys.value = []
checkedKeys.value = []
}else{
state.treeAddData = props?.AddContentList
checkedKeys.value = props?.AddContentList?.map(item=>item.id)
}
});
const onCheck = (checkedKeys, {checked: bool, checkedNodes, node, event}) => {
state.treeAddData = checkedNodes.filter(node => !node.children || node.children.length === 0);
console.log(state.treeAddData,'state.treeAddData')
}
const deleteTree = (item) => {
state.treeAddData = state.treeAddData.filter(node => node.id !== item.id);
checkedKeys.value = state.treeAddData.map(item=>item.id)
}
const clearTree = () => {
state.treeAddData = [];
checkedKeys.value = [];
}
const queryCreate = () => {
ctx.emit("update:AddContentList", state.treeAddData);
closeCodeModal()
}
const closeCodeModal = () => {
ctx.emit("update:showContent", false);
};
const expandedKeys = ref([]);
const selectedKeys = ref([]);
const checkedKeys = ref([]);
return {
...toRefs(state),
closeCodeModal,
queryCreate,
deleteTree,
clearTree,
onCheck,
expandedKeys,
selectedKeys,
checkedKeys,
treeData,
};
},
};
</script>
<style scoped lang="scss">
.twoDimensionalCode {
}
.codeModal {
.ant-modal {
.ant-modal-content {
width: 479px !important;
.ant-modal-body {
.QR {
z-index: 9999;
width: 700px;
background: #ffffff;
box-shadow: 0px 1px 35px 0px rgba(118, 136, 166, 0.21);
position: absolute;
left: 50%;
top: 10%;
transform: translate(-50%, -50%);
.qr_header {
position: absolute;
width: calc(100%);
height: 40px;
// background: linear-gradient(
// rgba(78, 166, 255, 0.2) 0%,
// rgba(78, 166, 255, 0) 100%
// );
}
.qr_main {
width: 100%;
position: relative;
.qrm_header {
display: flex;
align-items: center;
padding-top: 20px;
padding-left: 26px;
font-size: 16px;
.title {
font-size: 16px;
font-weight: 600;
color: #333333;
line-height: 22px;
}
.close_exit {
position: absolute;
right: 42px;
cursor: pointer;
width: 20px;
height: 20px;
background-image: url(@/assets/images/coursewareManage/close.png);
background-size: 100% 100%;
}
}
.line{
height: 1px;
margin-top: 16px;
background-color: #e8e8e8;
}
.content{
display: flex;
min-height: 500px;
.left{
width: 50%;
border-right: 1px solid #e8e8e8;
padding: 30px 15px;
max-height: 600px;
overflow-y: auto;
}
.right{
width: 50%;
// max-height: 600px;
// overflow-y: auto;
.headers{
display: flex;
justify-content: space-between;
margin: 30px 15px;
.header_right{
color: #666666;
cursor: pointer;
}
}
.tags{
max-height: 500px;
overflow-y: auto;
padding: 0 15px;
.tag{
height: 44px;
padding: 0 15px;
display: flex;
justify-content: space-between;
align-items: center;
.tag_delete{
display: none;
color: #4ea6ff;
transform: rotate(45deg);
font-size: 26px;
cursor: pointer;
}
}
.tag:hover .tag_delete {
display: block;
}
.tag:hover {
background-color: aliceblue;
}
}
}
}
}
.footer{
height: 60px;
text-align: right;
padding: 13px 30px;
border-top: 1px solid #e8e8e8;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,327 @@
<template>
<div class="twoDimensionalCode">
<!--选择教师专长页面 -->
<a-modal
:visible="showContent"
:footer="null"
:closable="closableQR"
wrapClassName="codeModal"
style="margin-top: 400px"
:zIndex="9999"
@cancel="closeCodeModal"
>
<div class="QR">
<div class="qr_header"></div>
<div class="qr_main">
<div class="qrm_header">
<span style="title">{{Addtitle}}</span>
<div class="close_exit" @click="closeCodeModal"></div>
</div>
<div class="line"></div>
<div class="content">
<div class="left">
<a-tree checkable checkStrictly :tree-data="treeData" :loading="orgLoading"
:load-data="onLoadData" v-model:expandedKeys="expandedKeys"
v-model:selectedKeys="selectedKeys"
v-model:checkedKeys="checkedKeys" :fieldNames="{
children: 'treeChildList',
key: 'id',
title: 'name',
value: 'name',
}" @check="onCheck">
</a-tree>
</div>
<div class="right">
<div class="headers">
<div>已选择标签<span style="color: #4ea6ff;margin-left:5px;">{{treeAddData?.length}}</span></div>
<div class="header_right" @click="clearTree">清空</div>
</div>
<div class="tags">
<div
class="tag"
v-for="(item, index) in treeAddData"
:key="index"
>
<div class="tag_text" :title="`${item?.orgName} - (原:${item?.affiliationName || '-'})`">{{ item?.orgName }} - ({{ item?.affiliationName || '-' }})</div>
<div @click="deleteTree(item)" class="tag_delete">+</div>
</div>
</div>
</div>
</div>
</div>
<div class="footer">
<a-button style="margin-right: 20px;" @click="closeCodeModal">取消</a-button>
<a-button type="primary" @click="queryCreate">确定</a-button>
</div>
</div>
</a-modal>
</div>
</template>
<script setup>
import { reactive, toRefs, watch,ref,computed } from "vue";
import { message } from "ant-design-vue";
import { useStore } from "vuex";
import { ORG_CHILD_LIST, ORG_LIST} from "@/api/apis";
import { defineProps,defineEmits } from "vue";
import { request, useRequest} from "@/api/request";
import * as lecturerApi from "@/api/Lecturer.js";
const props = defineProps({
showContent: {
type: Boolean,
default: false,
},
Addtitle:{
type:String,
default: true,
},
AddContentList:{
type:Array,
default: ()=>[],
}
})
const emit = defineEmits({})
const treeAddData = ref([])
const { data: treeData, loading: orgLoading } = useRequest(
ORG_LIST,
{ keyword: "" },
);
function onLoadData(treeNode) {
return request(ORG_CHILD_LIST, { keyword: "", orgId: treeNode.id }).then(
(r) => {
//等待接口联调
r?.data?.map(item=>{
notLists?.value?.some(i=>{
if(i.orgId == item.id){
item.disabled = true
item.name = item.name + '(' + i.orgName + ')'
return true
}
})
return item
})
treeNode.dataRef.treeChildList = r.data;
treeData.value = [...treeData.value];
}
);
}
watch(()=>props.showContent, (val) => {
if(!val){
expandedKeys.value = []
selectedKeys.value = []
checkedKeys.value = []
}else{
console.log(props.AddContentList,'xixiixix')
getNot()
treeAddData.value = props?.AddContentList
orgLists.value = props?.AddContentList
checkedKeys.value = props?.AddContentList?.map(item=>item.orgId)
}
});
const notLists = ref([])
const getNot = () => {
lecturerApi.getUnSelectOrg().then(res=>{
if(res.data.code == 200){
notLists.value = res.data.data
}
if(treeData.value.length){
treeData.value.map(item=>{
item.name = item.name?.split('(')[0]
notLists.value.some(i=>{
if(i.orgId == item.id){
item.disabled = true
item.name = item.name + '(' + i.orgName + ')'
return true
}
})
return item
})
}
}).catch(err=>{
message.error(err.data.msg)
})
}
const orgLists = ref([])
const onCheck = async (checkedKeys, {checked: bool, checkedNodes, node, event}) => {
// "965356037047586816"
let length = treeAddData.value.length
if(checkedNodes.length > length){
await lecturerApi.getSelectOrg(node.id).then(res=>{
const targetNode = checkedNodes.find(item=>item.id == res?.data?.data[0]?.orgId)
if(targetNode){
orgLists.value.push(Object.assign(targetNode,res?.data?.data[0]));
}
}).catch(err=>{
message.error('获取被占用组织失败,请重新尝试')
})
}
treeAddData.value = checkedNodes.map(item => {
if (!item || !item.id) {
return item;
}
try {
const orgItem = orgLists.value?.find(i => i && i.orgId == item.id);
if (orgItem) {
item.isSelect = orgItem.isSelect;
item.orgName = orgItem.orgName;
item.leaders = orgItem.leaders;
item.affiliationName = orgItem.affiliationName
}
return {
orgId: item.id,
orgName: item.name,
affiliationId: item.id,
...item
};
} catch (err) {
return item;
}
});
// treeAddData.value = checkedNodes;
console.log(treeAddData.value,'checkedNodes',orgLists.value)
}
const clearTree = () => {
treeAddData.value = [];
checkedKeys.value = [];
orgLists.value = [];
}
const deleteTree = (item) => {
treeAddData.value = treeAddData.value.filter(node => node.orgId !== item.orgId);
checkedKeys.value = treeAddData.value.map(item=>item.orgId)
}
const closeCodeModal = () => {
emit("update:showContent", false);
clearTree()
};
const queryCreate = () => {
emit("update:AddContentList", treeAddData.value);
closeCodeModal()
}
const expandedKeys = ref([]);
const selectedKeys = ref([]);
const checkedKeys = ref([]);
</script>
<style scoped lang="scss">
.twoDimensionalCode {
}
.codeModal {
.ant-modal {
.ant-modal-content {
width: 479px !important;
.ant-modal-body {
.QR {
z-index: 9999;
width: 700px;
background: #ffffff;
box-shadow: 0px 1px 35px 0px rgba(118, 136, 166, 0.21);
position: absolute;
left: 50%;
top: 10%;
transform: translate(-50%, -50%);
.qr_header {
position: absolute;
width: calc(100%);
height: 40px;
// background: linear-gradient(
// rgba(78, 166, 255, 0.2) 0%,
// rgba(78, 166, 255, 0) 100%
// );
}
.qr_main {
width: 100%;
position: relative;
.qrm_header {
display: flex;
align-items: center;
padding-top: 20px;
padding-left: 26px;
font-size: 16px;
.title {
font-size: 16px;
font-weight: 600;
color: #333333;
line-height: 22px;
}
.close_exit {
position: absolute;
right: 42px;
cursor: pointer;
width: 20px;
height: 20px;
background-image: url(@/assets/images/coursewareManage/close.png);
background-size: 100% 100%;
}
}
.line{
height: 1px;
margin-top: 16px;
background-color: #e8e8e8;
}
.content{
display: flex;
min-height: 500px;
.left{
width: 50%;
border-right: 1px solid #e8e8e8;
padding: 30px 15px;
max-height: 600px;
overflow-y: auto;
}
.right{
width: 50%;
// max-height: 600px;
// overflow-y: auto;
.headers{
display: flex;
justify-content: space-between;
margin: 30px 15px;
.header_right{
color: #666666;
cursor: pointer;
}
}
.tags{
max-height: 500px;
overflow-y: auto;
padding: 0 15px;
.tag{
height: 44px;
padding: 0 15px;
display: flex;
justify-content: space-between;
align-items: center;
.tag_text{
max-width: 240px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tag_delete{
display: none;
color: #4ea6ff;
transform: rotate(45deg);
font-size: 26px;
cursor: pointer;
}
}
.tag:hover .tag_delete {
display: block;
}
.tag:hover {
background-color: aliceblue;
}
}
}
}
}
.footer{
height: 60px;
text-align: right;
padding: 13px 30px;
border-top: 1px solid #e8e8e8;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,370 @@
<template>
<a-drawer
:visible="visible"
:closable="false"
class="RouterFaceStus"
placement="right"
width="70%"
>
<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="box">
<div style="margin-bottom: 20px;font-size: 18px;font-weight: 700;">选择讲师费汇总:<span style="color:red;margin-left:20px;">{{payableExpense.toFixed(2)||0}}</span></div>
<div class="table" style="padding-bottom:72px;">
<a-table
:columns="columns"
:data-source="tableData"
:pagination="false"
:scroll="{ x: 'max-content' }"
row-key="id"
:loading="loading"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
>
<template #action="{ record }">
<div class="action">
<div style="color: #1890ff;cursor: pointer;" class="btn" v-if="selectedRowKeys.includes(record.id)" @click="removeList(record)">取消选择</div>
<div style="color: #1890ff;cursor: pointer;" class="btn" v-else @click="addList(record)">选择</div>
</div>
</template>
</a-table>
</div>
</div>
</div>
<div class="btnn">
<button class="btn1" @click="closeDrawer">取消</button>
<button class="btn2" @click="queryDrawer">下一步</button>
</div>
</div>
</a-drawer>
</template>
<script setup lang="jsx">
import {computed, defineEmits,defineProps, ref, watch} from "vue";
import * as api from '@/api/Lecturer'
import { message } from "ant-design-vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
name:{
type: String,
default: ""
}
});
watch(()=>props.visible,(val)=>{
if(val){
loading.value = true
api.getListByStatus().then(res=>{
if(res.data.code == 200 ){
tableData.value = res.data.data
}else{
message.error(res.data.msg)
}
loading.value = false
}).catch(err=>{
message.error(err.data.msg)
loading.value = false
})
}else{
selectedRowKeys.value = []
selectsData.value = []
}
})
const loading = ref(false)
const selectedRowKeys = ref([])
const selectsData = ref([]);
const onSelectChange = (e, l) => {
selectedRowKeys.value = e
selectsData.value = l
}
const emit = defineEmits(['selectedRowKeys','update:visible'])
const addList = (item) => {
selectedRowKeys.value.push(item.id)
selectsData.value.push(item)
}
const payableExpense = ref(0)
watch(()=>selectsData.value.length,(val)=>{
if(val){
payableExpense.value = selectsData.value.reduce((a,b)=>{
return Number(a) + Number(b.payableExpense)
},0)
}else{
payableExpense.value = 0
}
})
const removeList = (item) => {
selectedRowKeys.value = selectedRowKeys.value.filter(t=> t != item.id)
selectsData.value = selectsData.value.filter(t=>t.id != item.id)
}
const columns = [
{
title: '讲师名称',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '讲师工号',
dataIndex: 'userNo',
key: 'userNo',
align: 'center',
},
{
title: '所属组织',
dataIndex: 'orgName',
key: 'orgName',
align: 'center',
ellipsis: true,
customCell :() => {return {style: {maxWidth: '200px',overflow: 'hidden',whiteSpace: 'nowrap',textOverflow:'ellipsis',cursor:'pointer'}}},
},
{
title: '讲师体系',
dataIndex: 'tsystemName',
key: 'tsystemName',
align: 'center',
},
{
title: '讲师等级',
dataIndex: 'tlevelName',
key: 'tlevelName',
align: 'center',
customRender: ({ text })=>{
return text||'-'
}
},
{
title: '发薪地',
dataIndex: 'payrollPlace',
key: 'payrollPlace',
align: 'center',
customRender: ({ text })=>{
return text||'-'
}
},
{
title: '课程类型',
dataIndex: 'courseType',
key: 'courseType',
align: 'center',
customRender: ({ text,record })=>{
switch (text) {
case 0:
return "在线课"
case 1:
return "面授课"
case 2:
return "课程开发"
case 3:
return "作业员入模培训"
case 4:
return "其他"
default:
return "-"
}
}
},
{
title: '课程名称',
dataIndex: 'courseName',
key: 'courseName',
align: 'center',
},
{
title: '授课/开发课程日期',
dataIndex: 'teachingDate',
key: 'teachingDate',
align: 'center',
},
{
title: '授课/开发课程时长',
dataIndex: 'teachingTime',
key: 'teachingTime',
align: 'center',
customRender: ({ text,record })=>{
return (text/60).toFixed(2)+'小时'
}
},
{
title: '参训人数',
dataIndex: 'studys',
key: 'studys',
align: 'center',
customRender: ({ text })=>{
return text||'0'
}
},
{
title: '评分',
dataIndex: 'score',
key: 'score',
align: 'center',
customRender: ({ text })=>{
return text||'-'
}
},
{
title: '课酬基准',
dataIndex: 'levelPay',
key: 'levelPay',
align: 'center',
},
{
title: '计划费用',
dataIndex: 'expense',
key: 'expense',
align: 'center',
},
{
title: '应发费用',
dataIndex: 'payableExpense',
key: 'payableExpense',
align: 'center',
},
{
title: '操作',
align: 'center',
fixed: 'right',
width: 100,
slots: { customRender: "action" },
},
]
const tableData = ref([])
const closeDrawer = () => {
emit("update:visible", false);
selectedRowKeys.value = []
selectsData.value = []
}
const queryDrawer = () => {
if(!selectedRowKeys.value.length){
message.error('请选择需要审批的数据')
return
}
emit("selectedRowKeys", selectedRowKeys.value)
// closeDrawer()
}
</script>
<style lang="scss" scoped>
.RouterFaceStus {
.drawerMains {
min-width: 600px;
// margin: 0px 32px 0px 32px;
overflow-x: auto;
display: flex;
flex-direction: column;
.headers {
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;
}
}
.content{
margin: 0 20px;
.head{
display: flex;
align-items: center;
overflow-x: auto;
.list{
display: flex;
align-items: center;
background-color: #666;
min-width: 260px;
padding: 20px 10px;
color: #ffffff;
border-radius:6px;
margin-right: 20px;
margin-bottom: 20px;
.left{
width: 35%;
text-align:right;
margin-right:30px;
.text{
margin-top:20px;
}
}
.right{
.text{
margin-top:20px;
}
}
}
.active{
background-color: #f5f5f5;
color: #000000;
}
}
.box{
.top{
display: flex;
align-items: center;
margin-bottom: 20px;
margin-top: 10px;
.item{
margin-right: 20px;
}
}
}
}
.btnn {
height: 72px;
width: 100%;
position: absolute;
bottom: 0;
left: 0;
z-index: 9;
display: flex;
align-items: center;
justify-content: end;
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;
margin-right: 15px;
}
}
}
}
</style>

View File

@@ -0,0 +1,660 @@
<template>
<a-drawer
:visible="visible"
:closable="false"
class="RouterFaceStus"
placement="right"
width="64%"
: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="item?.summaryTotal">{{item?.summaryTotal||'-'}}</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
:columns="columns"
:data-source="searchTrue?searchList:expenseList"
:pagination="false"
:scroll="{ x: 'max-content' }"
:loading="loadingData"
>
<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));
if(!ids.length){
return message.error('暂无可提交的数据')
}
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)
}).catch(err=>{
message.destroy()
message.error(err.data.msg)
close()
closeDrawer()
emit('visibleFalse',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 clickItem = (item,i) => {
expenseList.value = item.expenseList
indexList.value = i
resetData()
}
watch(()=>props.visible,(val)=>{
if(val){
loadingData.value = true
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
}
})
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)
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.payableExpense
}
})
}
const nameUserNo = ref(null)
const dateValue = ref(null)
const searchTrue = ref(false)
const searchData = (val) => {
searchTrue.value = val
if(!nameUserNo.value&&!dateValue.value){
searchList.value = expenseList.value;
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;
});
searchList.value = filteredList;
}
const resetData = () => {
nameUserNo.value = null
dateValue.value = null
searchData(false)
}
const emit = defineEmits(['update:visible'])
const columns = [
{
title: '讲师名称',
dataIndex: 'name',
key: 'name',
align: 'center',
},
{
title: '讲师工号',
dataIndex: 'userNo',
key: 'userNo',
align: 'center',
},
{
title: '所属组织',
dataIndex: 'orgName',
key: 'orgName',
align: 'center',
ellipsis: true,
customCell :() => {return {style: {maxWidth: '200px',overflow: 'hidden',whiteSpace: 'nowrap',textOverflow:'ellipsis',cursor:'pointer'}}},
},
{
title: '讲师体系',
dataIndex: 'tsystemName',
key: 'tsystemName',
align: 'center',
},
{
title: '讲师等级',
dataIndex: 'tlevelName',
key: 'tlevelName',
align: 'center',
customRender: ({ text })=>{
return text||'-'
}
},
{
title: '发薪地',
dataIndex: 'payrollPlace',
key: 'payrollPlace',
align: 'center',
customRender: ({ text })=>{
return text||'-'
}
},
{
title: '课程类型',
dataIndex: 'courseType',
key: 'courseType',
align: 'center',
customRender: ({ text,record })=>{
switch (text) {
case 0:
return "在线课"
case 1:
return "面授课"
case 2:
return "课程开发"
case 3:
return "作业员入模培训"
case 4:
return "其他"
default:
return "-"
}
}
},
{
title: '课程名称',
dataIndex: 'courseName',
key: 'courseName',
align: 'center',
},
{
title: '授课/开发课程日期',
dataIndex: 'teachingDate',
key: 'teachingDate',
align: 'center',
},
{
title: '授课/开发课程时长',
dataIndex: 'teachingTime',
key: 'teachingTime',
align: 'center',
customRender: ({ text,record })=>{
return (text/60).toFixed(2)+'小时'
}
},
{
title: '参训人数',
dataIndex: 'studys',
key: 'studys',
align: 'center',
customRender: ({ text })=>{
return text||'0'
}
},
{
title: '评分',
dataIndex: 'score',
key: 'score',
align: 'center',
customRender: ({ text })=>{
return text||'-'
}
},
{
title: '课酬基准',
dataIndex: 'levelPay',
key: 'levelPay',
align: 'center',
},
{
title: '计划费用',
dataIndex: 'expense',
key: 'expense',
align: 'center',
},
{
title: '应发费用',
dataIndex: 'payableExpense',
key: 'payableExpense',
align: 'center',
},
{
title: '操作',
align: 'center',
fixed: 'right',
slots: { customRender: "action" },
},
]
const closeDrawer = () => emit("update:visible", false);
const qureyDrawer = () => {
const ids = forData.value.flatMap(item => item.expenseList.map(item => item.id));
if(!ids.length){
return message.error('暂无可提交的数据')
}
dialog({
content: '是否确认讲师费信息无误?提交后按“培训发生组织”汇总至审批中心,等待验证后“提交”进入审批流程。',
ok: () => {
api.teacherExpenseConfirm({ids:ids?.join(',')}).then(res=>{
console.log(res,'resssss')
message.success('提交成功')
closeDrawer()
emit('visibleFalse',false)
}).catch(err=>{
message.destroy()
message.error(err.data.msg)
closeDrawer()
emit('visibleFalse',false)
})
}
})
}
const config = () => {
if(!expenseList.value.length){
message.error('暂无可提交的数据')
return
}
modalVisible.value = true;
}
</script>
<style lang="scss" scoped>
.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 30px;
margin: 34px auto 56px auto;
margin-top: 10px;
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;
}
}
}
}
.RouterFaceStus {
.drawerMains {
min-width: 600px;
// margin: 0px 32px 0px 32px;
overflow-x: auto;
display: flex;
flex-direction: column;
.headers {
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;
}
}
.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: 148px;
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;
align-items: center;
margin-bottom: 20px;
margin-top: 10px;
.item{
margin-right: 20px;
}
}
}
}
.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: 100px;
height: 40px;
background: #4ea6ff;
border-radius: 8px;
border: 0;
margin-right: 15px;
color: #fff;
}
}
}
}
</style>

View File

@@ -0,0 +1,114 @@
<!--
* @Author: wangzhiyu <w19165802736@163.com>
* @version: 1.0.0
* @Date: 2024-04-7 11:04:25
* @LastEditTime: 2024-04-08 21:44:20
* @Descripttion: 富文本编辑器组件
-->
<template>
<div style="border: 1px solid #ccc; width: 100%;position:relative; ">
<Toolbar style="border-bottom: 1px solid #ccc ;font-size:4px" :editor="editorRef" :defaultConfig="toolbarConfig" mode="simple" />
<Editor style="height: 100px; overflow-y: hidden" v-model="valueHtml" :defaultConfig="editorConfig" mode="simple" @onCreated="handleCreated" @onChange="handleChange" @customPaste="customPaste" />
<span style="position:absolute;right:6px;bottom:4px;">{{ valueLength }}/200</span>
</div>
</template>
<script setup>
// 富文本编辑器文档链接: https://www.wangeditor.com/v5/getting-started.html
// 引入富文本编辑器CSS
import '@wangeditor/editor/dist/css/style.css';
import { onBeforeUnmount, ref, shallowRef,watch,defineProps, defineEmits } from 'vue';
// 导入富文本编辑器的组件
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
const props = defineProps({
value: {
type: String,
default: ()=>'',
},
});
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef();
const emit = defineEmits(['update:value'])
// 内容 HTML
const valueHtml = ref('');
watch(()=>valueHtml.value,(val)=>{
emit('update:value',val)
})
watch(()=>props.value,(val)=>{
valueHtml.value = val
})
const toolbarConfig = {
excludeKeys: [
'insertLink', // 排除菜单组,写菜单组 key 的值即可
"group-image",
"todo",
"insertVideo",
"insertTable"
]
};
const editorConfig = ref({ placeholder: '请输入内容...', MENU_CONF: {} });
// 自定义图片上传
editorConfig.value.MENU_CONF['uploadImage'] = {
async customUpload(file, insertFn) {
const formData = new FormData();
formData.append("file", file);
fileUp(formData).then((res) => {
if (res.data.code === 200) {
// 最后插入图片 url alt href
console.log(
"上传图片结果",
process.env.VUE_APP_FILE_PATH + res.data.data
);
insertFn(
process.env.VUE_APP_FILE_PATH + res.data.data,
file.name,
res.data.data
);
}
});
},
};
// 自定义视频上传
editorConfig.value.MENU_CONF['uploadVideo'] = {
async customUpload(file, insertFn) {
console.log('上传视频', file);
},
};
const valueLength = ref(0)
const handleChange = (editor) => {
const reg = /<[^<>]+>/g;
const value = valueHtml.value.replace(reg, "");
valueLength.value = value.length
if (valueLength.value > 200) {
valueHtml.value = '<p>' + value.slice(0, 200) + '</p>'
editor.setHtml(valueHtml.value);
}
};
// 富文本编辑器生成后触发
const handleCreated = editor => {
editorRef.value = editor; // 记录 editor 实例,重要!
console.log(editorConfig.value.MENU_CONF, 'editorConfig.value');
};
// 监听富文本编辑器粘贴行为
const customPaste = (editor, event, callback) => {
// 获取粘贴的纯文本
const text = event.clipboardData.getData('text/plain');
if (text) {
editor.insertText(text);
event.preventDefault();
callback(false);
}
};
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value;
if (editor == null) return;
editor.destroy();
});
</script>

View File

@@ -0,0 +1,125 @@
<template>
<div>
<input type="file" @change="handleFileUpload" />
<img :src="avatarUrl" alt="Avatar" v-if="avatarUrl" />
</div>
</template>
<script>
export default {
data() {
return {
avatarUrl: '' // 存储头像的 URLBase64 编码或服务器 URL
};
},
methods: {
handleFileUpload(event) {
const file = event.target.files[0]; // 获取用户选择的文件
if (!file) return;
// 创建一个 FileReader 实例
const reader = new FileReader();
// 读取文件内容,并在读取完成后设置 img 的 src
reader.onload = (e) => {
this.avatarUrl = e.target.result; // e.target.result 是 Base64 编码的字符串
};
// 读取文件内容(作为 DataURL
reader.readAsDataURL(file);
// 如果你需要上传文件到服务器,可以在这里添加代码
// 例如,使用 Axios 发送 POST 请求到服务器
}
}
};
</script>
<!-- <template>
<a-upload
:file-list="files"
:action="FILE_UPLOAD_URL"
:show-upload-list="showUploadList"
:multiple="multiple"
:before-upload="beforeUpload"
:headers="headers"
@change="handleChange"
ref="imageRef"
>
<template v-for="(_, key, index) in $slots" :key="index" v-slot:[key]>
<slot :name="key"></slot>
</template>
</a-upload>
</template>
<script setup>
import {defineProps, defineEmits, defineExpose, ref, watch} from "vue";
import {message} from "ant-design-vue";
import {FILE_UPLOAD_URL} from "@/api/config";
import {getCookieForName} from "@/api/method";
const props = defineProps({
value: {
type: Array,
default: () => []
},
multiple: {
type: Boolean,
default: fals
},
showUploadList: {
type: Boolean,
default: false
},
fileType: {
type: Array,
default: () => []
}
})
const emit = defineEmits({})
const files = ref([])
const imageRef = ref()
const headers = { token: getCookieForName("token") };
watch(props, () => {
props.value.length !== files.value.length && (files.value = props.value)
})
function handleChange({file, fileList}) {
file.response && file.response.code === 200 && (file.url = file.response.data)
files.value = fileList
emit('update:value', fileList)
}
function beforeUpload(file) {
if (!props.fileType.includes(file.name.split(".").slice(-1).join(''))) {
message.error(
"不支持该格式"
);
return false;
}
}
function remove(i) {
files.value.splice(i, 1)
emit('update:value', files.value)
}
function reUpload(i) {
if (files.value[i].status === 'ready' || files.value[i].status === 'uploading') {
imageRef.value.abort(files.value[i].raw)
files.value[i].status = 'abort';
} else if (files.value[i].status === 'fail' || files.value[i].status === 'abort') {
imageRef.value.handleStart(files.value[i].raw)
imageRef.value.submit()
}
}
function abort(i) {
imageRef.value.abort(files.value[i].raw)
}
defineExpose({reUpload, remove, abort})
</script> -->

View File

@@ -0,0 +1,637 @@
<template>
<div>
<a-modal
:visible="showWork"
:footer="null"
:closable="closableQR"
wrapClassName="codeModal"
style="margin-top: 400px"
:zIndex="9"
@cancel="qr_exit"
>
<div class="QR">
<div class="qr_header"></div>
<div class="qr_main">
<div class="qrm_header">
<span style="title">{{ title }}</span>
<div class="close_exit" @click="closeCodeModal"></div>
</div>
<div class="line"></div>
<div class="contents">
<div class="drawerMain">
<div class="main">
<div class="minatitl">
<div class="up1">请下载</div>
<div class="up2" @click="downTemplate" style="cursor: pointer">
模板
</div>
<div class="up1">按要求填写数据并导入</div>
</div>
<div class="upload">
<div class="text">上传</div>
<div class="right">
<div style="height: 176px; margin-bottom: 20px">
<a-upload-dragger v-model:fileList="fileList" :action="importHomeWork" :name="fileName" :multiple="true"
:headers="headers"
@change="handleChange" :showUploadList="false">
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text">点击或将文件拖拽到此处上传</p>
<p class="ant-upload-hint">支持扩展名.xls/.xlsx</p>
</a-upload-dragger>
</div>
<div class="loadstate">
<div class="loadborder" v-if="uploadpercent < 100 && uploadpercent !== -1">
<div class="content">
<div class="img"></div>
<div class="timebox">
<div class="timetop">
<div class="tit">{{ fileName }}</div>
<div class="stateloading">正在上传</div>
</div>
<a-progress :percent="uploadpercent" />
</div>
<div class="curloading">
</div>
</div>
</div>
<div class="loadborder" v-if="uploadErr">
<div class="content">
<div class="img"></div>
<div class="timebox">
<div class="timetop">
<div class="tit">{{ fileName }}</div>
<div class="statedefeat">上传失败</div>
</div>
<a-progress :percent="uploadpercent" />
</div>
<div class="curloading">
<div style="color: #387df7; margin-left: 20px; cursor: pointer">
下载失败数据
</div>
</div>
<div class="defeat" style="display: flex; align-items: center">
<div style="color: #ff7474">
{{ succNum }}条数据导入成功{{ errNum }}条数据导入失败
</div>
</div>
</div>
</div>
<div class="loadborder" v-if="uploadpercent === 100">
<div class="content">
<div class="img"></div>
<div class="timebox">
<div class="timetop">
<div class="tit">{{ fileName }}</div>
<div class="statesucce">上传成功</div>
</div>
<a-progress :percent="uploadpercent" />
</div>
<div class="curloading">
<div class="cancel" style="margin-left: 20px; cursor: pointer" @click="removeUpload">
删除
</div>
</div>
</div>
<div v-if="errNum">
<div class="downloadErr" @click="downloadEeeorData">
下载失败数据
</div>
</div>
</div>
<div v-if="uploadpercent === 100" class="defeat" style="
display: flex;
align-items: center;
width: 500px;
height: 40px;" :style="{
background: errNum
? 'rgba(255, 116, 116, 0.1)'
: 'rgba(53, 174, 105, 0.1)',
border: errNum ? '1px solid #ff7474' : '1px solid #35AE69',
}">
<img style="width: 14px; height: 14px; margin-left: 16px" :src="
errNum
? require('../../assets/images/err.png')
: require('../../assets/images/success.png')
" />
<div style="margin-left: 8px" :style="{ color: errNum ? '#ff7474' : 'rgba(0,0,0,0.65)' }">
{{ succNum }}条数据导入成功{{ errNum }}条数据导入失败
</div>
</div>
</div>
</div>
</div>
</div>
<div class="btnn">
<button class="btn1" @click="closeCodeModal">取消</button>
<button class="btn2" @click="closeCodeModal">确定</button>
</div>
</div>
</div>
</div>
</div>
</a-modal>
</div>
</template>
<script>
import { reactive, toRefs, watch,ref,computed } from "vue";
import { message } from "ant-design-vue";
import { useStore } from "vuex";
import * as api from "../../api/index1";
import {getCookieForName} from "@/api/method";
export default {
name: "importWork",
components: {
},
props: {
showWork: {
type: Boolean,
default: false,
},
url: {
type: String,
default: null,
},
title:{
type: String,
default: null,
},
fileName:{
type: String,
default: 'file',
},
},
setup(props, ctx) {
const state = reactive({
fileType: ["xls", "xlsx"],
importHomeWork:
process.env.VUE_APP_BASE_API + props.url,
uploadpercent: -1,
uploadErr: false, //上传失败
addLoading: false,
fileList: [],
succNum: 0, //成功数据数
errNum: 0, //失败数据数
downloadErrUrl: null, //下载失败数据
fileName: "",
});
const headers = { token: getCookieForName("token") };
const closeCodeModal = () => {
ctx.emit("update:showWork", false);
state.fileList = [];
state.uploadpercent = -1;
state.uploadErr = false; //上传失败
};
//上传文件
const handleChange = (info) => {
if (info) {
var FileExt = info.file.name.replace(/.+\./, "");
if (["xls", "xlsx"].indexOf(FileExt.toLowerCase()) === -1) {
state.fileList = [];
state.uploadpercent = -1;
message.destroy();
message.error("请上传正确的文件格式");
return;
}
}
state.addLoading = true;
state.uploadpercent = parseInt(info.file.percent);
console.log("我是文件上传的进度---------->", info.file.percent);
const status = info.file.status;
console.log(info.file,'status')
if (status !== "uploading") {
// console.log(info.file, info.fileList);
}
if (status === "done") {
state.fileName = info.file.name;
if(!info.file.response.data){
message.error(`${info.file.name}上传失败`);
}
let i = 0;
let timeouts = setTimeout(() => {
// clearInterval(timer)
state.addLoading = false;
message.destroy();
message.error(`文件导入超时`);
}, 30000);
// let timer = setInterval(() => {
// let uid = info.file.response.data;
// api
// .getImportStatus(uid)
// .then((res) => {
// console.log("查询导入状态", res);
// if (res.data.code === 200) {
// if (res.data.data.status !== "START") {
// i++;
// if (i === 1) {
// message.destroy();
// message.success(`${info.file.name}上传成功`);
// state.addLoading = false;
// props.searchTaskList && props.searchTaskList();
// }
// state.succNum = res.data.data.successNum;
// state.errNum = res.data.data.failedNum;
// state.downloadErrUrl = res.data.data.url;
// console.log("props.getStudent", props.getStudent);
// clearInterval(timer);
// clearTimeout(timeouts);
// }
// } else {
// state.addLoading = false;
// message.destroy();
// clearTimeout(timeouts);
// }
// })
// .catch((err) => {
// state.addLoading = false;
// clearInterval(timer);
// clearTimeout(timeouts);
// console.log("查询导入状态失败", err);
// });
// }, 500);
} else if (status === "error") {
state.uploadErr = true;
message.error(`${info.file.name}上传失败`);
}
};
//删除
const removeUpload = () => {
state.fileList = [];
state.uploadpercent = -1;
state.uploadErr = false; //上传失败
state.succNum = 0;
state.errNum = 0;
state.downloadErrUrl = null;
state.addLoading = false;
};
// 下载失败数据
const downloadEeeorData = () => {
if (state.downloadErrUrl !== "") {
window.open(process.env.VUE_APP_FILE_PATH + state.downloadErrUrl);
}
};
function downTemplate() {
window.open(`${process.env.VUE_APP_BASE_API}/admin/student/exportHomeWorkTemplate?taskId=${props.id || ''}&type=${props.type || ''}&pid=${props.pid || ''}&thirdType=3`);
}
return {
...toRefs(state),
closeCodeModal,
handleChange,
downTemplate,
headers,
removeUpload,
downloadEeeorData,
};
},
}
</script>
<style lang="scss" scoped>
.codeModal {
.ant-modal {
.ant-modal-content {
width: 479px !important;
.ant-modal-body {
.QR {
z-index: 11;
width: 700px;
background: #ffffff;
box-shadow: 0px 1px 35px 0px rgba(118, 136, 166, 0.21);
position: absolute;
left: 50%;
top: 10%;
transform: translate(-50%, -50%);
.qr_header {
position: absolute;
width: calc(100%);
height: 40px;
// background: linear-gradient(
// rgba(78, 166, 255, 0.2) 0%,
// rgba(78, 166, 255, 0) 100%
// );
}
.qr_main {
width: 100%;
position: relative;
.qrm_header {
display: flex;
align-items: center;
padding-top: 20px;
padding-left: 26px;
font-size: 16px;
.title {
font-size: 16px;
font-weight: 600;
color: #333333;
line-height: 22px;
}
.close_exit {
position: absolute;
right: 42px;
cursor: pointer;
width: 20px;
height: 20px;
background-image: url(@/assets/images/coursewareManage/close.png);
background-size: 100% 100%;
}
}
.line{
height: 1px;
margin-top: 16px;
background-color: #666666;
}
.contents{
display: flex;
min-height: 500px;
.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;
flex-shrink: 0;
.headerTitle {
font-size: 18px;
font-weight: 600;
color: #333333;
line-height: 25px;
// margin-left: 24px;
}
}
.main {
overflow-y: auto;
.minatitl {
display: flex;
margin-top: 20px;
.up1 {
font-size: 16px;
font-weight: 400;
color: #333333;
}
.up2 {
font-size: 16px;
font-weight: 400;
color: #4ea6ff;
margin-left: 4px;
}
}
.upload {
margin-top: 32px;
display: flex;
.text {
font-size: 14px;
font-weight: 400;
color: #333333;
}
.right {
margin-left: 6px;
.load {
width: 500px;
height: 176px;
background: #f5f9fd;
border-radius: 4px;
// opacity: 0.3;
border: 1px dashed #caddfd;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
.cloud {
margin-top: 52px;
width: 28px;
height: 28px;
background-image: url(../../assets/images/basicinfo/cloud.png);
}
.tip {
font-size: 14px;
font-weight: 400;
color: #4ea6ff;
margin-top: 15px;
cursor: pointer;
}
.tipz {
font-size: 14px;
font-weight: 400;
color: #999999;
margin-top: 10px;
}
}
.loadstate {
width: 500px;
margin-bottom: 100px;
.loadborder {
width: 500px;
height: 173px;
border-radius: 4px;
border: 1px dashed #eaeaea;
margin-bottom: 30px;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
.content {
display: flex;
margin-left: 20px;
position: relative;
.defeat {
width: 400px;
position: absolute;
left: 46px;
top: 38px;
font-size: 14px;
font-weight: 500;
justify-content: space-between;
}
.img {
width: 30px;
height: 34px;
background-image: url(../../assets/images/basicinfo/exl.png);
}
.timebox {
margin-left: 15px;
margin-top: -5px;
.timetop {
display: flex;
width: 262px;
justify-content: space-between;
// margin-bottom: 8px;
.tit {
font-size: 14px;
font-weight: 400;
color: #333333;
}
.stateloading {
font-size: 14px;
font-weight: 400;
color: #4ea6ff;
}
.statedefeat {
font-size: 14px;
font-weight: 400;
color: #ff7474;
}
.statesucce {
font-size: 14px;
font-weight: 400;
color: #35ae69;
}
}
.prog {
width: 262px;
height: 5px;
background: #eaf1fe;
border-radius: 4px;
.inprogloading {
width: 55%;
height: 5px;
border-radius: 4px;
background: #4ea6ff;
}
//下载失败条
.inprogdefeat {
width: 55%;
height: 5px;
border-radius: 4px;
background: #ff7474;
}
//下载成功条
.inprogsucce {
width: 100%;
height: 5px;
border-radius: 4px;
background: #57c887;
}
}
}
.curloading {
margin-left: 15px;
margin-top: 15px;
display: flex;
.cur {
font-size: 14px;
font-weight: 400;
color: #333333;
}
.cancel {
font-size: 14px;
font-weight: 400;
color: #387df7;
}
}
}
.downloadErr {
width: 120px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 2px;
border: 1px solid #387df7;
font-size: 14px;
font-weight: 400;
color: #387df7;
line-height: 20px;
cursor: pointer;
margin-left: 66px;
margin-top: 16px;
position: absolute;
bottom: 28;
}
}
}
}
}
}
.btnn {
height: 72px;
width: 100%;
position: absolute;
background-color: #fff;
bottom: 0;
left: 0;
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>

View File

@@ -0,0 +1,432 @@
<template>
<a-drawer :visible="visible" placement="right" :closable="false" :maskClosable="false"
width="80%" :title="false">
<!-- 外部讲师查看详情 -->
<div class="LookExternalLecturer">
<div class="header" style="margin-top: -24px;">
<div class="headerTitle">查看讲师</div>
<!-- <router-link :to="{ path: '/lecturerList', query: { activeKey: '2', } }"> -->
<img
@click="handleBack"
style="width: 29px; height: 29px; cursor: pointer"
src="../../assets/images/basicinfo/close.png"
/>
<!-- </router-link> -->
</div>
<!-- <a-divider style="height: 1px; background-color: #b7b8b7 ;margin: 0;" /> -->
<a-layout-content>
<!-- 讲师信息 -->
<div style="width: 100%;margin-top: 10px;">
<span class="line" style="margin-left:12px;"></span>
<a-descriptions title="讲师信息" style="padding:0 20px;" bordered :column="3" :contentStyle="rowCenters" :labelStyle="rowCenter">
<!-- 一层 -->
<a-descriptions-item label="讲师头像"> <a-image
:width="55" style="border-radius: 50%;"
:src=formParam.photo
/></a-descriptions-item>
<a-descriptions-item label="讲师姓名">{{formParam.name||'-'}}</a-descriptions-item>
<a-descriptions-item label="手机号码">{{formParam.mobile||'-'}}</a-descriptions-item>
<a-descriptions-item label="供应商">{{formParam.supplier||'-'}}</a-descriptions-item>
<a-descriptions-item label="讲师邮箱">{{formParam.email||'-'}}</a-descriptions-item>
<a-descriptions-item label="授课时长" :span="2">{{formParam.teaching||'-'}}
<span style="margin-left: 5px;" v-if="formParam.teaching != null">分钟</span>
<span style="margin-left: 10px ; padding: 2px;" v-if="formParam.teaching != null">({{
(formParam.teaching / 60).toFixed(2) }}小时)</span>
</a-descriptions-item>
</a-descriptions>
<div style="margin-top: 18px;"></div>
<span class="line" style="margin-left:12px;"></span>
<!-- 记录 -->
<a-descriptions title="其他信息" bordered :column="4" style="padding:0 20px;" :contentStyle="rowCenters" :labelStyle="rowCenter" >
<!-- 一层 -->
<a-descriptions-item label="讲师介绍" :span="4" >
<div v-if="formParam.description !=null&&resp(formParam.description)" style="min-width: 500px;" v-html="formParam.description" ></div>
<div v-else>-</div>
</a-descriptions-item>
<a-descriptions-item label="备注" :span="4">
<div v-if="formParam.remark ==null || formParam.remark =='<p><br></p>'" style="min-width: 500px;">- </div>
<div style="min-width: 500px;">{{formParam.remark ===null?'':formParam.remark}}</div>
<!-- </a-tag>
</div> -->
</a-descriptions-item>
</a-descriptions>
<!-- -->
<div style="margin-top: 9px;"></div>
<a-tabs v-model:activeKey="activeKey" style="padding:0 20px;">
<a-tab-pane key="1" tab="授课记录">
<a-table :header-cell-style="{ 'text-align': 'center' }" :columns="teacherrecordsColumns"
:sroll="{ x: '1000' }"
:data-source="teacherrecordstableData" :loading="teacherrecordsLoading" @expand="expandTable" :pagination="pagination">
</a-table>
</a-tab-pane>
</a-tabs>
</div>
</a-layout-content>
</div>
</a-drawer>
</template>
<script lang ="jsx">
import { useRouter,useRoute } from "vue-router";
import { reactive, toRefs, ref,computed,watch } from "vue"
import {getTeacherById} from "../../api/Lecturer";
import { getNewInTeacherCourseList } from "../../api/Teaching";
// import boe from '@/assets/boe.jpg'
import avatar from '@/assets/avatar.png'
import dayjs from "dayjs";
export default{
name :"LookExternalLecturer",
components:{
},
props:{
visible:{
type: Boolean,
default: false,
},
id:{
type: String,
default: ""
}
},
setup(props,emits){
const router=useRouter();
const state = reactive({
activeKey:'1',
formParam: {
},
promotionrecordsLoading: false, //晋级记录遮罩层
teacherrecordsLoading: false,// 授课记录遮罩层
teacherrecordstableDataTotal: 0,//授课记录列表总条数
teacherrepromotableDataTotal: -1,//晋级记录总条数
teacherrecords: {
recordType:2,
pageNo: "1",
pageSize: "10",
id: null
},
teacherrepromo:{
id:null,
pageNo: "1",
pageSize: "10",
}
});
watch(()=>props.visible,(val)=>{
if(val){
TeacherSystem(props.id)
}else{
state.formParam = {}
teacherrecordstableData.value = []
}
})
//外部讲师详情
const TeacherSystem = (id) => {
getTeacherById({id,}).then((res) => {
console.log("内部讲师详情", res.data);
state.formParam = res.data.data
state.formParam.photo = state.formParam.photo ===null ?avatar : state.formParam.photo
state.formParam.teachingDate = res.data.data.teachingDate ? dayjs(res.data.data.teachingDate, 'YYYY-MM-DD HH:mm'):'',
getteacherrecordstableData()
})
.catch((err) => {
console.log("内部讲师详情", err);
});
}
//返回上一层
const handleBack=()=>{
emits.emit('update:visible',false)
};
//授课记录列表
const teacherrecordsColumns = ref([
// {
// title: '课程编号',
// dataIndex: 'offcourseId',
// key: 'offcourseId',
// ellipsis: true, align: "center",
// width: 120,
// },
{
title: '课程编号',
dataIndex: 'courseId',
key: 'courseId',
align: "center",
customRender: ({text, index})=>{
return index+1;
},
width: 120,
},
{
title: '课程名称',
dataIndex: 'courseName',
key: 'courseName',
ellipsis: true, align: "center",
width: 120,
},
{
title: '课程日期',
dataIndex: 'teachingDate',
key: 'teachingDate',
ellipsis: true, align: "center",
width: 200,
customRender: (value) => {
return (
<div>
{dayjs(value.record?.teachingDate).format("YYYY-MM-DD HH:mm")}
</div>
);
},
},
{
title: '内容分类',
dataIndex: 'courseTypeName',
key: 'courseTypeName',
ellipsis: true, align: "center",
width: 120,
},
{
title: '课程类型',
dataIndex: 'type',
key: 'type',
ellipsis: true, align: "center",
width: 120,
customRender: (value) => {
return (
<div>
{String(value.record.type)
? {
"0": "在线课",
"1": "面授课",
"2": "课程开发",
"3": "作业员入模培训",
"4": "其他",
}[value.record.type + ""]
: "-"}
</div>
)
}
},
{
title: '学习总人数',
dataIndex: 'studys',
key: 'studys',
ellipsis: true, align: "center",
width: 120,
},
{
title: '授课时长(分钟)',
dataIndex: 'teaching',
key: 'teaching',
ellipsis: true, align: "center",
width: 120,
},
{
title: '评分',
dataIndex: 'score',
key: 'score',
ellipsis: true, align: "center",
width: 120,
},
{
title: '开课状态',
dataIndex: 'courseStatus',
key: 'courseStatus',
ellipsis: true, align: "center",
width: 120,
customRender: (value) => {
return (
<div>
{value.record.courseStatus == 0 || value.record.courseStatus == 1
? {
"0": "未开课",
"1": "开课",
}[value.record.courseStatus + ""] || ""
: "-"}
</div>
)
}
},
{
title: '备注',
dataIndex: 'remark',
key: 'remark',
ellipsis: true, align: "center",
width: 120,
customRender: (text)=>{
return (
<div>
{text.record.remark || "-"}
</div>
)
},
},
])
//授课记录列表数据
const teacherrecordstableData = ref([
])
const getteacherrecordstableData = () => {
state.teacherrecordsLoading = true
state.teacherrecords.name = state.formParam.name
let obj = { ...state.teacherrecords }
// api接口
getNewInTeacherCourseList(obj).then((res) => {
teacherrecordstableData.value = res.data.data.records
state.teacherrecordstableDataTotal = Number(res.data.data.total);
state.teacherrecordsLoading = false
})
};
const pagination = computed(() => ({
total: state.teacherrecordstableDataTotal,
showSizeChanger: true,
showQuickJumper:true,
current: state.teacherrecords.pageNo,
pageSize: state.teacherrecords.pageSize,
onChange: paginationChange,
}));
const paginationChange = (e,pageSize) => {
state.teacherrecords.pageNo = e;
state.teacherrecords.pageSize = pageSize
getteacherrecordstableData();
};
//授课翻页
const teacherchangePagination = (page) => {
state.searchParam.pageNo = page;
// state.pageNo = page;
state.searchParam.pageSize = pageSize;
getTableDate();
state.teacherrecords.pageNo = page;
getteacherrecordstableData();
};
const resp = (val) => {
if(val){
const reg = /<[^<>]+>/g;
const value = val.replace(reg, "");
return value
}
}
return{
...toRefs(state),
router,
resp,
TeacherSystem,
handleBack,
rowCenter:{"text-align":"center",'width':'120px'},
rowCenters:{'min-width':'176px'},
teacherrecordstableData,
teacherrecordsColumns,
getteacherrecordstableData,
teacherchangePagination,
pagination,
paginationChange,
}
}
}
</script>
<style lang="scss" scoped>
.LookExternalLecturer {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.header {
padding: 0px 32px;
height: 73px;
border-bottom: 1px solid #e8e8e8;
display: flex;
justify-content: space-between;
align-items: center;
// background-color: red;
// margin-bottom: 20px;
flex-shrink: 0;
.headerTitle {
font-size: 18px;
font-weight: 600;
color: #333333;
line-height: 25px;
// margin-left: 24px;
}
}
.filter {
margin-left: 38px;
margin-right: 38px;
margin-top: 30px;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.select {
margin-right: 20px;
margin-bottom: 20px;
}
}
}
.backbtn{
float: right;
margin-right: 20px;
border: none;
color: #4ea6ff;
width: 80px;
height:64px
}
//小竖线
.line{
float:left; width: 3px; height: 25px; background: #4ea6ff;border-radius: 30%; margin-right: 5px;
}
::v-deep .ant-descriptions-header{
margin-bottom: 18px ;
margin-top: 18px;
}
.goback {
float: right;
padding-right: 70px;
//padding-top: 37px;
position: relative;
.return {
display: inline-block;
width: 42px;
height: 42px;
margin-top: 12px;
margin-right: 10px;
background-image: url("../../assets/images/projectadd/return.png");
}
.returntext {
display: inline-block;
position: absolute;
top: 12px;
color: #4ea6ff;
font-size: 14px;
}
}
::v-deep .ant-select-selection-overflow-item {
margin-top: -2px;
}
::v-deep .ant-select-multiple .ant-select-selection-item {
height: 34px
}
.tableBox {
padding-bottom: 20px;
margin: 20px 38px 30px;
::v-deep .ant-select-dropdown{
display: inline-block;
}
::v-deep .ant-select-selection-item{
margin-left: 3px;
}
::v-deep .ant-pagination-options-size-changer.ant-select{
width: 84px;
}
.pa {
width: 100%;
display: flex;
justify-content: right;
}
}
</style>

View File

@@ -0,0 +1,569 @@
<template>
<a-drawer :visible="visible" placement="right" :closable="false" :maskClosable="false"
width="80%" :title="false">
<!-- 内部讲师查看详情 -->
<div class="LookInsideLecturer">
<div class="header" style="margin-top: -24px;">
<div class="headerTitle">查看讲师</div>
<!-- <router-link :to="{ path: '/lecturerList', query: { activeKey: '1', } }"> -->
<img
@click="handleClose"
style="width: 29px; height: 29px; cursor: pointer"
src="../../assets/images/basicinfo/close.png"
/>
<!-- </router-link> -->
</div>
<a-layout-content>
<!-- 讲师信息 -->
<div style="width: 100%;margin-top: 10px;padding: 20px;">
<span class="line"></span>
<a-descriptions title="讲师信息" bordered :column="4" :contentStyle="rowCenter" :labelStyle="rowCenters">
<!-- 一层 -->
<a-descriptions-item label="讲师头像">
<a-image :width="55" style="border-radius: 50%;" :src=formParam.photo />
</a-descriptions-item>
<a-descriptions-item label="讲师姓名">{{formParam.name}}/{{formParam.userNo}}</a-descriptions-item>
<a-descriptions-item label="讲师体系">{{formParam.tsystemName || '-'}}</a-descriptions-item>
<!-- 二层 -->
<a-descriptions-item label="讲师级别">{{formParam.tlevelName||'-'}}</a-descriptions-item>
<a-descriptions-item label="认证状态">{{ formParam.certStatus==0?'未认证' :formParam.certStatus==1 ?'已认证':'-'}}
<span>
<a-button type="text" class="moreidbtn" v-if="formParam.certStatus == 1"
@click="handleup">查看认证资料</a-button>
</span>
</a-descriptions-item>
<a-descriptions-item label="授课时长"> <span v-if="formParam.teaching!= null" >{{ (Number(formParam.teaching) /60 ).toFixed(2)}}小时</span><span v-else>-</span>
</a-descriptions-item>
<a-descriptions-item label="发薪地 ">{{formParam.salaryName||'-'}}</a-descriptions-item>
<!-- 三层 -->
<a-descriptions-item label="在职状态">{{formParam.waitStatus=='0'?'在职' :formParam.waitStatus=='1' ?'离职':'-'}}</a-descriptions-item>
<a-descriptions-item label="账号状态">{{formParam.status=='0'?'临时' :formParam.status=='1' ?'启用':formParam.status==2 ?'停用':'-'}}</a-descriptions-item>
<a-descriptions-item label="创建方式">{{formParam.createFrom=='0'?'自动录入' :formParam.createFrom=='1'?'手动录入':'-'}}</a-descriptions-item>
<a-descriptions-item style="max-width: 400px;" label="所属组织" >
<span :title="formParam.orgName">{{ formParam.orgNames || '-' }}</span>
</a-descriptions-item>
</a-descriptions>
<div style="margin-top: 10px;"></div>
<span class="line" ></span>
<a-descriptions title="其他信息" bordered :column="4" :contentStyle="rowCenter" :labelStyle="rowCenters" >
<!-- 一层 -->
<a-descriptions-item style="max-width: 200px;" label="讲师介绍" :span="4" >
<div v-if="formParam.description !=null&&resp(formParam.description)" style="min-width: 500px;" v-html="formParam.description" ></div>
<div v-else>-</div>
</a-descriptions-item>
<a-descriptions-item label="工作职责" :span="4">
<div v-if="formParam.workExperience !=null&&resp(formParam.workExperience)" style="min-width: 500px;" v-html="formParam.workExperience" ></div>
<div v-else>-</div>
</a-descriptions-item>
<a-descriptions-item label="擅长课程" :span="4">
<div v-if="formParam.courses !=null&&resp(formParam.courses)" style="min-width: 500px;" v-html="formParam.courses" ></div>
<div v-else>-</div>
</a-descriptions-item>
<a-descriptions-item label="教师专长" :span="4">
<a-tag v-if="formParam.expertiseNames" color="blue" style="margin-right:10px" v-for="item in formParam.expertiseNames?.split(',')">{{item}}</a-tag>
<span v-else>-</span>
</a-descriptions-item>
</a-descriptions>
<!-- 记录 -->
<div style="margin-top: 8px;"></div>
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="授课记录">
<a-table :header-cell-style="{ 'text-align': 'center' }" :columns="teacherrecordsColumns"
:data-source="teacherrecordstableData" :loading="teacherrecordsLoading" :scroll="{ x: '1000' }" :pagination="pagination">
</a-table>
<div style="float: right;">
<a-pagination
v-if="tableDataTotal > 10"
:showSizeChanger="true"
:showQuickJumper="false"
:hideOnSinglePage="false"
:pageSize="searchParam.pageSize"
:current="searchParam.pageNo"
:total="tableDataTotal"
class="pagination"
@change="changePagination"
/>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="晋级记录">
<a-table :header-cell-style="{ 'text-align': 'center' }" style="border: 1px solid #f2f6fe" :columns="promotionrecordsColumns"
:data-source="promotionrecordstableData" :loading="promotionrecordsLoading" @expand="expandTable" :pagination="false">
<template #bodyCell="{ record, column,index }" >
</template>
</a-table>
<div style="float: right;">
<a-pagination
v-if="teacherrepromotableDataTotal > 10"
:showSizeChanger="true"
:showQuickJumper="false"
:hideOnSinglePage="true"
:pageSize="teacherrepromo.pageSize"
:current="teacherrepromo.pageNo"
:total="teacherrepromotableDataTotal"
class="pagination"
@change="teacherrepromoPagination"
/>
</div>
</a-tab-pane>
</a-tabs>
</div>
</a-layout-content>
</div>
</a-drawer>
</template>
<script lang ="jsx">
import { useRouter,useRoute } from "vue-router";
import { reactive, toRefs, ref, computed,watch } from "vue"
import { useStore } from "vuex";
import {getTeacherById ,getTeacherLogList} from "../../api/Lecturer";
import { getNewInTeacherCourseList } from "../../api/Teaching";
import avatar from '@/assets/avatar.png'
import Avatarman from '@/assets/Avatarman.png'
import Avatarwoman from '@/assets/Avatarwoman.png'
import dayjs from "dayjs";
export default{
name :"LookInsideLecturer",
components:{
},
props:{
visible:{
type: Boolean,
default: false,
},
id:{
type: String,
default: ""
}
},
setup(props,emits){
const router=useRouter();
const state = reactive({
activeKey:'1',
formParam: {
},
promotionrecordsLoading: false, //晋级记录遮罩层
teacherrecordsLoading: false,// 授课记录遮罩层
teacherrecordstableDataTotal: 0,//授课记录列表总条数
teacherrepromotableDataTotal: -1,//晋级记录总条数
teacherrecords: {
recordType:1,
pageNo: "1",
pageSize: "10",
id: null
},
teacherrepromo:{
userId:null,
pageNo: "1",
pageSize: "10",
}
});
watch(() => props.visible, (val) => {
console.log(val,'xixixiixix')
if(val){
const id = props.id
TeacherSystem(id)
}
})
const isOrgNames = (val) => {
if(val){
const parts = val.split('/');
const reversedParts = parts.reverse();
state.formParam.orgNames = reversedParts.join('/');
}
}
const handleClose = () => {
emits.emit('update:visible',false)
}
//内部讲师详情
const TeacherSystem = (id) => {
getTeacherById({id,}).then((res) => {
let objA= res.data.data
console.log(objA ,'11111111111')
state.formParam = objA
state.formParam.photo = state.formParam.photo ==null ?
state.formParam.gender == 1 ? Avatarman :
state.formParam.gender ==2 ? Avatarwoman : avatar : state.formParam.photo
state.teacherrepromo.userId=res.data.data.id
console.log(state.teacherrepromo.id);
isOrgNames(state.formParam.orgName)
getteacherrecordstableData()
promotionrecordstableData.value = res.data.data.levelLogList
})
.catch((err) => {
console.log("内部讲师详情", err);
});
}
const resp = (val) => {
if(val){
const reg = /<[^<>]+>/g;
const value = val.replace(reg, "");
return value
}
}
//授课记录列表
const teacherrecordsColumns = ref([
{
title: '课程编号',
dataIndex: 'courseId',
key: 'courseId',
align: "center",
customRender: ({text, index})=>{
return index+1;
},
width: 120,
},
{
title: '课程名称',
dataIndex: 'courseName',
key: 'courseName',
ellipsis: true,
align: "center",
width: 120,
},
{
title: '课程日期',
dataIndex: 'teachingDate',
key: 'teachingDate',
ellipsis: true, align: "center",
width: 200,
customRender: (value) => {
return (
<div>
{dayjs(value.record?.teachingDate).format("YYYY-MM-DD HH:mm")}
</div>
);
},
},
{
title: "内容分类",
width: 100,
dataIndex: "courseTypeName",
ellipsis: true,
key: "keywords",
align: "center",
},
{
title: '课程类型',
dataIndex: 'type',
key: 'type',
ellipsis: true, align: "center",
width: 120,
customRender: (value) => {
return (
<div>
{String(value.record.type)
? {
"0": "在线课",
"1": "面授课",
"2": "课程开发",
"3": "作业员入模培训",
"4": "其他",
}[value.record.type + ""]
: "-"}
</div>
)
}
},
{
title: '学习总人数',
dataIndex: 'studys',
key: 'studys',
ellipsis: true, align: "center",
width: 120,
},
{
title: '授课时长(分钟)',
dataIndex: 'teaching',
key: 'teaching',
ellipsis: true, align: "center",
width: 120,
},
{
title: '评分',
dataIndex: 'score',
key: 'score',
ellipsis: true, align: "center",
width: 120,
},
{
title: '开课状态',
dataIndex: 'courseStatus ',
key: 'courseStatus ',
ellipsis: true, align: "center",
width: 120,
customRender: (value) => {
return (
<div>
{value.record.courseStatus == 0 || value.record.courseStatus == 1
? {
"0": "未开课",
"1": "开课",
}[value.record.courseStatus + ""] || ""
: "-"}
</div>
)
}
},
{
title: '备注',
dataIndex: 'remark ',
key: 'remark ',
ellipsis: true,
align: "center",
width: 120,
customRender: (value) => {
return (
<div>{value.record.remark || '-'}</div>
)
}
},
])
//授课记录列表数据
const teacherrecordstableData = ref([
])
const getteacherrecordstableData = () => {
state.teacherrecordsLoading = true
state.teacherrecords.name = state.formParam.name
let obj = { ...state.teacherrecords }
// api接口
getNewInTeacherCourseList(obj).then((res) => {
console.log(res);
teacherrecordstableData.value = res.data.data.records
state.teacherrecordstableDataTotal = Number(res.data.data.total);
state.teacherrecordsLoading = false
})
};
const pagination = computed(() => ({
total: state.teacherrecordstableDataTotal,
showSizeChanger: true,
showQuickJumper:true,
current: state.teacherrecords.pageNo,
pageSize: state.teacherrecords.pageSize,
onChange: paginationChange,
}));
const paginationChange = (e,pageSize) => {
state.teacherrecords.pageNo = e;
state.teacherrecords.pageSize = pageSize
getteacherrecordstableData();
};
//授课翻页
const teacherchangePagination = (page) => {
state.teacherrecords.pageNo = page;
// state.pageNo = page;
state.teacherrecords.pageSize = pageSize;
getteacherrecordstableData();
};
//晋级记录翻页
const teacherrepromoPagination = (page) => {
state.teacherrepromo.pageNo = page;
// getpromotionrecordstableData();
};
//晋级记录列表
const promotionrecordsColumns = ref([
{
title: '变更时间',
dataIndex: 'createTime',
key: 'createTime',
ellipsis: true, align: "center",
width: 120,
},
{
title: '变更方式',
dataIndex: 'type',
key: 'type',
ellipsis: true, align: "center",
width: 120,
customRender: (value) => {
return (
<div>
{value.record.type == 1 ? "自动" : value.record.type == 0 ? '手动':"-"}
</div>
)
}
},
{
title: '变更结果',
dataIndex: 'result',
key: 'result',
ellipsis: true, align: "center",
width: 120,
},
{
title: '操作人',
dataIndex: 'updateName',
key: 'updateName',
ellipsis: true, align: "center",
width: 120,
},
])
//晋级记录列表数据
const promotionrecordstableData = ref([
])
// const getpromotionrecordstableData = () => {
// state.promotionrecordsLoading = true
// // state.teacherrepromo.kid = state.kid
// getTeacherLogList(state.teacherrepromo).then((res) => {
// promotionrecordstableData.value = res.data.data.records
// state.teacherrepromotableDataTotal = Number(res.data.data.total);
// })
// state.promotionrecordsLoading = false
// };
const goback = ()=>{
router.back()
}
const store = useStore();
// // 内容分类
// const sysTypeOptions = computed(() => {return store.state.content_type;});
// //内容分类
// const getSysTypeMap = (code) => {
// if(code=="")return
// // console.log(store.state.sysTypeMap,'map集合');
// return store.state.sysTypeMap.get(code)
// }
// getSysTypeMap()
const handleup = ()=>{
window.open (
`${process.env.VUE_APP_BOE_API_URL}/upload${state.formParam.certification}`
);
}
return{
...toRefs(state),
router,
goback,
handleup,
// getSysTypeMap,
// sysTypeOptions,
TeacherSystem,
handleClose,
isOrgNames,
rowCenter:{"text-align":"left",'min-width':'176px'},
rowCenters:{"text-align":"center",'width':'160px','min-width':'110px'},
teacherrecordstableData,
teacherrecordsColumns,
resp,
getteacherrecordstableData,
teacherchangePagination,
pagination,
paginationChange,
teacherrepromoPagination,
promotionrecordsColumns,
promotionrecordstableData,
// getpromotionrecordstableData,
}
}
}
</script>
<style lang="scss" scoped>
.LookInsideLecturer {
.header {
padding: 0px 32px;
height: 73px;
border-bottom: 1px solid #e8e8e8;
display: flex;
justify-content: space-between;
align-items: center;
// background-color: red;
// margin-bottom: 20px;
flex-shrink: 0;
.headerTitle {
font-size: 18px;
font-weight: 600;
color: #333333;
line-height: 25px;
// margin-left: 24px;
}
}
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.filter {
margin-left: 38px;
margin-right: 38px;
margin-top: 30px;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.select {
margin-right: 20px;
margin-bottom: 20px;
}
}
}
.backbtn{
float: right;
margin-right: 20px;
border: none;
color: #4ea6ff;
width: 80px;
height:64px
}
//小竖线
.line{
float:left; width: 3px; height: 25px; background: #4ea6ff;border-radius: 30%; margin-right: 5px;
}
::v-deep .ant-descriptions-header{
margin-bottom: 18px ;
}
.goback {
float: right;
padding-right: 70px;
//padding-top: 37px;
position: relative;
.return {
display: inline-block;
width: 42px;
height: 42px;
margin-top: 12px;
margin-right: 10px;
background-image: url("../../assets/images/projectadd/return.png");
}
.returntext {
display: inline-block;
position: absolute;
top: 12px;
color: #4ea6ff;
font-size: 14px;
}
}
.moreidbtn {
border: none;
color: #4ea6ff;
width: 80px
}
::v-deep .ant-select-selection-overflow-item {
margin-top: -2px;
}
::v-deep .ant-select-multiple .ant-select-selection-item {
height: 34px
}
.tableBox {
padding-bottom: 20px;
margin: 20px 38px 30px;
::v-deep .ant-select-dropdown{
display: inline-block;
}
::v-deep .ant-select-selection-item{
margin-left: 3px;
}
::v-deep .ant-pagination-options-size-changer.ant-select{
width: 84px;
}
.pa {
width: 100%;
display: flex;
justify-content: right;
}
}
</style>

View File

@@ -16,9 +16,8 @@
v-model:value="labelValue"
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择归属组织"
:placeholder="placeholder"
:labelInValue="true"
allow-clear
v-model:treeExpandedKeys="stuTreeExpandedKeys"
:loading="orgLoading"
:load-data="onLoadData"
@@ -46,6 +45,10 @@ const props = defineProps({
type: Boolean,
default: false,
},
placeholder: {
type: String,
default: "请选择归属组织"
}
});
const emit = defineEmits({});
const stuTreeExpandedKeys = ref([]);

View File

@@ -0,0 +1,108 @@
<template>
<a-select
:getPopupContainer="
(triggerNode) => {
return triggerNode.parentNode || document.body;
}
"
v-model:value="managerArray"
:placeholder="placeholder"
:options="options"
allowClear
showSearch
mode="multiple"
:disabled="disabled"
@search="searchMember"
@change="change"
@blur="blur"
@focus="focus"
:show-arrow="true"
style="width: 100%"
:maxTagTextLength="3"
:maxTagCount="4"
>
<template v-if="loading" #notFoundContent>
<a-spin size="small"/>
</template>
</a-select>
</template>
<script setup>
import {computed, defineEmits, defineProps, onMounted, ref, watch} from "vue";
import {throttle} from "@/api/method";
import {getUserList} from "@/api/Lecturer.js";
const props = defineProps({
disabled: Boolean,
placeholder: {
type: String,
default: "请输入搜索关键字",
},
arrayList:{
type: Array,
default: ()=>[]
},
type: {
type: Number,
default: 0
},
value: {
type: String,
default: ''
}
})
const emit = defineEmits({})
const managerArray = ref([])
const loading = ref(false)
onMounted(()=>{
managerArray.value = props.arrayList
options.value = props.arrayList
})
watch(()=>props.arrayList, ()=>{
managerArray.value = props.arrayList
})
const options = ref([])
const keyword = ref('')
const getList = () => {
loading.value = true
getUserList(keyword.value).then(res=>{
loading.value = false
if(res.data.code == 200){
options.value = res.data.data.list.map(e => ({
label: e.realName + e.userNo,
value: e.id,
userId: e.id,
userNo: e.userNo,
userName: e.realName,
}))
// console.log(options.value,'xixixixi')
}
}).catch(()=>{
loading.value = false
options.value =[]
})
}
const throttList = throttle(getList, 600);
//搜索学员
const searchMember = (val) => {
keyword.value = val
throttList()
};
const focus = () => {
keyword.value = ''
getList()
}
function blur() {
keyword.value = ''
}
function change(e, l) {
console.log(e, l,'xixixixiixix')
keyword.value = ''
l?.map(item => item.type = props.type)
if (Array.isArray(l)) {
emit('update:arrayList',l)
emit('update:value', l.map(t => t.value).join(','))
}
}
</script>

View File

@@ -29,7 +29,7 @@
<script setup>
import {computed, defineEmits, defineProps, onMounted, ref, watch} from "vue";
import {useThrottlePage} from "@/api/request";
import {USER_LIST} from "@/api/apis";
import {USER_LIST_NEW} from "@/api/apis";
const props = defineProps({
value: {
@@ -56,12 +56,12 @@ const emit = defineEmits({})
const isOpen = ref(false)
const memberParam = ref({keyword: '', pageNo:1, pageSize: 20})
const memberParam = ref({name: '', pageNo:1, pageSize: 20})
const {data: userList, loading} = useThrottlePage(USER_LIST, memberParam.value, false)
const {data: userList, loading} = useThrottlePage(USER_LIST_NEW, memberParam.value, false)
const options = computed(() => userList.value.filter(e => !(props.value + '').includes(e.id)).map(e => ({
label: e.realName + e.userNo,
label: e.name + '(' + e.mobile + ')',
value: e.id //,
// ...e,
// audienceList: null
@@ -93,18 +93,18 @@ const searchMember = (keyword) => {
isOpen.value = true
userList.value = []
memberParam.value.pageNo = 1
memberParam.value.keyword = keyword
memberParam.value.name = keyword
console.log('searchMember', memberParam.value)
};
function blur() {
isOpen.value = false
memberParam.value.keyword = ''
memberParam.value.name = ''
memberParam.value.pageNo = 1
}
function change(e, l) {
memberParam.value.keyword = ''
memberParam.value.name = ''
memberParam.value.pageNo = 1
isOpen.value = false
Array.isArray(l) && (selectOptions.value = l)

View File

@@ -29,7 +29,7 @@
<script setup>
import {computed, defineEmits, defineProps, onMounted, ref, watch} from "vue";
import {useThrottlePage} from "@/api/request";
import {USER_LIST} from "@/api/apis";
import {USER_LIST_NEW} from "@/api/apis";
const props = defineProps({
value: {
@@ -56,12 +56,12 @@ const emit = defineEmits({})
const isOpen = ref(false)
const memberParam = ref({keyword: '', pageNo:1, pageSize: 20})
const memberParam = ref({name: '', pageNo:1, pageSize: 20})
const {data: userList, loading} = useThrottlePage(USER_LIST, memberParam.value, false)
const {data: userList, loading} = useThrottlePage(USER_LIST_NEW, memberParam.value, false)
const options = computed(() => userList.value.filter(e => !(props.value + '').includes(e.id)).map(e => ({
label: e.realName + e.userNo,
label: e.name + '(' + e.mobile + ')' ,
value: e.id // ,
// ...e,
// audienceList: null
@@ -93,18 +93,18 @@ const searchMember = (keyword) => {
isOpen.value = true
userList.value = []
memberParam.value.pageNo = 1
memberParam.value.keyword = keyword
memberParam.value.name = keyword
console.log('searchMember', memberParam.value)
};
function blur() {
isOpen.value = false
memberParam.value.keyword = ''
memberParam.value.name = ''
memberParam.value.pageNo = 1
}
function change(e, l) {
memberParam.value.keyword = ''
memberParam.value.name = ''
memberParam.value.pageNo = 1
isOpen.value = false
Array.isArray(l) && (selectOptions.value = l)

View File

@@ -0,0 +1,140 @@
<template>
<a-select
:getPopupContainer="
(triggerNode) => {
return triggerNode.parentNode || document.body;
}
"
v-model:value="managerArray"
:placeholder="placeholder"
:filterOption="false"
:options="isOpen?options:selectOptions"
allowClear
showSearch
:disabled="disabled"
@popupScroll="memberScroll"
@search="searchMember"
:open="isOpen"
@change="change"
@blur="blur"
:show-arrow="false"
style="width: 100%"
>
<template v-if="loading" #notFoundContent>
<a-spin size="small"/>
</template>
</a-select>
</template>
<script setup>
import {computed, defineEmits, defineProps, onMounted, ref, watch} from "vue";
import {useThrottlePage} from "@/api/request";
import { getTeacherList } from "@/api/Lecturer";
const props = defineProps({
value: {
type: String,
default: ''
},
name: {
type: String,
default: ''
},
mobile: {
type: String,
default: ''
},
disabled: Boolean,
placeholder: {
type: String,
default: "请输入搜索关键字",
},
mode: String
})
const selectOptions = ref([])
const managerArray = computed(() => props.value === '' ? null : props.value)
const emit = defineEmits({})
const isOpen = ref(false)
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
const memberParam = ref({name: '', pageNo:1, pageSize: 999,teacherType:2 ,status:1})
const userList = ref([])
const loading = ref(false)
const getOutTeacher = () => {
getTeacherList(memberParam.value).then(res=>{
if(res.data.code == 200){
userList.value = res.data.data.records
loading.value = false
}
})
}
const options = computed(() => userList.value.map(e => ({
// label: e.name + '(' + e.userNo + ')' + e.organizationName,
label:e.name,
value: e.name,
...e,
audienceList: null
})))
watch(props, init)
function init() {
//第一次进来 编辑赋值
// if (props.value && (props.value + '') !== selectOptions.value.map(e => e.value).join(',')) {
// selectOptions.value = (props.value + '').split(',').map((e, i) => ({label: props.name.split(',')[i], value: e}))
// }
}
onMounted(() => {
console.log('onMounted')
init()
getOutTeacher()
})
const memberScroll = ({target: {scrollHeight, scrollTop, clientHeight}}) => {
scrollHeight === (clientHeight + scrollTop) && memberParam.value.pageNo++
};
const debounceObject = debounce(getOutTeacher,1000)
//搜索学员
const searchMember = (keyword) => {
console.log('searchMember', keyword)
loading.value = true
isOpen.value = true
userList.value = []
memberParam.value.pageNo = 1
memberParam.value.teacherType=2
memberParam.value.name = keyword
console.log('searchMember', memberParam.value)
debounceObject()
};
function blur() {
isOpen.value = false
memberParam.value.name = ''
memberParam.value.pageNo = 1
memberParam.value.teacherType=2
}
function change(e, l) {
memberParam.value.name = ''
memberParam.value.teacherType=2
memberParam.value.pageNo = 1
isOpen.value = false
Array.isArray(l) && (selectOptions.value = l)
Array.isArray(selectOptions.value) && emit('onChange', e, l)
emit('update:name', l?.label)
emit('update:value', l?.value)
emit('update:mobile', l?.label)
}
</script>

View File

@@ -0,0 +1,435 @@
<template>
<div>
<a-popover v-model:visible="visible" placement="bottom" trigger="click">
<template #content v-if="!disabled">
<div class="pover">
<div class="search">
<a-select
v-model:value="selectName"
style="width: 100%"
placeholder="请查询姓名或工号"
:options="isOpen?options:selectOptions"
:filter-option="false"
allowClear
showSearch
:open="isOpen"
:defaultOpen="false"
@search="searchMembers"
@change="handleChange"
@blur="blur"
>
<template #suffixIcon><ZoomInOutlined twoToneColor="#eb2f96" /></template>
<template v-if="loading" #notFoundContent>
<a-spin size="small"/>
</template>
</a-select>
</div>
<div class="tree">
<a-tree show-icon allow-clear tree-default-expand-all :tree-data="treeData" :loading="orgLoading"
:load-data="onLoadData" v-model:selectedKeys="stuTreeSelectKeys"
v-model:expandedKeys="stuTreeExpandedKeys" :fieldNames="{
children: 'treeChildList',
key: 'id',
title: 'name',
value: 'name',
}" @select="stuStuOrgSelect" style="max-height: 260px;overflow-y: auto;width: 250px;">
<template #smile>
<UserOutlined />
</template>
</a-tree>
</div>
<div class="btn">
<div class="btn0 btn1" @click="notChange">取消</div>
<!-- <div class="btn0 btn2" @click="changeOut">确定</div> -->
<a-button :loading="loadingChange" @click="changeOut" class="btn0 btn2" type="primary">确定</a-button>
</div>
</div>
</template>
<a-select
v-model:value="selectData"
style="width: 100%"
placeholder="请选择讲师"
:options="selectOptions"
:filter-option="false"
:open="false"
:defaultOpen="false"
:disabled="disabled"
>
<template v-if="loading" #notFoundContent>
<a-spin size="small"/>
</template>
</a-select>
</a-popover>
</div>
</template>
<script setup>
import { ref,defineProps,defineEmits,watch,onMounted } from 'vue';
import { request, useRequest} from "@/api/request";
import { message } from "ant-design-vue"
import { ORG_CHILD_LIST, ORG_LIST,USER_LIST_PAGE} from "@/api/apis";
import { getTeacherByDepartId,getTeacherByNameOrUserNo,getSystemInfoByUserId,getTeacherList } from "@/api/Lecturer";
import { ZoomInOutlined,UserOutlined } from '@ant-design/icons-vue';
const props = defineProps({
value:{
type: String,
default: '',
},
lable:{
type: String,
default: '',
},
newlable:{
type: String,
default: '',
},
system:{
type: Object,
default: ()=>{},
},
level:{
type: String,
default: '',
},
orgId:{
type: String,
default: '',
},
id:{
type: String,
default: '',
},
payrollPlaceCode: {
type: String,
default: '',
},
payrollPlaceName: {
type: String,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
lecturer: {
type: Boolean,
default: false,
}
})
const emit = defineEmits(['update:value','update:lable','update:system','update:level','update:newlable','update:orgId','update:id','update:payrollPlaceCode','update:payrollPlaceName'])
const visible = ref(false);
const selectData = ref(null);
watch(()=>props.value,(val)=>{
if(val){
selectData.value = val
}else{
selectData.value = null
}
},{immediate:true})
const teacherName = ref('')
const selectName = ref(null)
const options = ref([]);
const selectOptions = ref([])
const isOpen = ref(false);
const loading = ref(false);
function blur() {
isOpen.value = false
}
const orgName = ref('')
const orgId = ref('')
const payrollPlaceCode = ref('')
const payrollPlaceName = ref('')
const systemName = ref(null)
const levelName = ref('')
const neworgName = ref('')
const teacherId = ref('')
const handleChange = (e,l) => {
isOpen.value = false
// emit('update:value',e)
teacherName.value = e
orgName.value = l.orgName
// systemName.value = l.systemName
levelName.value = l.levelName
teacherId.value = l.key
orgId.value = l.orgId
payrollPlaceCode.value = l.payrollPlaceCode
payrollPlaceName.value = l.payrollPlaceName
if( l.orgName !==null ){
neworgName.value= orgName.value.split('/')
neworgName.value= neworgName.value[ neworgName.value.length-1]
}
emit('tlevel',l)
};
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
const searchMembers = (keyword) => {
isOpen.value = true
options.value = []
loading.value = true
if(keyword == '' || keyword == null){
isOpen.value = false
loading.value = false
options.value = []
}else{
debounceObject(keyword)
}
};
const searchMember = (keyword) => {
const obj = {
keyword,
pageNo:'1',
pageSize:'50',
departId:'',
}
props.lecturer && getTeacherByNameOrUserNo(obj).then((res) => {
if(res.data.status == 200){
loading.value = false
options.value = res.data.result.list.map((item) => {
return {
value: item.realName+'/'+item.userNo,
label: item.realName+'('+item.userNo+')'+item.orgName,
system: item.realName+'('+item.userNo+')'+item.tSystemName,
level: item.realName+'('+item.userNo+')'+item.sLevelName,
key: item.id,
orgName: item.orgName,
orgId: item.departId,
tSystemName:item.tSystemName,
sLevelName:item.sLevelName,
payrollPlaceCode: item.payrollPlaceCode,
payrollPlaceName:item.payrollPlaceName,
avatar: item.avatar,
gender: item.gender
}
})
}else{
loading.value = false
}
}).catch(()=>{
loading.value = false
})
const params = {
pageNo: 1,
pageSize: 50,
name: keyword,
status: 1,
}
!props.lecturer && getTeacherList(params).then(res=>{
console.log(res,'ressss')
if(res.data.code == 200){
loading.value = false
options.value = res.data.data.records.map((item) => {
return {
value: item.name+'/'+item.userNo,
label: item.name+'('+item.userNo+')'+item.tsystemName,
system: item.name+'('+item.userNo+')'+item.tsystemName,
level: item.name+'('+item.userNo+')'+item.sLevelName,
key: item.id,
orgName: item.orgName,
orgId: item.orgId,
tSystemName:item.tsystemName,
sLevelName:item.sLevelName,
payrollPlaceCode: item.salaryId,
payrollPlaceName:item.salaryName,
tlevelName: item.tlevelName, //讲师级别
tlevelId: item.tlevelId, //讲师级别id
// salaryName: item.salaryName, //发薪地
// salaryId: item.salaryId,
levelPay: item.levelPay
}
})
}
}).catch(()=>{
loading.value = false
})
};
const debounceObject = debounce(searchMember, 1500);
const filterOption = (input, option) => {
console.log(input,option,'xixixixi')
};
const stuTreeSelectKeys = ref([]);
const stuTreeExpandedKeys = ref([]);
const teacherById = ref(null)
const { data: treeData, loading: orgLoading } = useRequest(
ORG_LIST,
{ keyword: "" },
);
function onLoadData(treeNode) {
teacherById.value = treeNode.id
const promises = [];
const childDatas = []
promises.push(
getTeacherByDepartId({organizationId:teacherById.value}).then((res) => {
if(res.data.code == 200){
res.data.data.list.map(item=>{
return (
item.isLeaf = true,
item.name = item.realName + '/' + item.userNo,
item.slots = {icon:'smile'}
)
})
childDatas.unshift(...res.data.data.list)
}
}).catch((err)=>{
console.log(err,'err')
})
);
promises.push(
request(ORG_CHILD_LIST, { keyword: "", orgId: treeNode.id }).then((r) => {
// treeNode.dataRef.treeChildList = r.data;
childDatas.push(...r.data)
treeData.value = [...treeData.value];
})
);
return Promise.all(promises).then(() => {
treeNode.dataRef.treeChildList = childDatas;
treeData.value = [...treeData.value];
});
}
function stuStuOrgSelect(e, {selected: bool, selectedNodes, node, event}) {
console.log(selectedNodes)
teacherName.value = ''
orgName.value = ''
orgId.value = ''
systemName.value = null
levelName.value = ''
teacherId.value = ''
payrollPlaceCode.value = ''
payrollPlaceName.value = ''
if(selectedNodes[0].isLeaf){
teacherName.value = selectedNodes[0].name
orgName.value = selectedNodes[0].orgName
orgId.value = selectedNodes[0].departId
// systemName.value = selectedNodes[0].systemName
levelName.value = selectedNodes[0].levelName
teacherId.value = selectedNodes[0].id
// emit('update:value',selectedNodes[0].realName)
payrollPlaceCode.value = selectedNodes[0].payrollPlaceCode
payrollPlaceName.value = selectedNodes[0].payrollPlaceName
}
}
watch(()=>visible.value,(val)=>{
if(val){
stuTreeSelectKeys.value = []
stuTreeExpandedKeys.value = []
teacherName.value = ''
orgName.value = ''
orgId.value = ''
systemName.value = null
levelName.value = ''
neworgName.value=''
selectName.value = null
// selectData.value = ''
teacherId.value = ''
loadingChange.value = false
// teacherId.value = '1012311820121276416'
// changeneworg()
payrollPlaceCode.value = ''
payrollPlaceName.value = ''
}
})
const notChange = () => {
visible.value = false
teacherName.value = ''
orgName.value = ''
orgId.value = ''
systemName.value = null
levelName.value = ''
neworgName.value=''
teacherId.value = ''
loadingChange.value = false
payrollPlaceCode.value = ''
payrollPlaceName.value = ''
}
const loadingChange = ref(false)
watch(()=>teacherId.value,(val)=>{
if(val){
loadingChange.value = true
getSystemInfoByUserId(teacherId.value).then(res=>{
if(res.data.code == 200){
systemName.value = {
systemId:res.data.data?.id,
systemName:res.data.data?.systemName,
systemCode:res.data.data?.systemCode,
levelVoList: res.data.data?.levelVoList?.map(item=>{
return (
item.label = item.levelName,
item.value = item.id,
item
)
})
}
}
loadingChange.value = false
emit('update:system',systemName.value)
}).catch(err=>{
message.error('讲师体系获取失败,请重新选择')
loadingChange.value = false
})
}
})
const changeOut = () => {
if(!teacherName.value){
message.error('请选择讲师')
return
}
const neworgName = ref('')
// const changeneworg= ()=>{
// if( orgName !==null ){
// neworgName=orgName.split('/')
// neworgName= neworgName[neworgName.length-1]
// }
selectData.value = teacherName.value
emit('update:value',teacherName.value)
emit('update:lable',orgName.value)
emit('update:orgId',orgId.value)
emit('update:system',systemName.value)
emit('update:level',levelName.value)
emit('update:newlable',neworgName.value)
emit('update:id',teacherId.value)
emit('update:payrollPlaceCode',payrollPlaceCode.value)
emit('update:payrollPlaceName',payrollPlaceName.value)
notChange()
}
</script>
<style lang="scss" scoped>
.pover{
.search{
margin-top: 10px;
}
.tree{
margin-top: 5px;
border: 1px solid rgba(215, 215, 215, 1);
}
.btn{
margin-top: 10px;
display: flex;
justify-content: flex-end;
.btn0{
width: 70px;
height: 25px;
color: #FFFFFF;
text-align: center;
line-height: 25px;
cursor: pointer;
}
.btn1{
margin-right: 10px;
background-color: rgba(170, 170, 170, 1);
}
.btn2{
background-color: rgba(50, 107, 250, 1);
padding: 0;
}
}
}
</style>

View File

@@ -0,0 +1,251 @@
<template>
<a-upload-dragger :data="{ ...params }" :multiple="false" :accept="accept" :action="uploadUrl" :maxCount="maxCount"
@change="handleUploadChange" v-model:file-list="fileList" style="width:50%;">
<p class="ant-upload-drag-icon">
<UploadOutlined />
</p>
<p class="ant-upload-text">将文件拖到此处或点击上传</p>
<template #itemRender="{ file }">
<div class="loadstate">
<div class="loadborder">
<div class="content">
<div class="timebox">
<div class="timetop">
<div class="tit">{{ file.name }}</div>
<div class="stateloading">{{
{
done: "上传成功",
uploading: "正在上传",
error: "上传失败",
removed: "正在上传",
}[file.status]
}}
</div>
</div>
<a-progress :percent="file.percent" />
</div>
<div class="curloading">
<!-- <div class="cur">100%</div> -->
<div class="cancel" style="margin-left: 20px; cursor: pointer" @click="removeUpload">
删除
</div>
</div>
</div>
</div>
</div>
</template>
</a-upload-dragger>
</template>
<script setup>
import { UploadOutlined } from '@ant-design/icons-vue'
import { defineProps, ref, defineExpose,watch,onMounted } from "vue";
import { message } from "ant-design-vue";
const props = defineProps({
uploadUrl: {
type: String,
default: ''
},
params: {
type: Object,
default: {}
},
maxCount: {
type: Number,
default: 1
},
accept: {
type: String,
default: ''
},
size: {
type: Number,
default: 0
},
loadTrue: {
type:String,
default: ''
},
})
onMounted(() => {
if(props.loadTrue){
fileList.value = [{
name:props.loadTrue,
status: 'done',
percent: 100,
}]
}
});
const emit = defineEmits(['update:value', 'change'])
const fileList = ref([]);
const handleUploadChange = ({ file, fileList }) => {
console.log(file, fileList, 'file');
var FileExt = file.name.replace(/.+\./, "");
if (props.accept && props.accept.split(',').indexOf('.' + FileExt.toLowerCase()) === -1) {
fileList.value = [];
return message.error("请上传正确的文件格式");
}
if(props.size && file.size > props.size){
removeUpload()
message.destroy();
message.error("文件大小超过5MB!");
return
}
emit('update:value', fileList)
emit('change', fileList)
}
const removeUpload = () => {
fileList.value = [];
emit('removeList', false)
}
defineExpose({
fileList,
removeUpload
})
</script>
<style scoped lang="scss">
.loadstate {
width: 500px;
margin-bottom: 26px;
.loadborder {
width: 396px;
height: 70px;
border-radius: 4px;
border: 1px dashed #eaeaea;
margin-top: 10px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
.content {
display: flex;
margin-left: 20px;
position: relative;
.defeat {
width: 262px;
padding: 10px 20px;
position: absolute;
left: 46px;
top: 42px;
font-size: 14px;
font-weight: 500;
height: 32px;
border-radius: 2px;
border: 1px solid #387df7;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
.detext {
font-size: 14px;
font-weight: 400;
color: #387df7;
}
}
.img {
width: 30px;
height: 34px;
// background-image: url(@/assets/images/basicinfo/exl.png);
}
.timebox {
margin-left: 15px;
margin-top: -5px;
.timetop {
display: flex;
width: 262px;
justify-content: space-between;
.tit {
font-size: 14px;
font-weight: 400;
color: #333333;
width: 200px;
height: 21px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.stateloading {
font-size: 14px;
font-weight: 400;
color: #4ea6ff;
}
.statedefeat {
font-size: 14px;
font-weight: 400;
color: #ff7474;
}
.statesucce {
font-size: 14px;
font-weight: 400;
color: #35ae69;
}
}
.prog {
width: 262px;
height: 5px;
background: #eaf1fe;
border-radius: 4px;
.inprogloading {
width: 55%;
height: 5px;
border-radius: 4px;
background: #4ea6ff;
}
//下载失败条
.inprogdefeat {
width: 55%;
height: 5px;
border-radius: 4px;
background: #ff7474;
}
//下载成功条
.inprogsucce {
width: 100%;
height: 5px;
border-radius: 4px;
background: #57c887;
}
}
}
.curloading {
margin-left: 15px;
margin-top: 15px;
display: flex;
.cur {
font-size: 14px;
font-weight: 400;
color: #333333;
}
.cancel {
font-size: 14px;
font-weight: 400;
color: #387df7;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,451 @@
<template>
<div @click="openDrawer">
<slot></slot>
</div>
<a-drawer
:visible="visible"
class="drawerStyle impotergroupleader"
placement="right"
width="80%"
>
<div class="drawerMain">
<div class="header">
<div class="headerTitle">{{ title }}</div>
<img
style="width: 29px; height: 29px; cursor: pointer"
src="../../assets/images/basicinfo/close.png"
@click="closeDrawer"
/>
</div>
<div class="content">
<div class="title">
<div class="line"></div>
<div class="text">培训发生组织基本信息</div>
</div>
<div class="desc">
<a-descriptions :column="2" bordered>
<a-descriptions-item :labelStyle="{ width: '165px' }" label="培训发生组织编号">{{formData?.affiliationCode||'-'}}</a-descriptions-item>
<a-descriptions-item :labelStyle="{ width: '165px' }" label="培训发生组名称">{{formData?.affiliationName||'-'}}</a-descriptions-item>
<a-descriptions-item label="是否为根节点">{{formData?.isParent==1?'否':'是'}}</a-descriptions-item>
<a-descriptions-item label="组织担当">{{formData?.act||'-'}}</a-descriptions-item>
<a-descriptions-item v-if="formData?.isParent==1" label="一级审批人">{{formData?.one||'-'}}</a-descriptions-item>
<a-descriptions-item v-if="formData?.isParent==1" label="二级审批人">{{formData?.two||'-'}}</a-descriptions-item>
<a-descriptions-item v-if="formData?.isParent==1" label="三级审批人">{{formData?.three||'-'}}</a-descriptions-item>
<a-descriptions-item label="状态">
{{{1:'待提交', 2:'审核中',3: '已完成',4: '审核失败',5:'待审核'}[formData?.status]}}
</a-descriptions-item>
</a-descriptions>
</div>
<div class="tabs">
<a-tabs @change="change" v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="管理组织列表">
<div style="margin-bottom: 30px">
<a-table :columns="columns" :data-source="formData?.tableData" :pagination="false"/>
</div>
<div style="margin-bottom: 100px">
<!-- <a-descriptions :column="2" bordered>
<a-descriptions-item label="培训发生组织编号">{{formData?.affiliationCode||'-'}}</a-descriptions-item>
<a-descriptions-item label="培训发生组名称">{{formData?.affiliationName||'-'}}</a-descriptions-item>
<a-descriptions-item label="是否为根节点">{{formData?.isParent==1?'否':'是'}}</a-descriptions-item>
<a-descriptions-item label="组织担当">{{formData?.act||'-'}}</a-descriptions-item>
<a-descriptions-item label="一级审批人">{{formData?.one||'-'}}</a-descriptions-item>
<a-descriptions-item label="二级审批人">{{formData?.two||'-'}}</a-descriptions-item>
<a-descriptions-item label="三级审批人">{{formData?.three||'-'}}</a-descriptions-item>
<a-descriptions-item label="状态">
{{['-','待审核', '审核中', '已完成', '审核失败'][formData?.type]}}
</a-descriptions-item>
</a-descriptions> -->
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="审批记录" force-render>
<div style="margin-bottom: 20px">
<a-table :columns="columnsTwo" :loading="formData?.loadingTwo" :data-source="formData?.tableDataTwo" :pagination="pagination">
<template #action="{ record }">
<div class="action">
<div style="color: #1890ff;cursor: pointer;" class="btn" @click="lookList(record)">查看</div>
</div>
</template>
</a-table>
</div>
<div style="margin-bottom: 100px">
<a-table v-if="threeList" :columns="columnsThree" :loading="formData?.loadingThree" :data-source="formData?.tableDataThree" :pagination="false"/>
</div>
</a-tab-pane>
</a-tabs>
</div>
</div>
<div class="btnn" v-if="formData?.status==1||formData?.status==4">
<!-- <button class="btn2" @click="closeDrawer">取消</button>
<button class="btn2" @click="closeDrawer">确定</button> -->
<button class="btn2" @click="confirm">撤销编辑内容</button>
</div>
</div>
</a-drawer>
</template>
<script setup lang="jsx">
import {defineEmits, defineProps, ref,watch,reactive,computed} from "vue";
import dialog from '@/utils/dialog'
import * as api from '@/api/Lecturer'
import { message } from 'ant-design-vue';
const props = defineProps({
id: {
type: String,
defalut: ""
},
title: {
type: String,
default: ""
}
});
const emit = defineEmits({});
const columns = [
{
title: '组织的名称',
dataIndex: 'orgName',
key: 'orgName',
align: 'center',
},
{
title: '类型',
dataIndex: 'updateStatus',
key: 'updateStatus',
align: 'center',
customRender: ({text})=>{
switch (text) {
case 0:
return <span>未更改</span>;
case 1:
return <span>新增</span>;
case 2:
return <span>移除</span>;
case 3:
return <span>编辑</span>;
default:
return <span>-</span>;
}
},
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
align: 'center',
customRender: ({text})=>{
switch (text) {
case 1:
return <span>待提交</span>;
case 2:
return <span>审核中</span>;
case 3:
return <span>已完成</span>;
case 4:
return <span>审核失败</span>;
case 5:
return <span>待审核</span>;
default:
return <span>-</span>;
}
},
},
];
const formData = ref({})
const columnsThree = [
{
title: '层级审批人',
dataIndex: 'employeeNames',
key: 'employeeNames',
align: 'center',
},
{
title: '操作',
dataIndex: 'approvalStatus',
key: 'approvalStatus',
align: 'center',
customRender: ({text})=>{
switch (text) {
case -1:
return <span>未处理</span>;
case 3:
return <span>通过</span>;
case 4:
return <span>拒绝</span>;
default:
return <span>-</span>;
}
}
},
{
title: '审批人',
dataIndex: 'employeeName',
key: 'employeeName',
align: 'center',
},
{
title: '审批时间',
dataIndex: 'approvalTime',
key: 'approvalTime',
align: 'center',
},
{
title: '审批建议',
dataIndex: 'approvalMsg',
key: 'approvalMsg',
align: 'center',
ellipsis: true,
width: 200,
customRender: ({text})=>{
return <span>{text||'-'}</span>
}
},
]
const columnsTwo = ref([
{
title: '审批提交时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
},
{
title: '审批状态',
dataIndex: 'status',
key: 'status',
align: 'center',
customRender: ({text})=>{
switch (text) {
case 0:
return <span>待提交</span>;
case 1:
return <span>待审核</span>;
case 2:
return <span>审核中</span>;
case 3:
return <span>审核通过</span>;
case 4:
return <span>拒绝</span>;
case 5:
return <span>撤销中</span>;
case 6:
return <span>已撤销</span>;
default:
return <span>-</span>;
}
}
},
{
title: '审批人',
dataIndex: 'employeeName',
key: 'employeeName',
align: 'center',
},
{
title: '操作',
dataIndex: 'address',
key:'age',
align: 'center',
slots: { customRender: "action" },
}
])
const approvalData = ref(null)
const threeList = ref(false)
const lookList = (record) => {
console.log(record,'resssssss')
if(!threeList.value||record.approvalId!=approvalData.value){
threeList.value = true
approvalData.value = record.approvalId
formData.value.loadingThree = true
api.getApprovalResultByApprovalIdList(record.approvalId).then(res=>{
if(res.data.code == 200){
formData.value.tableDataThree = res.data.data
}
formData.value.loadingThree = false
}).catch(err=>{
message.destroy()
formData.value.loadingThree = false
message.error(err.data.msg)
})
}else{
threeList.value = false
approvalData.value = null
}
}
const visible = ref(false);
watch(visible, (val)=>{
if(val){
threeList.value = false
activeKey.value = '1'
api.getAffiliationById(props.id).then(res=>{
if(res.data.code == 200){
formData.value = res.data.data
formData.value.act = filterList(formData.value.affiliationUserList,0)
formData.value.one = filterList(formData.value.affiliationUserList,1)
formData.value.two = filterList(formData.value.affiliationUserList,2)
formData.value.three = filterList(formData.value.affiliationUserList,3)
formData.value.tableData = res.data.data.affiliationOrgList
}
}).catch(err=>{
message.destroy()
message.error(err.data.msg)
})
// getTwoData()
}
})
const getTwoData = () => {
formData.value.loadingTwo = true
api.getByBusinessIdList(params).then(res=>{
if(res.data.code == 200){
formData.value.tableDataTwo = res.data.data.records
params.total = res.data.data.total
}
formData.value.loadingTwo = false
}).catch(err=>{
message.destroy()
message.error(err.data.msg)
formData.value.loadingTwo = false
})
}
const params = reactive({
pageNo: 1,
pageSize: 10,
total: 0,
businessType: 2,
businessId: props.id
})
const pagination = computed(() => ({
total: params.total,
showSizeChanger: true,
showQuickJumper:true,
current: params.pageNo,
pageSize: params.pageSize,
onChange: paginationChange,
}));
const paginationChange = (e,pageSize) => {
params.pageNo = e;
params.pageSize = pageSize
getTwoData()
};
const filterList = (val,index) => {
return val?.filter(item=>item.type==index).map(item=>item.userName).join(',')
}
const closeDrawer = () => {
visible.value = false;
};
const confirm = () => {
dialog({
content: "您确定撤销编辑吗?撤销后将恢复到'已完成”前状态,且无法恢复。请谨慎操作。",
ok: () => {
api.recovery(props.id).then(res=>{
if(res.data.code == 200){
message.success("撤销成功")
visible.value = false;
emit('searchList',true)
}
}).catch(err=>{
message.destroy()
message.error(err.data.msg)
})
}
})
}
const activeKey = ref("1");
const change = (val)=>{
// console.log(val,'val')
if(val==2){
getTwoData()
}
}
function openDrawer() {
visible.value = true;
}
</script>
<style scoped lang="scss">
.impotergroupleader > .ant-drawer-content-wrapper {
min-width: 800px !important;
width: 800px !important;
}
.impotergroupleader {
.drawerMain {
min-width: 600px;
margin:0;
padding: 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;
flex-shrink: 0;
.headerTitle {
font-size: 18px;
font-weight: 600;
color: #333333;
line-height: 25px;
// margin-left: 24px;
margin: 0;
}
}
.content{
padding-right: 20px;
.title{
display: flex;
align-items: center;
margin-bottom: 20px;
.line{
width: 5px;
height: 15px;
background: #4ea6ff;
margin-right: 10px;
}
.text{
}
}
.desc{
margin-bottom: 30px;
}
}
.btnn {
height: 72px;
width: 100%;
position: absolute;
background-color: #fff;
bottom: 0;
left: 0;
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: 120px;
height: 40px;
background: #4ea6ff;
border-radius: 8px;
border: 0;
margin-left: 15px;
color: #fff;
}
}
}
}
</style>