Learn.Archives.Web/src/views/teacher/index.vue

789 lines
20 KiB
Vue

<template>
<div class="app-container" style="padding: 5px">
<div class="search-container" style="padding-top: 5px">
<!-- 搜索项目 -->
<el-form ref="searchForm" :inline="true" :model="search">
<el-form-item>
<el-input v-model="search.searchStr" placeholder="姓名/账号/学号" />
</el-form-item>
<el-form-item>
<el-input v-model="search.phone" placeholder="手机号" />
</el-form-item>
<!-- <el-form-item style="width: 100px">
<el-select
v-model="search.userType"
placeholder="用户类型"
clearable
filterable
@change="userTypeChange"
>
<el-option
v-for="item in userTypeList"
:key="item.value"
:label="item.text"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item> -->
<el-form-item v-show="search.userType === 1" style="width: 100px">
<el-select v-model="search.level" placeholder="学生层次" clearable filterable>
<el-option
v-for="item in userLevelList"
:key="item.value"
:label="item.text"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-select
v-model="search.schoolId"
placeholder="学校"
clearable
filterable
@change="schoolChange"
>
<el-option
v-for="item in schoolList"
:key="item.value"
:label="item.text"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-show="search.schoolId" style="width: 145px">
<el-select
v-model="search.grade"
placeholder="年级"
clearable
filterable
@change="gradeChange"
>
<el-option
v-for="item in gradeList"
:key="item.value"
:label="item.text"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-show="search.schoolId" style="width: 100px">
<el-select v-model="search.classId" placeholder="班级" clearable filterable>
<el-option
v-for="item in classList"
:key="item.value"
:label="item.text"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-show="search.schoolId" style="width: 100px">
<el-select v-model="search.subjectId" placeholder="科目" clearable filterable>
<el-option
v-for="item in subjectList"
:key="item.value"
:label="item.text"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleReloadPaged"
>查询</el-button
>
<el-button type="default" @click="searchReload">重置</el-button>
</el-form-item>
<el-form-item v-show="selectUser">
<el-button type="success" icon="el-icon-check" @click="selectUserCallBack()"
>选择用户</el-button
>
</el-form-item>
</el-form>
</div>
<div v-show="!selectUser" class="toolbar-container">
<!-- 按钮组 -->
<el-button type="success" @click="importData">导入用户</el-button>
<el-button type="default" @click="downLoadImportUsersTemplate"
>下载导入用户模板</el-button
>
<!-- <el-button title="根据当前筛选条件导出" type="primary" @click="exportUser"
>导出用户</el-button
> -->
</div>
<div v-show="!selectUser" class="toolbar-container">
<!-- 按钮组 -->
<el-button type="success" plain @click="AddDialog">新增</el-button>
</div>
<el-table
ref="selectUserTable"
:data="table.data"
style="width: 100%"
:max-height="maxTableHeight"
@row-dblclick="setCurrent"
@row-click="selectUserClick"
@selection-change="handleSelectionChange"
>
<!-- <el-table-column type="selection" width="40" /> -->
<el-table-column label="操作" width="100">
<template #default="scope">
<el-button text type="primary" plain @click="EditDialog(scope.row)"
>修改</el-button
>
</template>
</el-table-column>
<el-table-column prop="id" label="用户Id" width="100" />
<el-table-column label="用户信息" width="200">
<template #default="scope">
<el-tag :type="getUserTypeTag(scope.row.userType)" style="margin-right: 5px">{{
userTypeList.find((s) => s.value == scope.row.userType)?.text
}}</el-tag>
<span>{{ scope.row.realName }} </span>
</template>
</el-table-column>
<el-table-column prop="account" label="账号" width="120" />
<el-table-column prop="phone" label="手机号" width="120" />
<!-- <el-table-column prop="studentId" label="学号" width="120" />
<el-table-column prop="gKSubject" label="新高考学科" width="150" /> -->
<el-table-column prop="studentId" label="职务" width="120" />
<el-table-column label="任教信息" min-width="500">
<template #default="scope">
<div
v-for="(position, index) in scope.row.positions"
v-show="index < 3 || (index >= 3 && showAllPosition.includes(scope.row))"
:key="'Position' + index"
>
<div class="subjectTagEnableDiv">
<el-tag>{{ position.schoolName || "-" }}</el-tag>
<el-tag type="warning">{{
position.graduationYear
? position.gradeLevel + position.graduationYear + "届"
: "-"
}}</el-tag>
<el-tag type="primary" class="classTag">{{
position.className || "-"
}}</el-tag>
<el-tag type="info" class="subjectTag">{{
position.subjectName || "-"
}}</el-tag>
<el-tag type="danger">{{ position.name || "-" }}</el-tag>
</div>
</div>
<div
v-if="scope.row.positions != undefined && scope.row.positions.length > 3"
class="userTagRow"
@click="showPosition(scope.row)"
>
<el-icon
v-if="showAllPosition.includes(scope.row)"
title="折叠职位"
class="userTagRowItop"
><ArrowDownBold
/></el-icon>
<el-icon v-else title="展开更多职位"><ArrowDownBold /></el-icon>
</div>
</template>
</el-table-column>
</el-table>
<el-pagination
style="display: flex; justify-content: center"
:current-page="pagination.now"
:page-sizes="[10, 20, 40, 80, 100]"
:page-size="pagination.size"
layout="prev, pager, next,sizes, total"
:total="pagination.total"
@size-change="pageSizeChange"
@current-change="pageIndexChange"
/>
<div class="dialog-container">
<el-dialog
v-if="dialog.update.visible"
ref="UserEditFromDialog"
v-model="dialog.update.visible"
:title="dialog.update.title"
:width="dialog.update.width"
:close-on-click-modal="dialog.close"
:close-on-press-escape="dialog.close"
append-to-body
>
<UserForm :id="editId" @handlePagedCallback="handleAddCallback" />
</el-dialog>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import type { FormInstance, UploadProps } from "element-plus";
import UserForm from "./edit.vue";
import {
getSchoolData,
getClassCombo,
getSubjectData,
getPageUserList,
UserDetail,
Position,
} from "@/api/userCenter";
import { getenum } from "@/api/enum";
import { hTableAPI } from "@/api/hTable";
import { text } from "stream/consumers";
import {
Check,
Delete,
Edit,
Message,
ArrowDownBold,
Search,
Star,
Phone,
} from "@element-plus/icons-vue";
import { ComboModel, gradeComboModel } from "@/components/hTable/hTable";
import { ImportTeacher } from "@/api/student";
const classAPI = new hTableAPI("usercenter/back/classes");
const schoolsAPI = new hTableAPI("usercenter/back/schools");
interface SearchParams {
searchStr: string;
phone: string;
userType: string | number;
level: string | number;
schoolId: string | number;
graduationYear: string | number;
grade: string;
classId: string | number;
subjectId: string | number;
positionId: string | number;
}
interface TableData {
data: UserDetail[];
selectRows: UserDetail[];
sort: string;
border: boolean;
}
interface PaginationData {
index: number;
size: number;
total: number;
now?: number;
}
interface DialogData {
close: boolean;
update: {
title: string;
visible: boolean;
width: string;
};
editLevel: {
userIds: number[];
title: string;
visible: boolean;
width: string;
};
editSubjectLevel: {
userIds: number[];
title: string;
visible: boolean;
width: string;
};
bindUser: {
title: string;
visible: boolean;
width: string;
height: string;
};
userBindInfo: {
title: string;
visible: boolean;
width: string;
height: string;
};
}
const props = defineProps({
selectUser: {
type: Boolean,
default: false,
},
selectCallBack: {
type: Function,
default: () => {},
},
maxTableHeight: {
type: Number,
default: 580,
},
searchData: {
type: Object as () => SearchParams,
default: undefined,
},
});
const baseUrl = import.meta.env.VITE_API_USERCENTER_URL;
const excelImportUsersUrl = `${baseUrl}/back/users/downloadimportusersexceltemplate`;
const excelImportMeetingUrl = `${baseUrl}/back/users/downloadimportmeetingexceltemplate`;
const excelImportOrdersUrl = `${baseUrl}/back/users/downloadimportordersexceltemplate`;
const searchForm = ref<FormInstance>();
const editId = ref(0);
const showAllPosition = ref<UserDetail[]>([]);
const selectUserTable = ref();
const search = reactive<SearchParams>({
searchStr: "",
phone: "",
userType: "",
level: "",
schoolId: "",
graduationYear: "",
grade: "",
classId: "",
subjectId: "",
positionId: "",
});
const userTypeList = ref<ComboModel[]>([
{ value: 1, text: "学生" },
{ value: 2, text: "教师" },
{ value: 3, text: "管理员" },
]);
const userLevelList = ref<ComboModel[]>([]);
const schoolList = ref<ComboModel[]>([]);
const gradeList = ref<ComboModel[]>(gradeComboModel());
const classList = ref<ComboModel[]>([]);
const subjectList = ref<ComboModel[]>([]);
const positionList = ref<any[]>([]);
const table = reactive<TableData>({
data: [],
selectRows: [],
sort: "",
border: true,
});
const pagination = reactive<PaginationData>({
index: 1,
size: 10,
total: 0,
});
const dialog = reactive<DialogData>({
close: false,
update: {
title: "",
visible: false,
width: "1000px",
},
editLevel: {
userIds: [],
title: "",
visible: false,
width: "400px",
},
editSubjectLevel: {
userIds: [],
title: "",
visible: false,
width: "450px",
},
bindUser: {
title: "分配权限码",
visible: false,
width: "1150px",
height: "",
},
userBindInfo: {
title: "用户权限码",
visible: false,
width: "1150px",
height: "",
},
});
const checkUserBindInfo = () => {
if (table.selectRows.length != 1) {
ElMessage.warning("请选择一个用户");
return;
}
dialog.userBindInfo.visible = true;
};
const showPosition = (row: UserDetail) => {
if (showAllPosition.value.includes(row)) {
showAllPosition.value.splice(showAllPosition.value.indexOf(row), 1);
} else {
showAllPosition.value.push(row);
}
};
const codeBindUser = () => {
if (table.selectRows.length == 0) {
ElMessage.warning("请选择需要分配权限的用户");
return;
}
dialog.bindUser.visible = true;
};
const initSearchData = () => {
if (props.searchData !== undefined) {
for (const key in props.searchData) {
search[key] = props.searchData[key];
}
}
};
const selectUserClick = (row: UserDetail) => {
if (props.selectUser) {
selectUserTable.value.toggleRowSelection(row);
}
};
const setCurrent = (row: UserDetail) => {
selectUserTable.value.toggleRowSelection(row);
};
const selectUserCallBack = () => {
const u = table.selectRows;
props.selectCallBack(u);
selectUserTable.value.clearSelection();
};
const exportUser = async () => {
const data = {
SearchStr: search.searchStr,
UserType: search.userType || 0,
Level: search.level || 0,
SchoolId: search.schoolId || 0,
GraduationYear: search.graduationYear || 0,
Grade: search.grade,
ClassId: search.classId || 0,
SubjectId: search.subjectId || 0,
PositionId: search.positionId || 0,
PageIndex: pagination.index,
PageSize: pagination.size,
};
// const res = await exportUserApi(data);
// if (res.type === "application/json") {
// const json = await readerBlob(res);
// if (json !== undefined && json.code !== 200) {
// ElMessage.error(json.Message);
// }
// } else if (res !== undefined && res.size !== 0) {
// ElMessage.success("导出成功,下载中🕖请稍等");
// const url = window.URL.createObjectURL(res);
// const link = document.createElement("a");
// link.href = url;
// link.setAttribute("download", "导出用户信息.xlsx");
// document.body.appendChild(link);
// link.click();
// document.body.removeChild(link);
// } else {
// ElMessage.warning("导出失败,无有效数据");
// }
};
const fetchInitData = async () => {
schoolList.value = (await getSchoolData()).data;
subjectList.value = (await getenum("SubjectEnum")).data;
userLevelList.value = (await getenum("StudentLevelEnum")).data;
userTypeList.value = (await getenum("UserTypeEnum")).data;
};
const userTypeChange = () => {
search.level = "";
};
const schoolChange = () => {
search.graduationYear = "";
search.grade = "";
search.classId = "";
search.subjectId = "";
getClass();
};
const gradeChange = () => {
search.classId = "";
search.subjectId = "";
getClass();
};
const getClass = () => {
if (
search.schoolId == 0 ||
search.schoolId == undefined ||
search.grade == "" ||
search.grade == undefined
) {
classList.value = [];
return;
}
const data = {
schoolId: search.schoolId || 0,
graduationYear: search.graduationYear || 0,
grade: search.grade,
};
getClassCombo(data).then((res) => {
if (res.code === 200) {
classList.value = res.data;
}
});
};
const fetchPagedData = (searchUnUse = false) => {
const data = {
SearchStr: search.searchStr,
Phone: search.phone,
UserType: 2,
Level: search.level || 0,
SchoolId: search.schoolId || 0,
GraduationYear: search.graduationYear || 0,
Grade: search.grade,
ClassId: search.classId || 0,
SubjectId: search.subjectId || 0,
PositionId: search.positionId || 0,
PageIndex: pagination.index,
PageSize: pagination.size,
UnUsed: searchUnUse,
};
getPageUserList(data).then((res) => {
if (res.code === 200) {
pagination.total = res.data.total;
res.data.data.forEach((item) => {
if (item.positions) {
item.positions = PositionsSort(item.positions);
}
});
table.data = res.data.data;
}
});
};
const handleSelectionChange = (selection: UserDetail[]) => {
table.selectRows = selection;
};
const getUserTypeTag = (type: number) => {
return type === 1 ? "info" : "warning";
};
const getUserLevelTag = (level: number) => {
return level === 0
? "info"
: level === 1
? "success"
: level === 2
? "warning"
: "error";
};
const getUserLevelText = (level: number) => {
const r = userLevelList.value.filter((w) => w.value === level);
if (r.length > 0) {
return r[0].text;
}
return "";
};
const PositionsSort = (arr: Position[]) => {
arr.sort((a, b) => {
if (a.enable === b.enable) {
return 0;
} else if (a.enable) {
return -1;
} else {
return 1;
}
});
return arr.filter((s) => s.enable);
};
function searchReload() {
for (const key in search) {
search[key] = "";
}
}
const handleReloadPaged = (event?: any, searchUnUse?: boolean) => {
pagination.index = 1;
table.selectRows = [];
fetchPagedData(searchUnUse);
};
function pageSizeChange(o) {
pagination.size = o;
fetchPagedData();
}
function pageIndexChange(o) {
pagination.index = o;
fetchPagedData();
}
const AddDialog = () => {
editId.value = 0;
dialog.update.title = "添加教师";
dialog.update.visible = true;
};
const EditDialog = (row?) => {
if (row == null && table.selectRows.length !== 1) {
ElMessage.warning("请选择要修改用户");
return;
}
dialog.update.title = "修改用户";
editId.value = row != null ? row.id : table.selectRows[0].id;
dialog.update.visible = true;
};
const handleAddCallback = () => {
dialog.update.visible = false;
handleReloadPaged();
};
const handleEditLevel = () => {
if (table.selectRows.length === 0) {
ElMessage.warning("请选择要修改用户");
return;
}
dialog.editLevel.title = "修改学生层次";
dialog.editLevel.userIds = table.selectRows.map((w) => w.id);
dialog.editLevel.visible = true;
};
const handleEditLevelCallback = () => {
dialog.editLevel.visible = false;
handleReloadPaged();
};
const handleEditSubjectLevel = () => {
if (table.selectRows.length === 0) {
ElMessage.warning("请选择要修改用户");
return;
}
dialog.editSubjectLevel.title = "修改学生科目层次";
dialog.editSubjectLevel.userIds = table.selectRows.map((w) => w.id);
dialog.editSubjectLevel.visible = true;
};
const handleEditSubjectLevelCallback = () => {
dialog.editSubjectLevel.visible = false;
handleReloadPaged();
};
const importData = () => {
console.log("批量导入");
let fileE = document.createElement("input");
fileE.type = "file";
var formData = new window.FormData();
fileE.onchange = async function () {
formData.append("file", fileE.files[0]);
let res = await ImportTeacher(fileE.files[0]);
if (res.code != undefined) {
if (res.code !== 200) return ElMessage.error(res.message);
else return ElMessage.success("所有数据录入成功");
} else if (res.type === "application/json") {
let json = JSON.parse(await res.text());
let code = json.code || json.Code;
if (json !== undefined && code !== 200) {
return ElMessage.error(json.message);
} else {
return ElMessage.success("操所有数据录入成功作成功");
}
} else if (res === undefined || res.size === 0)
return ElMessage.success("所有数据录入成功");
const url = res && window.URL.createObjectURL(res);
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", "未成功导入的老师信息数据" + ".xlsx");
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
ElMessage({
showClose: true,
type: "warning",
message: "导入失败,已导出错误数据.",
duration: 0,
});
};
try {
fileE.click();
} catch (error) {}
};
const downLoadImportUsersTemplate = () => {
const baseUrl = import.meta.env.VITE_API_BASEURL;
const excelImportUsersUrl = `${baseUrl}/Student/DwImportTeacherTemplate`;
window.open(excelImportUsersUrl, "_blank");
};
onMounted(async () => {
await initSearchData();
await fetchInitData();
fetchPagedData();
});
</script>
<style lang="scss" scoped>
.userTagRowItop {
transform: rotate(180deg) !important;
}
.userTagRow i {
padding-top: 3px;
transform: rotate(0deg);
font-size: 1.3rem;
cursor: pointer;
}
.userTagRow {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
}
.subjectTagEnableDiv {
display: flex;
padding: 3px;
gap: 5px;
}
.classTag {
color: #a3bf08 !important;
background-color: #f4fbd1 !important;
border-color: #f4fbd1 !important;
}
.subjectTag {
color: #eb0de4 !important;
background-color: #fbd9ff !important;
border-color: #fbd9ff !important;
}
.subjectlevel_ul {
margin: 0;
padding: 0;
list-style: none;
}
.toolbar-container {
margin-bottom: 5px;
}
</style>